1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1989-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  *
25  * posix dd
26  */
27 
28 static const char usage1[] =
29 "[-1p0?\n@(#)$Id: dd (AT&T Research) 2011-04-21 $\n]"
30 USAGE_LICENSE
31 "[+NAME?dd - convert and copy a file]"
32 "[+DESCRIPTION?\bdd\b copies an input file to an output file with optional"
33 "	conversions. The standard input and output are used by default."
34 "	Input and output block sizes can be specified to take advantage of"
35 "	physical io limitations. Options are of the form \aname=value\a"
36 "	and may be specified with 0, 1, or 2 leading `-' characters.]"
37 "[+?A \anumber\a argument may be a scaled number with optional trailing"
38 " multipliers of the form `* \anumber\a', `x \anumber\a' or `X \anumber\a'."
39 " Scale suffixes may be one of:]{"
40 "	[+b|B?512]"
41 "	[+k|K?1000]"
42 "	[+ki|Ki?1024]"
43 "	[+m|M?1000000]"
44 "	[+mi|Mi?1048576]"
45 "	[+g|G?1000000000]"
46 "	[+gi|Gi?1073741824]"
47 "	[+t|T?1000000000000]"
48 "	[+ti|Ti?1099511627776]"
49 "	[+p|P?1000000000000000]"
50 "	[+pi|Pi?1125899906842624]"
51 "	[+e|E?1000000000000000000]"
52 "	[+ei|Ei?1152921504606846976]"
53 "}"
54 ;
55 
56 static const char usage2[] =
57 "[+SEE ALSO?\bcp\b(1), \biconv\b(1), \bpax\b(1), \btr\b(1), \bseek\b(2)]"
58 ;
59 
60 #include <ast.h>
61 #include <ctype.h>
62 #include <ccode.h>
63 #include <error.h>
64 #include <iconv.h>
65 #include <ls.h>
66 #include <sig.h>
67 #include <swap.h>
68 
69 #define CODE		0
70 #define CONV		1
71 #define FLAG		2
72 #define NUMBER		3
73 #define STRING		4
74 
75 #define A2E		(1<<0)
76 #define A2I		(1<<1)
77 #define A2N		(1<<2)
78 #define A2O		(1<<3)
79 #define BLOCK		(1<<4)
80 #define E2A		(1<<5)
81 #define IGNERROR	(1<<6)
82 #define I2A		(1<<7)
83 #define ISPECIAL	(1<<8)
84 #define LCASE		(1<<9)
85 #define N2A		(1<<10)
86 #define NOERROR		(1<<11)
87 #define NOTRUNC		(1<<12)
88 #define O2A		(1<<13)
89 #define OSPECIAL	(1<<14)
90 #define SILENT		(1L<<15)
91 #define SWAP		(1L<<16)
92 #define SYNC		(1L<<17)
93 #define UCASE		(1L<<18)
94 #define UNBLOCK		(1L<<19)
95 
96 #define BS		512
97 
98 #define operand_begin	bs
99 #define operand_end	to
100 
101 #define conv_begin	a2e
102 #define conv_end	unblock
103 
104 typedef struct
105 {
106 	const char*	name;
107 	const char*	help;
108 } Desc_t;
109 
110 typedef struct
111 {
112 	char*		string;
113 	Sflong_t	number;
114 } Value_t;
115 
116 typedef struct
117 {
118 	const char*	name;
119 	long		type;
120 	const char*	help;
121 	Value_t		value;
122 } Operand_t;
123 
124 typedef struct
125 {
126 	Sfio_t*		fp;
127 	Sfulong_t	complete;
128 	Sfulong_t	partial;
129 	Sfulong_t	truncated;
130 	Sfulong_t	remains;
131 	int		special;
132 } Io_t;
133 
134 typedef struct
135 {
136 	Operand_t	bs;
137 	Operand_t	cbs;
138 	Operand_t	conv;
139 	Operand_t	count;
140 	Operand_t	from;
141 	Operand_t	ibs;
142 	Operand_t	ifn;
143 	Operand_t	iseek;
144 	Operand_t	obs;
145 	Operand_t	ofn;
146 	Operand_t	oseek;
147 	Operand_t	silent;
148 	Operand_t	skip;
149 	Operand_t	swap;
150 	Operand_t	to;
151 
152 	Operand_t	a2e;
153 	Operand_t	a2i;
154 	Operand_t	a2n;
155 	Operand_t	a2o;
156 	Operand_t	ascii;
157 	Operand_t	block;
158 	Operand_t	e2a;
159 	Operand_t	ebcdic;
160 	Operand_t	i2a;
161 	Operand_t	ibm;
162 	Operand_t	ifix;
163 	Operand_t	ignerror;
164 	Operand_t	lcase;
165 	Operand_t	n2a;
166 	Operand_t	noerror;
167 	Operand_t	notrunc;
168 	Operand_t	o2a;
169 	Operand_t	ofix;
170 	Operand_t	swab;
171 	Operand_t	sync;
172 	Operand_t	ucase;
173 	Operand_t	unblock;
174 
175 	Iconv_disc_t	iconv;
176 	Io_t		in;
177 	Io_t		out;
178 	char*		buffer;
179 	int		pad;
180 	iconv_t		cvt;
181 	Sfio_t*		tmp;
182 } State_t;
183 
184 static State_t		state =
185 {
186 	{
187 		"bs",
188 		NUMBER,
189 		"Input and output block size.",
190 	},
191 	{
192 		"cbs",
193 		NUMBER,
194 		"Conversion buffer size (logical record length)."
195 	},
196 	{
197 		"conv",
198 		CONV,
199 		"Convert input.",
200 	},
201 	{
202 		"count",
203 		NUMBER,
204 		"Copy only \anumber\a input blocks.",
205 	},
206 	{
207 		"from",
208 		CODE,
209 		"Convert from \acodeset\a to the \bto\b=\acodeset\a"
210 		" or the local default. \acodeset\a names are matched"
211 		" by left-anchored case-insensitive \bksh\b(1) patterns.",
212 	},
213 	{
214 		"ibs",
215 		NUMBER,
216 		"Input block size.",
217 		0, BS,
218 	},
219 	{
220 		"if",
221 		STRING,
222 		"The input file name; standard input is the default.",
223 	},
224 	{
225 		"iseek",
226 		NUMBER,
227 		"Seek \anumber\a blocks from the beginning of the input"
228 		" file before copying.",
229 	},
230 	{
231 		"obs",
232 		NUMBER,
233 		"Output block size.",
234 		0, BS,
235 	},
236 	{
237 		"of",
238 		STRING,
239 		"The output file name; standard output is the default.",
240 	},
241 	{
242 		"oseek|seek",
243 		NUMBER,
244 		"Seek \anumber\a blocks from the beginning of the output"
245 		" file before copying.",
246 	},
247 	{
248 		"silent",
249 		FLAG,
250 		"\bsilent\b does not print the total number of io blocks"
251 		" on exit.",
252 	},
253 	{
254 		"skip",
255 		NUMBER,
256 		"Skip \anumber\a blocks before reading.  Seek is used if"
257 		" possible, otherwise the blocks are read and discarded.",
258 	},
259 	{
260 		"swap",
261 		NUMBER,
262 		"Swap bytes acording to the inclusive or of: 1-byte,"
263 		" 2-short, 4-long, 8-quad, etc.",
264 	},
265 	{
266 		"to",
267 		CODE,
268 		"Convert to \acodeset\a from the \bfrom\b=\acodeset\a"
269 		" or the local default. See \bfrom\b for \acodeset\a names.",
270 	},
271 
272 	{
273 		"a2e",
274 		A2E,
275 		"ascii to ebcdic",
276 	},
277 	{
278 		"a2i",
279 		A2I,
280 		"ascii to ibm",
281 	},
282 	{
283 		"a2n",
284 		A2N,
285 		"ascii to native",
286 	},
287 	{
288 		"a2o",
289 		A2O,
290 		"ascii to open edition ebcdic",
291 	},
292 	{
293 		"ascii",
294 		E2A,
295 		"ebcdic to ascii",
296 	},
297 	{
298 		"block",
299 		BLOCK,
300 		"newline-terminated ascii to fixed record length",
301 	},
302 	{
303 		"e2a",
304 		E2A,
305 		"ebcdic to ascii",
306 	},
307 	{
308 		"ebcdic",
309 		A2E,
310 		"equivalent to \ba2e\b",
311 	},
312 	{
313 		"i2a",
314 		I2A,
315 		"ibm to ascii",
316 	},
317 	{
318 		"ibm",
319 		A2I,
320 		"ascii to ibm ebcdic",
321 	},
322 	{
323 		"ignerror",
324 		IGNERROR,
325 		"continue processing after errors",
326 	},
327 	{
328 		"ispecial",
329 		ISPECIAL,
330 		"currently ignored",
331 	},
332 	{
333 		"lcase",
334 		LCASE,
335 		"to lower case",
336 	},
337 	{
338 		"n2a",
339 		N2A,
340 		"native to ascii",
341 	},
342 	{
343 		"noerror",
344 		NOERROR,
345 		"stop processing only after 5 consecutive errors",
346 	},
347 	{
348 		"notrunc",
349 		NOTRUNC,
350 		"do not truncate pre-existing output files",
351 	},
352 	{
353 		"o2a",
354 		O2A,
355 		"open edition ibm to ascii",
356 	},
357 	{
358 		"ospecial",
359 		OSPECIAL,
360 		"currently ignored"
361 	},
362 	{
363 		"swab",
364 		SWAP,
365 		"swap byte pairs",
366 	},
367 	{
368 		"sync",
369 		SYNC,
370 		"Pad each input block to \bibs\b. Pad with spaces if"
371 		" \bconv=block\b or \bconv=unblock\b, otherwise pad"
372 		" with nulls.",
373 	},
374 	{
375 		"ucase",
376 		UCASE,
377 		"to upper case",
378 	},
379 	{
380 		"unblock",
381 		UNBLOCK,
382 		"fixed-length records to newline-terminated records",
383 	},
384 };
385 
386 static Desc_t		desc[] =
387 {
388 	"codeset",	"",
389 	"conversion",	"",
390 	0,		0,
391 	"number",	0,
392 	"file",		0,
393 };
394 
395 static void
fini(int code)396 fini(int code)
397 {
398 	if (state.in.fp != sfstdin)
399 		sfclose(state.in.fp);
400 	if (state.out.fp == sfstdout)
401 		sfsync(state.out.fp);
402 	else
403 		sfclose(state.out.fp);
404 	if (!state.silent.value.number)
405 	{
406 		sfprintf(sfstderr, "%I*u+%I*u records in\n", sizeof(Sfulong_t), (Sfulong_t)state.in.complete, sizeof(Sfulong_t), (Sfulong_t)(state.in.partial + (state.in.remains > 0)));
407 		sfprintf(sfstderr, "%I*u+%I*u records out\n", sizeof(Sfulong_t), (Sfulong_t)state.out.complete, sizeof(Sfulong_t), (Sfulong_t)(state.out.partial + (state.out.remains > 0)));
408 		if (state.in.truncated)
409 			sfprintf(sfstderr, "%I*u truncated record%s\n", sizeof(Sfulong_t), (Sfulong_t)state.in.truncated, state.in.truncated == 1 ? "" : "s");
410 	}
411 	exit(code);
412 }
413 
414 static void
interrupt(int sig)415 interrupt(int sig)
416 {
417 	signal(sig, SIG_DFL);
418 	fini(EXIT_TERM(sig));
419 }
420 
421 static ssize_t
output(Sfio_t * sp,const Void_t * buf,size_t n,Sfdisc_t * disc)422 output(Sfio_t* sp, const Void_t* buf, size_t n, Sfdisc_t* disc)
423 {
424 	register ssize_t	r;
425 	register size_t		x;
426 
427 	if ((r = sfwr(sp, buf, n, disc)) > 0)
428 	{
429 		x = r / state.obs.value.number;
430 		state.out.complete += x;
431 		if (x = r - x * state.obs.value.number)
432 		{
433 			if (state.out.special)
434 				state.out.partial++;
435 			else if ((state.out.remains += x) >= state.obs.value.number)
436 			{
437 				state.out.remains -= state.obs.value.number;
438 				state.out.complete++;
439 			}
440 		}
441 	}
442 	return r;
443 }
444 
445 int
main(int argc,char ** argv)446 main(int argc, char** argv)
447 {
448 	register char*		s;
449 	register char*		v;
450 	register char*		b;
451 	register Operand_t*	op;
452 	register Operand_t*	vp;
453 	register int		f;
454 	char*			usage;
455 	char*			e;
456 	int			i;
457 	char*			cb;
458 	size_t			cc;
459 	Sfio_t*			sp;
460 	Sflong_t		c;
461 	Sflong_t		d;
462 	Sflong_t		m;
463 	Sflong_t		n;
464 	Sflong_t		r;
465 	Sflong_t		partial;
466 	struct stat		st;
467 	Sfdisc_t		disc;
468 
469 	setlocale(LC_ALL, "");
470 	error_info.id = "dd";
471 	iconv_init(&state.iconv, errorf);
472 	state.from.value.string = state.to.value.string = "";
473 	if (!(sp = sfstropen()))
474 		error(ERROR_SYSTEM|3, "out of space");
475 	sfputr(sp, usage1, '\n');
476 	for (op = &state.operand_begin; op <= &state.operand_end; op++)
477 	{
478 		sfprintf(sp, "[%d:%s?%s", op - &state.operand_begin + 10, op->name, op->help);
479 		i = ']';
480 		if (desc[op->type].name)
481 		{
482 			if (desc[op->type].help)
483 			{
484 				if (*desc[op->type].help)
485 					sfprintf(sp, " %s", desc[op->type].help);
486 				else
487 					sfprintf(sp, " \a%s\a may be one of:", desc[op->type].name);
488 				sfprintf(sp, "]:[%s", desc[op->type].name);
489 				desc[op->type].help = 0;
490 				if (op->type == CONV)
491 				{
492 					i = '}';
493 					sfprintf(sp, "]{\n");
494 					for (vp = &state.conv_begin; vp <= &state.conv_end; vp++)
495 						sfprintf(sp, "\t[+%s?%s]\n", vp->name, vp->help);
496 				}
497 				else if (op->type == CODE)
498 				{
499 					register iconv_list_t*	ic;
500 
501 					sfputc(sp, ']');
502 					sfputc(sp, '{');
503 					for (ic = iconv_list(NiL); ic; ic = iconv_list(ic))
504 					{
505 						sfputc(sp, '[');
506 						sfputc(sp, '+');
507 						sfputc(sp, '\b');
508 						optesc(sp, ic->match, '?');
509 						sfputc(sp, '?');
510 						optesc(sp, ic->desc, 0);
511 						sfputc(sp, ']');
512 						sfputc(sp, '\n');
513 					}
514 					i = '}';
515 				}
516 			}
517 			else
518 				sfprintf(sp, "]:[%s", desc[op->type].name);
519 		}
520 		sfputc(sp, i);
521 		sfputc(sp, '\n');
522 	}
523 	sfputr(sp, usage2, '\n');
524 	if (!(usage = sfstruse(sp)))
525 		error(ERROR_SYSTEM|3, "out of space");
526 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
527 		signal(SIGINT, interrupt);
528 	while (f = optget(argv, usage))
529 	{
530 
531 		if (f > 0)
532 		{
533 			if (f == '?')
534 				error(ERROR_USAGE|4, "%s", opt_info.arg);
535 			if (f = ':')
536 				error(2, "%s", opt_info.arg);
537 			continue;
538 		}
539 		op = &state.operand_begin - (f + 10);
540 		v = opt_info.arg;
541 		switch (op->type)
542 		{
543 		case CODE:
544 		case STRING:
545 			op->value.string = v;
546 			break;
547 		case CONV:
548 			do
549 			{
550 				if (e = strchr(v, ','))
551 					*e = 0;
552 				vp = (Operand_t*)strsearch(&state.conv_begin, &state.conv_end - &state.conv_begin + 1, sizeof(Operand_t), stracmp, v, NiL);
553 				if (e)
554 					*e++ = ',';
555 				if (!vp)
556 					error(3, "%s: unknown %s value", v, op->name);
557 				op->value.number |= vp->type;
558 			} while (v = e);
559 			break;
560 		case FLAG:
561 			op->value.number = opt_info.number;
562 			break;
563 		case NUMBER:
564 			c = 0;
565 			for (;;)
566 			{
567 				n = strtonll(v, &e, NiL, 0);
568 				if (n < 0)
569 					error(3, "%s: %s must be >= 0", v, op->name);
570 				if (!c)
571 				{
572 					c = 1;
573 					op->value.number = n;
574 				}
575 				else
576 					op->value.number *= n;
577 				for (v = e; isspace(*v); v++);
578 				if (*v != 'x' && *v != 'X' && *v != '*')
579 					break;
580 				while (isspace(*++v));
581 				if (!*v)
582 				{
583 					v = e;
584 					break;
585 				}
586 			}
587 			if (*v)
588 				error(3, "%s: %s: invalid numeric expression", op->name, opt_info.arg);
589 			break;
590 		}
591 	}
592 	if (error_info.errors)
593 		error(ERROR_USAGE|4, "%s", optusage(NiL));
594 	error_info.exit = fini;
595 	switch ((long)(state.conv.value.number & (A2E|A2I|A2N|A2O|E2A|I2A|N2A|O2A)))
596 	{
597 	case 0:
598 		break;
599 	case A2E:
600 		state.from.value.string = "ascii";
601 		state.to.value.string = "ebcdic-e";
602 		break;
603 	case A2I:
604 		state.from.value.string = "ascii";
605 		state.to.value.string = "ebcdic-i";
606 		break;
607 	case A2N:
608 		state.from.value.string = "ascii";
609 		state.to.value.string = "";
610 		break;
611 	case A2O:
612 		state.from.value.string = "ascii";
613 		state.to.value.string = "ebcdic-o";
614 		break;
615 	case E2A:
616 		state.from.value.string = "ebcdic-e";
617 		state.to.value.string = "ascii";
618 		break;
619 	case I2A:
620 		state.from.value.string = "ebcdic-i";
621 		state.to.value.string = "ascii";
622 		break;
623 	case N2A:
624 		state.from.value.string = "";
625 		state.to.value.string = "ascii";
626 		break;
627 	case O2A:
628 		state.from.value.string = "ebcdic-o";
629 		state.to.value.string = "ascii";
630 		break;
631 	default:
632 		error(3, "only one of %s={%s,%s,%s} may be specified", state.conv.name, state.ascii.value.string, state.ebcdic.value.string, state.ibm.value.string);
633 	}
634 	if (streq(state.from.value.string, state.to.value.string))
635 		state.cvt = (iconv_t)(-1);
636 	else  if ((state.cvt = iconv_open(state.to.value.string, state.from.value.string)) == (iconv_t)(-1))
637 		error(3, "cannot convert from %s to %s", *state.from.value.string ? state.from.value.string : ccmapname(CC_NATIVE), *state.to.value.string ? state.to.value.string : ccmapname(CC_NATIVE));
638 	else if (state.cvt == (iconv_t)(0)) /* ast iconv identity */
639 		state.cvt = (iconv_t)(-1);
640 	else if (!(state.tmp = sfstropen()))
641 		error(ERROR_SYSTEM|3, "out of space");
642 	if ((state.conv.value.number & (BLOCK|UNBLOCK)) == (BLOCK|UNBLOCK))
643 		error(3, "only one of %s=%s and %s=%s may be specified", state.conv.name, state.block.value.string, state.conv.name, state.unblock.value.string);
644 	if ((state.conv.value.number & (SYNC|UNBLOCK)) == (SYNC|UNBLOCK))
645 	{
646 		state.conv.value.number &= ~SYNC;
647 		error(1, "%s=%s ignored for %s=%s", state.conv.name, state.sync.value.string, state.conv.name, state.unblock.value.string);
648 	}
649 	if (state.conv.value.number & SWAP)
650 		state.swap.value.number = 1;
651 	else if (state.swap.value.number)
652 		state.conv.value.number |= SWAP;
653 	if (state.oseek.value.number && (state.conv.value.number & NOTRUNC))
654 	{
655 		state.oseek.value.number = 0;
656 		error(1, "%s ignored for %s=%s", state.oseek.name, state.conv.name, state.notrunc.value.string);
657 	}
658 	if ((state.conv.value.number & (LCASE|UCASE)) == (LCASE|UCASE))
659 		error(3, "only one of %s=%s and %s=%s may be specified", state.conv.name, state.lcase.value.string, state.conv.name, state.ucase.value.string);
660 	if (state.conv.value.number & (BLOCK|UNBLOCK))
661 	{
662 		if (!state.cbs.value.number)
663 			error(3, "%s must be specified for %s=%s", state.cbs.name, state.conv.name, (state.conv.value.number & BLOCK) ? state.block.value.string : state.unblock.value.string);
664 		state.pad = ' ';
665 		if (state.conv.value.number & UNBLOCK)
666 		{
667 			state.ibs.value.number = state.cbs.value.number;
668 			if (state.bs.value.number)
669 			{
670 				state.obs.value.number = state.bs.value.number;
671 				state.bs.value.number = 0;
672 			}
673 		}
674 	}
675 	else if (state.cbs.value.number)
676 	{
677 		state.cbs.value.number = 0;
678 		error(1, "%s ignored", state.cbs.name);
679 	}
680 	if (n = state.bs.value.number)
681 		state.ibs.value.number = state.obs.value.number = n;
682 	if (n = state.iseek.value.number)
683 	{
684 		if (state.skip.value.number)
685 			error(3, "only on of %s and %s may be specified", state.skip.name, state.iseek.name);
686 		if (!state.ibs.value.number)
687 			error(3, "%s requires %s or %s", state.iseek.name, state.bs.name, state.ibs.name);
688 		state.skip.value.number = n;
689 	}
690 	if (state.oseek.value.number && !state.obs.value.number)
691 		error(3, "%s requires %s or %s", state.oseek.name, state.bs.name, state.obs.name);
692 	if (!(s = state.ifn.value.string))
693 	{
694 		state.ifn.value.string = "/dev/stdin";
695 		state.in.fp = sfstdin;
696 #if O_NONBLOCK
697 		if ((i = fcntl(sffileno(sfstdin), F_GETFL)) > 0 && (i & O_NONBLOCK))
698 			fcntl(sffileno(sfstdin), F_SETFL, i & ~O_NONBLOCK);
699 #endif
700 	}
701 	else if (!(state.in.fp = sfopen(NiL, s, "rb")))
702 		error(ERROR_SYSTEM|3, "%s: cannot read", s);
703 	if (!(s = state.ofn.value.string))
704 	{
705 		state.ofn.value.string = "/dev/stdout";
706 		state.out.fp = sfstdout;
707 	}
708 	else if (!(state.out.fp = sfopen(NiL, s, (state.conv.value.number & NOTRUNC) ? "a+b" : state.oseek.value.number ? "w+b" : "wb")))
709 		error(ERROR_SYSTEM|3, "%s: cannot write", s);
710 	if ((state.conv.value.number & ISPECIAL) || fstat(sffileno(state.in.fp), &st) || !S_ISREG(st.st_mode))
711 		state.in.special = 1;
712 	if (state.in.special && !(state.conv.value.number & BLOCK) && (n = state.ibs.value.number))
713 		sfsetbuf(state.in.fp, NiL, n);
714 	if ((state.conv.value.number & OSPECIAL) || fstat(sffileno(state.out.fp), &st) || !S_ISREG(st.st_mode))
715 		state.out.special = 1;
716 	if (state.out.special && !(state.conv.value.number & UNBLOCK) && (n = state.obs.value.number))
717 		sfsetbuf(state.out.fp, NiL, n);
718 	state.bs.value.number = state.ibs.value.number;
719 	if (state.obs.value.number > state.bs.value.number)
720 		state.bs.value.number = state.obs.value.number;
721 	if (state.cbs.value.number > state.bs.value.number)
722 		state.bs.value.number = state.cbs.value.number;
723 	if (!(state.buffer = newof(0, char, state.bs.value.number, 0)))
724 		error(ERROR_SYSTEM|3, "out of space");
725 	if (n = state.skip.value.number)
726 	{
727 		if (!state.ibs.value.number)
728 			error(3, "%s requires %s or %s", state.skip.name, state.bs.name, state.ibs.name);
729 		n *= state.ibs.value.number;
730 		if (sfseek(state.in.fp, n, SEEK_SET) != n)
731 		{
732 			do
733 			{
734 				if (!sfreserve(state.in.fp, state.in.special && state.ibs.value.number < n ? state.ibs.value.number : n, 0))
735 				{
736 					if (sfvalue(state.in.fp) > 0)
737 						error(ERROR_SYSTEM|3, "%s: seek read error", state.ifn.value.string);
738 					break;
739 				}
740 			} while ((n -= sfvalue(state.in.fp)) > 0);
741 			if (n > 0)
742 				error(3, "%s: cannot seek past end of file", state.ifn.value.string);
743 		}
744 	}
745 	if (n = state.oseek.value.number)
746 	{
747 		n *= state.obs.value.number;
748 		if (sfseek(state.out.fp, n, SEEK_SET) != n)
749 		{
750 			do
751 			{
752 				if (!sfreserve(state.out.fp, state.out.special && state.obs.value.number < n ? state.obs.value.number : n, 0))
753 				{
754 					if (sfvalue(state.out.fp) > 0)
755 						error(ERROR_SYSTEM|3, "%s: seek read error", state.ofn.value.string);
756 					break;
757 				}
758 			} while ((n -= sfvalue(state.out.fp)) > 0);
759 			while (n > 0 && (c = sfwrite(state.out.fp, state.buffer, state.out.special && state.obs.value.number < n ? state.obs.value.number : n)) > 0)
760 				n -= c;
761 			if (c < 0)
762 				error(ERROR_SYSTEM|3, "%s: seek 0 fill write error", state.ofn.value.string);
763 		}
764 	}
765 	if (state.silent.value.number)
766 		state.iconv.errorf = 0;
767 	if (state.conv.value.number & NOERROR)
768 	{
769 		state.iconv.errorf = 0;
770 		if (state.conv.value.number & SYNC)
771 			state.iconv.fill = 0;
772 		else
773 			state.iconv.flags |= ICONV_OMIT;
774 	}
775 	else
776 		state.iconv.flags |= ICONV_FATAL;
777 	memset(&disc, 0, sizeof(disc));
778 	disc.writef = output;
779 	sfdisc(state.out.fp, &disc);
780 	if (!state.obs.value.number)
781 		state.obs.value.number = BS;
782 	if (state.conv.value.number & BLOCK)
783 	{
784 		c = state.cbs.value.number;
785 		memset(state.buffer, state.pad, c);
786 		while ((s = sfgetr(state.in.fp, '\n', 0)) || (s = sfgetr(state.in.fp, '\n', -1)))
787 		{
788 			state.in.complete++;
789 			n = sfvalue(state.in.fp) - 1;
790 			if (n > c)
791 			{
792 				if (sfwrite(state.out.fp, s, c) != c)
793 					error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
794 				state.in.truncated++;
795 			}
796 			else
797 			{
798 				if (sfwrite(state.out.fp, s, n) != n)
799 					error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
800 				n = c - n;
801 				if (sfwrite(state.out.fp, state.buffer, n) != n)
802 					error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
803 			}
804 		}
805 	}
806 	else
807 	{
808 		if (!(c = state.ibs.value.number))
809 			c = BS;
810 		f = state.conv.value.number;
811 		if (!state.in.special)
812 			f &= ~NOERROR;
813 		if (!(r = state.count.value.number))
814 			r = -1;
815 		partial = 0;
816 		while (state.in.complete != r)
817 		{
818 			b = sfreserve(state.in.fp, SF_UNBOUND, 0);
819 			m = sfvalue(state.in.fp);
820 			if (!b)
821 			{
822 				if (!m)
823 					break;
824 				error(ERROR_SYSTEM|((f & NOERROR) ? 2 : 3), "%s: read error", state.ifn.value.string);
825 				memset(b = state.buffer, state.pad, m = c);
826 			}
827 			while (state.in.complete != r)
828 			{
829 				s = b;
830 
831 				/*
832 				 * m is the amount actually read
833 				 * c is the specified (or default) block size
834 				 */
835 
836 				if (m >= c)
837 				{
838 					/*
839 					 * the read gave us at least a complete block
840 					 */
841 
842 					state.in.complete++;
843 
844 					/*
845 					 * set n (the # of bytes to write) to the block size
846 					 */
847 
848 					n = c;
849 
850 					/*
851 					 * if we've hit the block count, check to see if the
852 					 * previous write was in the middle of an output block, and
853 					 * adjust the # of bytes to write accordingly; don't bother
854 					 * adjusting the remains, since we're done
855 					 */
856 
857 					if (state.in.complete >= r && (state.in.remains + n) >= c)
858 						n -= state.in.remains;
859 				}
860 				else
861 				{
862 					/*
863 					 * read gave us less than a complete block
864 					 */
865 
866 					n = m;
867 					if (state.in.special)
868 						state.in.partial++;
869 
870 					/*
871 					 * see if writing the amount read in will cross an (output) block boundry
872 					 */
873 
874 					if ((state.in.remains + n) >= c)
875 					{
876 						state.in.complete++;
877 
878 						/*
879 						 * see above comment on block count, but also adjust
880 						 * partial block byte count (remains)
881 						 */
882 
883 						if (state.in.complete >= r)
884 						{
885 							n = c - state.in.remains;
886 							state.in.remains += m - c;
887 						}
888 						else
889 						{
890 							partial++;
891 							state.in.remains += m - c;
892 						}
893 					}
894 					else
895 						state.in.remains += n;
896 					if (f & SYNC)
897 					{
898 						s = memcpy(state.buffer, s, n);
899 						memset(s + n, state.pad, c - n);
900 						n = c;
901 					}
902 				}
903 				if (f & SWAP)
904 					swapmem(state.swap.value.number, s, s, n);
905 				if (state.cvt != (iconv_t)(-1))
906 				{
907 					cb = s;
908 					cc = n;
909 					state.iconv.errors = 0;
910 					if ((d = iconv_write(state.cvt, state.tmp, &cb, &cc, &state.iconv)) < 0)
911 						d = 0;
912 					if (!(s = sfstruse(state.tmp)))
913 						error(ERROR_SYSTEM|3, "out of space");
914 					m -= n - d;
915 					if (state.iconv.errors && (state.iconv.flags & ICONV_FATAL))
916 					{
917 						m -= n;
918 						n = 0;
919 					}
920 				}
921 				switch (f & (LCASE|UCASE))
922 				{
923 				case LCASE:
924 					for (v = (e = s) + n; s < v; s++)
925 						if (isupper(*s))
926 							*s = tolower(*s);
927 					s = e;
928 					break;
929 				case UCASE:
930 					for (v = (e = s) + n; s < v; s++)
931 						if (islower(*s))
932 							*s = toupper(*s);
933 					s = e;
934 					break;
935 				}
936 				if (f & UNBLOCK)
937 				{
938 					for (v = s + n; v > s && *(v - 1) == ' '; v--);
939 					if (sfwrite(state.out.fp, s, v - s) != (v - s) || sfputc(state.out.fp, '\n') != '\n')
940 						error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
941 				}
942 				else if (sfwrite(state.out.fp, s, n) != n)
943 					error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
944 				if ((m -= n) <= 0)
945 					break;
946 				b += n;
947 			}
948 		}
949 		if (state.in.partial)
950 		{
951 			state.in.complete -= partial;
952 			state.in.remains = 0;
953 		}
954 		if (sfsync(state.out.fp))
955 			error(ERROR_SYSTEM|3, "%s: write error", state.ofn.value.string);
956 	}
957 	fini(error_info.errors != 0);
958 }
959