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 <gsf@research.att.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