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 */
80266488Sthomasstatic off_t	last_sp = 0;	/* size of last added sparse block */
81111629Smarkmu_int	ddflags = 0;		/* conversion options */
8251208Sgreensize_t	cbsz;			/* conversion block size */
83111629Smarkmuintmax_t files_cnt = 1;	/* # of files to copy */
8451249Sgreenconst	u_char *ctab;		/* conversion table */
85133762Srwatsonchar	fill_char;		/* Character to fill with if defined */
86250469Seadlervolatile sig_atomic_t need_summary;
871556Srgrimes
881556Srgrimesint
8990108Simpmain(int argc __unused, char *argv[])
901556Srgrimes{
9119720Sphk	(void)setlocale(LC_CTYPE, "");
921556Srgrimes	jcl(argv);
931556Srgrimes	setup();
941556Srgrimes
95250469Seadler	(void)signal(SIGINFO, siginfo_handler);
961556Srgrimes	(void)signal(SIGINT, terminate);
971556Srgrimes
981556Srgrimes	atexit(summary);
991556Srgrimes
1001556Srgrimes	while (files_cnt--)
1011556Srgrimes		dd_in();
1021556Srgrimes
1031556Srgrimes	dd_close();
104249063Sbrooks	/*
105249063Sbrooks	 * Some devices such as cfi(4) may perform significant amounts
106249063Sbrooks	 * of work when a write descriptor is closed.  Close the out
107249063Sbrooks	 * descriptor explicitly so that the summary handler (called
108249063Sbrooks	 * from an atexit() hook) includes this work.
109249063Sbrooks	 */
110249063Sbrooks	close(out.fd);
1111556Srgrimes	exit(0);
1121556Srgrimes}
1131556Srgrimes
114126667Sphkstatic int
115126667Sphkparity(u_char c)
116126667Sphk{
117126667Sphk	int i;
118126667Sphk
119126667Sphk	i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^
120126667Sphk	    (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7);
121126667Sphk	return (i & 1);
122126667Sphk}
123126667Sphk
1241556Srgrimesstatic void
12590108Simpsetup(void)
1261556Srgrimes{
1271556Srgrimes	u_int cnt;
12819720Sphk	struct timeval tv;
1291556Srgrimes
1301556Srgrimes	if (in.name == NULL) {
1311556Srgrimes		in.name = "stdin";
1321556Srgrimes		in.fd = STDIN_FILENO;
1331556Srgrimes	} else {
13448051Sgreen		in.fd = open(in.name, O_RDONLY, 0);
13548026Sgreen		if (in.fd == -1)
1361556Srgrimes			err(1, "%s", in.name);
1371556Srgrimes	}
1381556Srgrimes
1391556Srgrimes	getfdtype(&in);
1401556Srgrimes
1411556Srgrimes	if (files_cnt > 1 && !(in.flags & ISTAPE))
1421556Srgrimes		errx(1, "files is not supported for non-tape devices");
1431556Srgrimes
1441556Srgrimes	if (out.name == NULL) {
1451556Srgrimes		/* No way to check for read access here. */
1461556Srgrimes		out.fd = STDOUT_FILENO;
1471556Srgrimes		out.name = "stdout";
1481556Srgrimes	} else {
1491556Srgrimes#define	OFLAGS \
1501556Srgrimes    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
1511556Srgrimes		out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
1521556Srgrimes		/*
1531556Srgrimes		 * May not have read access, so try again with write only.
1541556Srgrimes		 * Without read we may have a problem if output also does
1551556Srgrimes		 * not support seeks.
1561556Srgrimes		 */
15748026Sgreen		if (out.fd == -1) {
1581556Srgrimes			out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
1591556Srgrimes			out.flags |= NOREAD;
1601556Srgrimes		}
16148026Sgreen		if (out.fd == -1)
1621556Srgrimes			err(1, "%s", out.name);
1631556Srgrimes	}
1641556Srgrimes
1651556Srgrimes	getfdtype(&out);
1661556Srgrimes
1671556Srgrimes	/*
1681556Srgrimes	 * Allocate space for the input and output buffers.  If not doing
1691556Srgrimes	 * record oriented I/O, only need a single buffer.
1701556Srgrimes	 */
17151249Sgreen	if (!(ddflags & (C_BLOCK | C_UNBLOCK))) {
1721556Srgrimes		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
17351208Sgreen			err(1, "input buffer");
1741556Srgrimes		out.db = in.db;
17551208Sgreen	} else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
17651208Sgreen	    (out.db = malloc(out.dbsz + cbsz)) == NULL)
17751208Sgreen		err(1, "output buffer");
178266488Sthomas
179266488Sthomas	/* dbp is the first free position in each buffer. */
1801556Srgrimes	in.dbp = in.db;
1811556Srgrimes	out.dbp = out.db;
1821556Srgrimes
1831556Srgrimes	/* Position the input/output streams. */
1841556Srgrimes	if (in.offset)
1851556Srgrimes		pos_in();
1861556Srgrimes	if (out.offset)
1871556Srgrimes		pos_out();
1881556Srgrimes
1891556Srgrimes	/*
19063688Sgreen	 * Truncate the output file.  If it fails on a type of output file
19163688Sgreen	 * that it should _not_ fail on, error out.
1921556Srgrimes	 */
19362311Sgreen	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) &&
19462311Sgreen	    out.flags & ISTRUNC)
19562311Sgreen		if (ftruncate(out.fd, out.offset * out.dbsz) == -1)
19662311Sgreen			err(1, "truncating %s", out.name);
1971556Srgrimes
198126667Sphk	if (ddflags & (C_LCASE  | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) {
199126667Sphk		if (ctab != NULL) {
200126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
201126667Sphk				casetab[cnt] = ctab[cnt];
20246073Simp		} else {
203126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
204126667Sphk				casetab[cnt] = cnt;
2055701Sache		}
206126667Sphk		if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) {
207126667Sphk			/*
208126667Sphk			 * If the input is not EBCDIC, and we do parity
209126667Sphk			 * processing, strip input parity.
210126667Sphk			 */
211126667Sphk			for (cnt = 200; cnt <= 0377; ++cnt)
212126667Sphk				casetab[cnt] = casetab[cnt & 0x7f];
213126667Sphk		}
214126667Sphk		if (ddflags & C_LCASE) {
215126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
216126667Sphk				casetab[cnt] = tolower(casetab[cnt]);
217126667Sphk		} else if (ddflags & C_UCASE) {
218126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
219126667Sphk				casetab[cnt] = toupper(casetab[cnt]);
220126667Sphk		}
221126667Sphk		if ((ddflags & C_PARITY)) {
222126667Sphk			/*
223126667Sphk			 * This should strictly speaking be a no-op, but I
224126667Sphk			 * wonder what funny LANG settings could get us.
225126667Sphk			 */
226126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
227126667Sphk				casetab[cnt] = casetab[cnt] & 0x7f;
228126667Sphk		}
229126667Sphk		if ((ddflags & C_PARSET)) {
230126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
231126667Sphk				casetab[cnt] = casetab[cnt] | 0x80;
232126667Sphk		}
233126667Sphk		if ((ddflags & C_PAREVEN)) {
234126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
235126667Sphk				if (parity(casetab[cnt]))
236126667Sphk					casetab[cnt] = casetab[cnt] | 0x80;
237126667Sphk		}
238126667Sphk		if ((ddflags & C_PARODD)) {
239126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
240126667Sphk				if (!parity(casetab[cnt]))
241126667Sphk					casetab[cnt] = casetab[cnt] | 0x80;
242126667Sphk		}
243126667Sphk
24451208Sgreen		ctab = casetab;
24546073Simp	}
24651208Sgreen
247239991Sed	(void)gettimeofday(&tv, NULL);
248239991Sed	st.start = tv.tv_sec + tv.tv_usec * 1e-6;
2491556Srgrimes}
2501556Srgrimes
2511556Srgrimesstatic void
25290108Simpgetfdtype(IO *io)
2531556Srgrimes{
2541556Srgrimes	struct stat sb;
25550460Sgreen	int type;
2561556Srgrimes
25751208Sgreen	if (fstat(io->fd, &sb) == -1)
2581556Srgrimes		err(1, "%s", io->name);
25962311Sgreen	if (S_ISREG(sb.st_mode))
26062311Sgreen		io->flags |= ISTRUNC;
26150460Sgreen	if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
26250487Sgreen		if (ioctl(io->fd, FIODTYPE, &type) == -1) {
26351913Sgreen			err(1, "%s", io->name);
26450487Sgreen		} else {
26550487Sgreen			if (type & D_TAPE)
26650487Sgreen				io->flags |= ISTAPE;
267109873Sphk			else if (type & (D_DISK | D_MEM))
26851212Sgreen				io->flags |= ISSEEK;
26950487Sgreen			if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0)
27050487Sgreen				io->flags |= ISCHR;
27150487Sgreen		}
27251335Sgreen		return;
27351335Sgreen	}
27451335Sgreen	errno = 0;
27551335Sgreen	if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
27651335Sgreen		io->flags |= ISPIPE;
27751335Sgreen	else
27851249Sgreen		io->flags |= ISSEEK;
2791556Srgrimes}
2801556Srgrimes
2811556Srgrimesstatic void
28290108Simpdd_in(void)
2831556Srgrimes{
28448026Sgreen	ssize_t n;
2851556Srgrimes
28628430Sjlemon	for (;;) {
28751335Sgreen		switch (cpy_cnt) {
28851335Sgreen		case -1:			/* count=0 was specified */
2891556Srgrimes			return;
29051335Sgreen		case 0:
29151335Sgreen			break;
29251335Sgreen		default:
293111629Smarkm			if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt)
29451335Sgreen				return;
29551335Sgreen			break;
29651335Sgreen		}
2971556Srgrimes
2981556Srgrimes		/*
29951208Sgreen		 * Zero the buffer first if sync; if doing block operations,
3001556Srgrimes		 * use spaces.
3011556Srgrimes		 */
30248026Sgreen		if (ddflags & C_SYNC) {
303133762Srwatson			if (ddflags & C_FILL)
304133762Srwatson				memset(in.dbp, fill_char, in.dbsz);
305133762Srwatson			else if (ddflags & (C_BLOCK | C_UNBLOCK))
3061556Srgrimes				memset(in.dbp, ' ', in.dbsz);
3071556Srgrimes			else
3081556Srgrimes				memset(in.dbp, 0, in.dbsz);
30948026Sgreen		}
3101556Srgrimes
3111556Srgrimes		n = read(in.fd, in.dbp, in.dbsz);
3121556Srgrimes		if (n == 0) {
3131556Srgrimes			in.dbrcnt = 0;
3141556Srgrimes			return;
3151556Srgrimes		}
3161556Srgrimes
3171556Srgrimes		/* Read error. */
31848026Sgreen		if (n == -1) {
3191556Srgrimes			/*
3201556Srgrimes			 * If noerror not specified, die.  POSIX requires that
3211556Srgrimes			 * the warning message be followed by an I/O display.
3221556Srgrimes			 */
32328430Sjlemon			if (!(ddflags & C_NOERROR))
3241556Srgrimes				err(1, "%s", in.name);
3251556Srgrimes			warn("%s", in.name);
3261556Srgrimes			summary();
3271556Srgrimes
3281556Srgrimes			/*
32951249Sgreen			 * If it's a seekable file descriptor, seek past the
3301556Srgrimes			 * error.  If your OS doesn't do the right thing for
3311556Srgrimes			 * raw disks this section should be modified to re-read
3321556Srgrimes			 * in sector size chunks.
3331556Srgrimes			 */
33451249Sgreen			if (in.flags & ISSEEK &&
3351556Srgrimes			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
3361556Srgrimes				warn("%s", in.name);
3371556Srgrimes
3381556Srgrimes			/* If sync not specified, omit block and continue. */
3391556Srgrimes			if (!(ddflags & C_SYNC))
3401556Srgrimes				continue;
3411556Srgrimes
3421556Srgrimes			/* Read errors count as full blocks. */
3431556Srgrimes			in.dbcnt += in.dbrcnt = in.dbsz;
3441556Srgrimes			++st.in_full;
3451556Srgrimes
3461556Srgrimes		/* Handle full input blocks. */
34762311Sgreen		} else if ((size_t)n == in.dbsz) {
3481556Srgrimes			in.dbcnt += in.dbrcnt = n;
3491556Srgrimes			++st.in_full;
3501556Srgrimes
3511556Srgrimes		/* Handle partial input blocks. */
3521556Srgrimes		} else {
3531556Srgrimes			/* If sync, use the entire block. */
3541556Srgrimes			if (ddflags & C_SYNC)
3551556Srgrimes				in.dbcnt += in.dbrcnt = in.dbsz;
3561556Srgrimes			else
3571556Srgrimes				in.dbcnt += in.dbrcnt = n;
3581556Srgrimes			++st.in_part;
3591556Srgrimes		}
3601556Srgrimes
3611556Srgrimes		/*
3621556Srgrimes		 * POSIX states that if bs is set and no other conversions
3631556Srgrimes		 * than noerror, notrunc or sync are specified, the block
3641556Srgrimes		 * is output without buffering as it is read.
3651556Srgrimes		 */
366249811Skib		if ((ddflags & ~(C_NOERROR | C_NOTRUNC | C_SYNC)) == C_BS) {
3671556Srgrimes			out.dbcnt = in.dbcnt;
3681556Srgrimes			dd_out(1);
3691556Srgrimes			in.dbcnt = 0;
3701556Srgrimes			continue;
3711556Srgrimes		}
3721556Srgrimes
3731556Srgrimes		if (ddflags & C_SWAB) {
37432324Sjoerg			if ((n = in.dbrcnt) & 1) {
3751556Srgrimes				++st.swab;
3761556Srgrimes				--n;
3771556Srgrimes			}
37862311Sgreen			swab(in.dbp, in.dbp, (size_t)n);
3791556Srgrimes		}
3801556Srgrimes
3811556Srgrimes		in.dbp += in.dbrcnt;
3821556Srgrimes		(*cfunc)();
383250469Seadler		if (need_summary) {
384250469Seadler			summary();
385250469Seadler		}
3861556Srgrimes	}
3871556Srgrimes}
3881556Srgrimes
3891556Srgrimes/*
39051249Sgreen * Clean up any remaining I/O and flush output.  If necessary, the output file
3911556Srgrimes * is truncated.
3921556Srgrimes */
3931556Srgrimesstatic void
39490108Simpdd_close(void)
3951556Srgrimes{
3961556Srgrimes	if (cfunc == def)
3971556Srgrimes		def_close();
3981556Srgrimes	else if (cfunc == block)
3991556Srgrimes		block_close();
4001556Srgrimes	else if (cfunc == unblock)
4011556Srgrimes		unblock_close();
40228430Sjlemon	if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
403133762Srwatson		if (ddflags & C_FILL)
404133762Srwatson			memset(out.dbp, fill_char, out.dbsz - out.dbcnt);
405133762Srwatson		else if (ddflags & (C_BLOCK | C_UNBLOCK))
40628430Sjlemon			memset(out.dbp, ' ', out.dbsz - out.dbcnt);
40728430Sjlemon		else
40828430Sjlemon			memset(out.dbp, 0, out.dbsz - out.dbcnt);
4091556Srgrimes		out.dbcnt = out.dbsz;
4101556Srgrimes	}
41130312Sjoerg	if (out.dbcnt || pending)
4121556Srgrimes		dd_out(1);
4131556Srgrimes}
4141556Srgrimes
4151556Srgrimesvoid
41690108Simpdd_out(int force)
4171556Srgrimes{
41851208Sgreen	u_char *outp;
41951208Sgreen	size_t cnt, i, n;
42051208Sgreen	ssize_t nw;
4211556Srgrimes	static int warned;
42251208Sgreen	int sparse;
4231556Srgrimes
4241556Srgrimes	/*
4251556Srgrimes	 * Write one or more blocks out.  The common case is writing a full
4261556Srgrimes	 * output block in a single write; increment the full block stats.
4271556Srgrimes	 * Otherwise, we're into partial block writes.  If a partial write,
4281556Srgrimes	 * and it's a character device, just warn.  If a tape device, quit.
4291556Srgrimes	 *
4301556Srgrimes	 * The partial writes represent two cases.  1: Where the input block
4311556Srgrimes	 * was less than expected so the output block was less than expected.
4321556Srgrimes	 * 2: Where the input block was the right size but we were forced to
4331556Srgrimes	 * write the block in multiple chunks.  The original versions of dd(1)
4341556Srgrimes	 * never wrote a block in more than a single write, so the latter case
4351556Srgrimes	 * never happened.
4361556Srgrimes	 *
4371556Srgrimes	 * One special case is if we're forced to do the write -- in that case
4381556Srgrimes	 * we play games with the buffer size, and it's usually a partial write.
4391556Srgrimes	 */
4401556Srgrimes	outp = out.db;
441266488Sthomas
442266488Sthomas	/*
443266488Sthomas	 * If force, first try to write all pending data, else try to write
444266488Sthomas	 * just one block. Subsequently always write data one full block at
445266488Sthomas	 * a time at most.
446266488Sthomas	 */
4471556Srgrimes	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
448266488Sthomas		cnt = n;
449266488Sthomas		do {
45030312Sjoerg			sparse = 0;
45130312Sjoerg			if (ddflags & C_SPARSE) {
45230312Sjoerg				sparse = 1;	/* Is buffer sparse? */
45330312Sjoerg				for (i = 0; i < cnt; i++)
45430312Sjoerg					if (outp[i] != 0) {
45530312Sjoerg						sparse = 0;
45630312Sjoerg						break;
45730312Sjoerg					}
45830312Sjoerg			}
45930312Sjoerg			if (sparse && !force) {
46030312Sjoerg				pending += cnt;
461266488Sthomas				last_sp = cnt;
46230312Sjoerg				nw = cnt;
46330312Sjoerg			} else {
46430312Sjoerg				if (pending != 0) {
465266488Sthomas					/* If forced to write, and we have no
466266488Sthomas					 * data left, we need to write the last
467266488Sthomas					 * sparse block explicitly.
468266488Sthomas					 */
469266488Sthomas					if (force && cnt == 0) {
470266488Sthomas						pending -= last_sp;
471266488Sthomas						assert(outp == out.db);
472266488Sthomas						memset(outp, 0, cnt);
473266488Sthomas					}
47448051Sgreen					if (lseek(out.fd, pending, SEEK_CUR) ==
47548051Sgreen					    -1)
47648051Sgreen						err(2, "%s: seek error creating sparse file",
47748051Sgreen						    out.name);
478266488Sthomas					pending = last_sp = 0;
47930312Sjoerg				}
48030312Sjoerg				if (cnt)
48130312Sjoerg					nw = write(out.fd, outp, cnt);
48230312Sjoerg				else
48330312Sjoerg					return;
48430312Sjoerg			}
48530312Sjoerg
4861556Srgrimes			if (nw <= 0) {
4871556Srgrimes				if (nw == 0)
4881556Srgrimes					errx(1, "%s: end of device", out.name);
4891556Srgrimes				if (errno != EINTR)
4901556Srgrimes					err(1, "%s", out.name);
4911556Srgrimes				nw = 0;
4921556Srgrimes			}
493266488Sthomas
4941556Srgrimes			outp += nw;
4951556Srgrimes			st.bytes += nw;
496266488Sthomas
497266488Sthomas			if ((size_t)nw == n && n == out.dbsz)
498266488Sthomas				++st.out_full;
499266488Sthomas			else
500266488Sthomas				++st.out_part;
501266488Sthomas
502266488Sthomas			if ((size_t) nw != cnt) {
503266488Sthomas				if (out.flags & ISTAPE)
504266488Sthomas					errx(1, "%s: short write on tape device",
505266488Sthomas				    	out.name);
506266488Sthomas				if (out.flags & ISCHR && !warned) {
507266488Sthomas					warned = 1;
508266488Sthomas					warnx("%s: short write on character device",
509266488Sthomas				    	out.name);
510266488Sthomas				}
5111556Srgrimes			}
512266488Sthomas
513266488Sthomas			cnt -= nw;
514266488Sthomas		} while (cnt != 0);
515266488Sthomas
5161556Srgrimes		if ((out.dbcnt -= n) < out.dbsz)
5171556Srgrimes			break;
5181556Srgrimes	}
5191556Srgrimes
5201556Srgrimes	/* Reassemble the output block. */
5211556Srgrimes	if (out.dbcnt)
52251208Sgreen		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
5231556Srgrimes	out.dbp = out.db + out.dbcnt;
5241556Srgrimes}
525