11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Keith Muller of the University of California, San Diego and Lance 71556Srgrimes * Visser of Convex Computer Corporation. 81556Srgrimes * 91556Srgrimes * Redistribution and use in source and binary forms, with or without 101556Srgrimes * modification, are permitted provided that the following conditions 111556Srgrimes * are met: 121556Srgrimes * 1. Redistributions of source code must retain the above copyright 131556Srgrimes * notice, this list of conditions and the following disclaimer. 141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 151556Srgrimes * notice, this list of conditions and the following disclaimer in the 161556Srgrimes * documentation and/or other materials provided with the distribution. 171556Srgrimes * 4. Neither the name of the University nor the names of its contributors 181556Srgrimes * may be used to endorse or promote products derived from this software 191556Srgrimes * without specific prior written permission. 201556Srgrimes * 211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311556Srgrimes * SUCH DAMAGE. 321556Srgrimes */ 331556Srgrimes 34114433Sobrien#if 0 351556Srgrimes#ifndef lint 3620420Sstevestatic char const copyright[] = 371556Srgrimes"@(#) Copyright (c) 1991, 1993, 1994\n\ 381556Srgrimes The Regents of the University of California. All rights reserved.\n"; 391556Srgrimes#endif /* not lint */ 401556Srgrimes 411556Srgrimes#ifndef lint 4236007Scharnierstatic char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; 43114433Sobrien#endif /* not lint */ 4435773Scharnier#endif 4599109Sobrien#include <sys/cdefs.h> 4699109Sobrien__FBSDID("$FreeBSD$"); 471556Srgrimes 481556Srgrimes#include <sys/param.h> 491556Srgrimes#include <sys/stat.h> 5050460Sgreen#include <sys/conf.h> 5160859Sobrien#include <sys/disklabel.h> 5250460Sgreen#include <sys/filio.h> 5391807Smarkm#include <sys/time.h> 541556Srgrimes 55266488Sthomas#include <assert.h> 561556Srgrimes#include <ctype.h> 571556Srgrimes#include <err.h> 581556Srgrimes#include <errno.h> 591556Srgrimes#include <fcntl.h> 60111629Smarkm#include <inttypes.h> 6119720Sphk#include <locale.h> 621556Srgrimes#include <stdio.h> 631556Srgrimes#include <stdlib.h> 641556Srgrimes#include <string.h> 651556Srgrimes#include <unistd.h> 661556Srgrimes 671556Srgrimes#include "dd.h" 681556Srgrimes#include "extern.h" 691556Srgrimes 7090108Simpstatic void dd_close(void); 7190108Simpstatic void dd_in(void); 7290108Simpstatic void getfdtype(IO *); 7390108Simpstatic void setup(void); 741556Srgrimes 751556SrgrimesIO in, out; /* input/output state */ 761556SrgrimesSTAT st; /* statistics */ 7790108Simpvoid (*cfunc)(void); /* conversion function */ 78111629Smarkmuintmax_t cpy_cnt; /* # of blocks to copy */ 7991079Smarkmstatic off_t pending = 0; /* pending seek if sparse */ 80111629Smarkmu_int ddflags = 0; /* conversion options */ 8151208Sgreensize_t cbsz; /* conversion block size */ 82111629Smarkmuintmax_t files_cnt = 1; /* # of files to copy */ 8351249Sgreenconst u_char *ctab; /* conversion table */ 84133762Srwatsonchar fill_char; /* Character to fill with if defined */ 85250469Seadlervolatile sig_atomic_t need_summary; 861556Srgrimes 871556Srgrimesint 8890108Simpmain(int argc __unused, char *argv[]) 891556Srgrimes{ 9019720Sphk (void)setlocale(LC_CTYPE, ""); 911556Srgrimes jcl(argv); 921556Srgrimes setup(); 931556Srgrimes 94250469Seadler (void)signal(SIGINFO, siginfo_handler); 951556Srgrimes (void)signal(SIGINT, terminate); 961556Srgrimes 971556Srgrimes atexit(summary); 981556Srgrimes 991556Srgrimes while (files_cnt--) 1001556Srgrimes dd_in(); 1011556Srgrimes 1021556Srgrimes dd_close(); 103249063Sbrooks /* 104249063Sbrooks * Some devices such as cfi(4) may perform significant amounts 105249063Sbrooks * of work when a write descriptor is closed. Close the out 106249063Sbrooks * descriptor explicitly so that the summary handler (called 107249063Sbrooks * from an atexit() hook) includes this work. 108249063Sbrooks */ 109249063Sbrooks close(out.fd); 1101556Srgrimes exit(0); 1111556Srgrimes} 1121556Srgrimes 113126667Sphkstatic int 114126667Sphkparity(u_char c) 115126667Sphk{ 116126667Sphk int i; 117126667Sphk 118126667Sphk i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^ 119126667Sphk (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7); 120126667Sphk return (i & 1); 121126667Sphk} 122126667Sphk 1231556Srgrimesstatic void 12490108Simpsetup(void) 1251556Srgrimes{ 1261556Srgrimes u_int cnt; 12719720Sphk struct timeval tv; 1281556Srgrimes 1291556Srgrimes if (in.name == NULL) { 1301556Srgrimes in.name = "stdin"; 1311556Srgrimes in.fd = STDIN_FILENO; 1321556Srgrimes } else { 13348051Sgreen in.fd = open(in.name, O_RDONLY, 0); 13448026Sgreen if (in.fd == -1) 1351556Srgrimes err(1, "%s", in.name); 1361556Srgrimes } 1371556Srgrimes 1381556Srgrimes getfdtype(&in); 1391556Srgrimes 1401556Srgrimes if (files_cnt > 1 && !(in.flags & ISTAPE)) 1411556Srgrimes errx(1, "files is not supported for non-tape devices"); 1421556Srgrimes 1431556Srgrimes if (out.name == NULL) { 1441556Srgrimes /* No way to check for read access here. */ 1451556Srgrimes out.fd = STDOUT_FILENO; 1461556Srgrimes out.name = "stdout"; 1471556Srgrimes } else { 1481556Srgrimes#define OFLAGS \ 1491556Srgrimes (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) 1501556Srgrimes out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); 1511556Srgrimes /* 1521556Srgrimes * May not have read access, so try again with write only. 1531556Srgrimes * Without read we may have a problem if output also does 1541556Srgrimes * not support seeks. 1551556Srgrimes */ 15648026Sgreen if (out.fd == -1) { 1571556Srgrimes out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); 1581556Srgrimes out.flags |= NOREAD; 1591556Srgrimes } 16048026Sgreen if (out.fd == -1) 1611556Srgrimes err(1, "%s", out.name); 1621556Srgrimes } 1631556Srgrimes 1641556Srgrimes getfdtype(&out); 1651556Srgrimes 1661556Srgrimes /* 1671556Srgrimes * Allocate space for the input and output buffers. If not doing 1681556Srgrimes * record oriented I/O, only need a single buffer. 1691556Srgrimes */ 17051249Sgreen if (!(ddflags & (C_BLOCK | C_UNBLOCK))) { 1711556Srgrimes if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) 17251208Sgreen err(1, "input buffer"); 1731556Srgrimes out.db = in.db; 17451208Sgreen } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL || 17551208Sgreen (out.db = malloc(out.dbsz + cbsz)) == NULL) 17651208Sgreen err(1, "output buffer"); 177266488Sthomas 178266488Sthomas /* dbp is the first free position in each buffer. */ 1791556Srgrimes in.dbp = in.db; 1801556Srgrimes out.dbp = out.db; 1811556Srgrimes 1821556Srgrimes /* Position the input/output streams. */ 1831556Srgrimes if (in.offset) 1841556Srgrimes pos_in(); 1851556Srgrimes if (out.offset) 1861556Srgrimes pos_out(); 1871556Srgrimes 1881556Srgrimes /* 18963688Sgreen * Truncate the output file. If it fails on a type of output file 19063688Sgreen * that it should _not_ fail on, error out. 1911556Srgrimes */ 19262311Sgreen if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) && 19362311Sgreen out.flags & ISTRUNC) 19462311Sgreen if (ftruncate(out.fd, out.offset * out.dbsz) == -1) 19562311Sgreen err(1, "truncating %s", out.name); 1961556Srgrimes 197126667Sphk if (ddflags & (C_LCASE | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) { 198126667Sphk if (ctab != NULL) { 199126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 200126667Sphk casetab[cnt] = ctab[cnt]; 20146073Simp } else { 202126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 203126667Sphk casetab[cnt] = cnt; 2045701Sache } 205126667Sphk if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) { 206126667Sphk /* 207126667Sphk * If the input is not EBCDIC, and we do parity 208126667Sphk * processing, strip input parity. 209126667Sphk */ 210126667Sphk for (cnt = 200; cnt <= 0377; ++cnt) 211126667Sphk casetab[cnt] = casetab[cnt & 0x7f]; 212126667Sphk } 213126667Sphk if (ddflags & C_LCASE) { 214126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 215126667Sphk casetab[cnt] = tolower(casetab[cnt]); 216126667Sphk } else if (ddflags & C_UCASE) { 217126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 218126667Sphk casetab[cnt] = toupper(casetab[cnt]); 219126667Sphk } 220126667Sphk if ((ddflags & C_PARITY)) { 221126667Sphk /* 222126667Sphk * This should strictly speaking be a no-op, but I 223126667Sphk * wonder what funny LANG settings could get us. 224126667Sphk */ 225126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 226126667Sphk casetab[cnt] = casetab[cnt] & 0x7f; 227126667Sphk } 228126667Sphk if ((ddflags & C_PARSET)) { 229126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 230126667Sphk casetab[cnt] = casetab[cnt] | 0x80; 231126667Sphk } 232126667Sphk if ((ddflags & C_PAREVEN)) { 233126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 234126667Sphk if (parity(casetab[cnt])) 235126667Sphk casetab[cnt] = casetab[cnt] | 0x80; 236126667Sphk } 237126667Sphk if ((ddflags & C_PARODD)) { 238126667Sphk for (cnt = 0; cnt <= 0377; ++cnt) 239126667Sphk if (!parity(casetab[cnt])) 240126667Sphk casetab[cnt] = casetab[cnt] | 0x80; 241126667Sphk } 242126667Sphk 24351208Sgreen ctab = casetab; 24446073Simp } 24551208Sgreen 246239991Sed (void)gettimeofday(&tv, NULL); 247239991Sed st.start = tv.tv_sec + tv.tv_usec * 1e-6; 2481556Srgrimes} 2491556Srgrimes 2501556Srgrimesstatic void 25190108Simpgetfdtype(IO *io) 2521556Srgrimes{ 2531556Srgrimes struct stat sb; 25450460Sgreen int type; 2551556Srgrimes 25651208Sgreen if (fstat(io->fd, &sb) == -1) 2571556Srgrimes err(1, "%s", io->name); 25862311Sgreen if (S_ISREG(sb.st_mode)) 25962311Sgreen io->flags |= ISTRUNC; 26050460Sgreen if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { 26150487Sgreen if (ioctl(io->fd, FIODTYPE, &type) == -1) { 26251913Sgreen err(1, "%s", io->name); 26350487Sgreen } else { 26450487Sgreen if (type & D_TAPE) 26550487Sgreen io->flags |= ISTAPE; 266109873Sphk else if (type & (D_DISK | D_MEM)) 26751212Sgreen io->flags |= ISSEEK; 26850487Sgreen if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0) 26950487Sgreen io->flags |= ISCHR; 27050487Sgreen } 27151335Sgreen return; 27251335Sgreen } 27351335Sgreen errno = 0; 27451335Sgreen if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) 27551335Sgreen io->flags |= ISPIPE; 27651335Sgreen else 27751249Sgreen io->flags |= ISSEEK; 2781556Srgrimes} 2791556Srgrimes 2801556Srgrimesstatic void 28190108Simpdd_in(void) 2821556Srgrimes{ 28348026Sgreen ssize_t n; 2841556Srgrimes 28528430Sjlemon for (;;) { 28651335Sgreen switch (cpy_cnt) { 28751335Sgreen case -1: /* count=0 was specified */ 2881556Srgrimes return; 28951335Sgreen case 0: 29051335Sgreen break; 29151335Sgreen default: 292111629Smarkm if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt) 29351335Sgreen return; 29451335Sgreen break; 29551335Sgreen } 2961556Srgrimes 2971556Srgrimes /* 29851208Sgreen * Zero the buffer first if sync; if doing block operations, 2991556Srgrimes * use spaces. 3001556Srgrimes */ 30148026Sgreen if (ddflags & C_SYNC) { 302133762Srwatson if (ddflags & C_FILL) 303133762Srwatson memset(in.dbp, fill_char, in.dbsz); 304133762Srwatson else if (ddflags & (C_BLOCK | C_UNBLOCK)) 3051556Srgrimes memset(in.dbp, ' ', in.dbsz); 3061556Srgrimes else 3071556Srgrimes memset(in.dbp, 0, in.dbsz); 30848026Sgreen } 3091556Srgrimes 3101556Srgrimes n = read(in.fd, in.dbp, in.dbsz); 3111556Srgrimes if (n == 0) { 3121556Srgrimes in.dbrcnt = 0; 3131556Srgrimes return; 3141556Srgrimes } 3151556Srgrimes 3161556Srgrimes /* Read error. */ 31748026Sgreen if (n == -1) { 3181556Srgrimes /* 3191556Srgrimes * If noerror not specified, die. POSIX requires that 3201556Srgrimes * the warning message be followed by an I/O display. 3211556Srgrimes */ 32228430Sjlemon if (!(ddflags & C_NOERROR)) 3231556Srgrimes err(1, "%s", in.name); 3241556Srgrimes warn("%s", in.name); 3251556Srgrimes summary(); 3261556Srgrimes 3271556Srgrimes /* 32851249Sgreen * If it's a seekable file descriptor, seek past the 3291556Srgrimes * error. If your OS doesn't do the right thing for 3301556Srgrimes * raw disks this section should be modified to re-read 3311556Srgrimes * in sector size chunks. 3321556Srgrimes */ 33351249Sgreen if (in.flags & ISSEEK && 3341556Srgrimes lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) 3351556Srgrimes warn("%s", in.name); 3361556Srgrimes 3371556Srgrimes /* If sync not specified, omit block and continue. */ 3381556Srgrimes if (!(ddflags & C_SYNC)) 3391556Srgrimes continue; 3401556Srgrimes 3411556Srgrimes /* Read errors count as full blocks. */ 3421556Srgrimes in.dbcnt += in.dbrcnt = in.dbsz; 3431556Srgrimes ++st.in_full; 3441556Srgrimes 3451556Srgrimes /* Handle full input blocks. */ 34662311Sgreen } else if ((size_t)n == in.dbsz) { 3471556Srgrimes in.dbcnt += in.dbrcnt = n; 3481556Srgrimes ++st.in_full; 3491556Srgrimes 3501556Srgrimes /* Handle partial input blocks. */ 3511556Srgrimes } else { 3521556Srgrimes /* If sync, use the entire block. */ 3531556Srgrimes if (ddflags & C_SYNC) 3541556Srgrimes in.dbcnt += in.dbrcnt = in.dbsz; 3551556Srgrimes else 3561556Srgrimes in.dbcnt += in.dbrcnt = n; 3571556Srgrimes ++st.in_part; 3581556Srgrimes } 3591556Srgrimes 3601556Srgrimes /* 3611556Srgrimes * POSIX states that if bs is set and no other conversions 3621556Srgrimes * than noerror, notrunc or sync are specified, the block 3631556Srgrimes * is output without buffering as it is read. 3641556Srgrimes */ 365249811Skib if ((ddflags & ~(C_NOERROR | C_NOTRUNC | C_SYNC)) == C_BS) { 3661556Srgrimes out.dbcnt = in.dbcnt; 3671556Srgrimes dd_out(1); 3681556Srgrimes in.dbcnt = 0; 3691556Srgrimes continue; 3701556Srgrimes } 3711556Srgrimes 3721556Srgrimes if (ddflags & C_SWAB) { 37332324Sjoerg if ((n = in.dbrcnt) & 1) { 3741556Srgrimes ++st.swab; 3751556Srgrimes --n; 3761556Srgrimes } 37762311Sgreen swab(in.dbp, in.dbp, (size_t)n); 3781556Srgrimes } 3791556Srgrimes 3801556Srgrimes in.dbp += in.dbrcnt; 3811556Srgrimes (*cfunc)(); 382250469Seadler if (need_summary) { 383250469Seadler summary(); 384250469Seadler } 3851556Srgrimes } 3861556Srgrimes} 3871556Srgrimes 3881556Srgrimes/* 38951249Sgreen * Clean up any remaining I/O and flush output. If necessary, the output file 3901556Srgrimes * is truncated. 3911556Srgrimes */ 3921556Srgrimesstatic void 39390108Simpdd_close(void) 3941556Srgrimes{ 3951556Srgrimes if (cfunc == def) 3961556Srgrimes def_close(); 3971556Srgrimes else if (cfunc == block) 3981556Srgrimes block_close(); 3991556Srgrimes else if (cfunc == unblock) 4001556Srgrimes unblock_close(); 40128430Sjlemon if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) { 402133762Srwatson if (ddflags & C_FILL) 403133762Srwatson memset(out.dbp, fill_char, out.dbsz - out.dbcnt); 404133762Srwatson else if (ddflags & (C_BLOCK | C_UNBLOCK)) 40528430Sjlemon memset(out.dbp, ' ', out.dbsz - out.dbcnt); 40628430Sjlemon else 40728430Sjlemon memset(out.dbp, 0, out.dbsz - out.dbcnt); 4081556Srgrimes out.dbcnt = out.dbsz; 4091556Srgrimes } 41030312Sjoerg if (out.dbcnt || pending) 4111556Srgrimes dd_out(1); 412298258Sthomas 413298258Sthomas /* 414298258Sthomas * If the file ends with a hole, ftruncate it to extend its size 415298258Sthomas * up to the end of the hole (without having to write any data). 416298258Sthomas */ 417298258Sthomas if (out.seek_offset > 0 && (out.flags & ISTRUNC)) { 418298258Sthomas if (ftruncate(out.fd, out.seek_offset) == -1) 419298258Sthomas err(1, "truncating %s", out.name); 420298258Sthomas } 4211556Srgrimes} 4221556Srgrimes 4231556Srgrimesvoid 42490108Simpdd_out(int force) 4251556Srgrimes{ 42651208Sgreen u_char *outp; 42751208Sgreen size_t cnt, i, n; 42851208Sgreen ssize_t nw; 4291556Srgrimes static int warned; 43051208Sgreen int sparse; 4311556Srgrimes 4321556Srgrimes /* 4331556Srgrimes * Write one or more blocks out. The common case is writing a full 4341556Srgrimes * output block in a single write; increment the full block stats. 4351556Srgrimes * Otherwise, we're into partial block writes. If a partial write, 4361556Srgrimes * and it's a character device, just warn. If a tape device, quit. 4371556Srgrimes * 4381556Srgrimes * The partial writes represent two cases. 1: Where the input block 4391556Srgrimes * was less than expected so the output block was less than expected. 4401556Srgrimes * 2: Where the input block was the right size but we were forced to 4411556Srgrimes * write the block in multiple chunks. The original versions of dd(1) 4421556Srgrimes * never wrote a block in more than a single write, so the latter case 4431556Srgrimes * never happened. 4441556Srgrimes * 4451556Srgrimes * One special case is if we're forced to do the write -- in that case 4461556Srgrimes * we play games with the buffer size, and it's usually a partial write. 4471556Srgrimes */ 4481556Srgrimes outp = out.db; 449266488Sthomas 450266488Sthomas /* 451266488Sthomas * If force, first try to write all pending data, else try to write 452266488Sthomas * just one block. Subsequently always write data one full block at 453266488Sthomas * a time at most. 454266488Sthomas */ 4551556Srgrimes for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { 456266488Sthomas cnt = n; 457266488Sthomas do { 45830312Sjoerg sparse = 0; 45930312Sjoerg if (ddflags & C_SPARSE) { 46030312Sjoerg sparse = 1; /* Is buffer sparse? */ 46130312Sjoerg for (i = 0; i < cnt; i++) 46230312Sjoerg if (outp[i] != 0) { 46330312Sjoerg sparse = 0; 46430312Sjoerg break; 46530312Sjoerg } 46630312Sjoerg } 46730312Sjoerg if (sparse && !force) { 46830312Sjoerg pending += cnt; 46930312Sjoerg nw = cnt; 47030312Sjoerg } else { 47130312Sjoerg if (pending != 0) { 472298258Sthomas /* 473298258Sthomas * Seek past hole. Note that we need to record the 474298258Sthomas * reached offset, because we might have no more data 475298258Sthomas * to write, in which case we'll need to call 476298258Sthomas * ftruncate to extend the file size. 477266488Sthomas */ 478298258Sthomas out.seek_offset = lseek(out.fd, pending, SEEK_CUR); 479298258Sthomas if (out.seek_offset == -1) 48048051Sgreen err(2, "%s: seek error creating sparse file", 48148051Sgreen out.name); 482298258Sthomas pending = 0; 48330312Sjoerg } 484298258Sthomas if (cnt) { 48530312Sjoerg nw = write(out.fd, outp, cnt); 486298258Sthomas out.seek_offset = 0; 487298258Sthomas } else { 48830312Sjoerg return; 489298258Sthomas } 49030312Sjoerg } 49130312Sjoerg 4921556Srgrimes if (nw <= 0) { 4931556Srgrimes if (nw == 0) 4941556Srgrimes errx(1, "%s: end of device", out.name); 4951556Srgrimes if (errno != EINTR) 4961556Srgrimes err(1, "%s", out.name); 4971556Srgrimes nw = 0; 4981556Srgrimes } 499266488Sthomas 5001556Srgrimes outp += nw; 5011556Srgrimes st.bytes += nw; 502266488Sthomas 503266488Sthomas if ((size_t)nw == n && n == out.dbsz) 504266488Sthomas ++st.out_full; 505266488Sthomas else 506266488Sthomas ++st.out_part; 507266488Sthomas 508266488Sthomas if ((size_t) nw != cnt) { 509266488Sthomas if (out.flags & ISTAPE) 510266488Sthomas errx(1, "%s: short write on tape device", 511266488Sthomas out.name); 512266488Sthomas if (out.flags & ISCHR && !warned) { 513266488Sthomas warned = 1; 514266488Sthomas warnx("%s: short write on character device", 515266488Sthomas out.name); 516266488Sthomas } 5171556Srgrimes } 518266488Sthomas 519266488Sthomas cnt -= nw; 520266488Sthomas } while (cnt != 0); 521266488Sthomas 5221556Srgrimes if ((out.dbcnt -= n) < out.dbsz) 5231556Srgrimes break; 5241556Srgrimes } 5251556Srgrimes 5261556Srgrimes /* Reassemble the output block. */ 5271556Srgrimes if (out.dbcnt) 52851208Sgreen (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); 5291556Srgrimes out.dbp = out.db + out.dbcnt; 5301556Srgrimes} 531