Annotation of src/usr.bin/pr/pr.c, Revision 1.12
1.12 ! deraadt 1: /* $OpenBSD: pr.c,v 1.11 2001/11/02 16:25:02 deraadt Exp $ */
1.2 deraadt 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1991 Keith Muller.
5: * Copyright (c) 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Keith Muller of the University of California, San Diego.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the University of
22: * California, Berkeley and its contributors.
23: * 4. Neither the name of the University nor the names of its contributors
24: * may be used to endorse or promote products derived from this software
25: * without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37: * SUCH DAMAGE.
38: */
39:
40: #ifndef lint
41: static char copyright[] =
42: "@(#) Copyright (c) 1993\n\
43: The Regents of the University of California. All rights reserved.\n";
44: #endif /* not lint */
45:
46: #ifndef lint
47: /* from: static char sccsid[] = "@(#)pr.c 8.1 (Berkeley) 6/6/93"; */
1.12 ! deraadt 48: static char *rcsid = "$OpenBSD: pr.c,v 1.11 2001/11/02 16:25:02 deraadt Exp $";
1.1 deraadt 49: #endif /* not lint */
50:
51: #include <sys/types.h>
52: #include <sys/time.h>
53: #include <sys/stat.h>
54:
55: #include <ctype.h>
56: #include <errno.h>
57: #include <signal.h>
58: #include <stdio.h>
1.12 ! deraadt 59: #include <stdarg.h>
1.1 deraadt 60: #include <stdlib.h>
61: #include <string.h>
62: #include <unistd.h>
63:
64: #include "pr.h"
65: #include "extern.h"
66:
67: /*
68: * pr: a printing and pagination filter. If multiple input files
69: * are specified, each is read, formatted, and written to standard
1.8 provos 70: * output. By default, input is separated into 66-line pages, each
1.1 deraadt 71: * with a header that includes the page number, date, time and the
72: * files pathname.
73: *
74: * Complies with posix P1003.2/D11
75: */
76:
77: /*
1.5 grr 78: * pr: more boundary conditions than a four-legged porcupine
79: *
80: * the original version didn't support form-feeds, while many of the ad-hoc
81: * pr implementations out there do. Addding this and making it work reasonably
82: * in all four output modes required quite a bit of hacking and a few minor
83: * bugs were noted and fixed in the processs. Some implementations have this
84: * as the as -f, some as -F so we accept either.
85: *
1.12 ! deraadt 86: * The implementation of form feeds on top of the existing I/O structure is
1.5 grr 87: * a bit ideosyncratic. Basically they are treated as temporary end-of-file
88: * conditions and an additional level of "loop on form feed" is added to each
89: * of the output modes to continue after such a transient end-of-file's. This
90: * has the general benefit of making the existing header/trailer logic work
91: * and provides a usable framework for rational behavior in multi-column modes.
92: *
93: * The orginal "efficient" implementation of the "skip to page N" option was
94: * bogus and I substituted the basic inhibit printing until page N approach.
95: * This is still fairly bogus vis-a-vis numbering pages on multiple files
96: * restarting at one, but at least lets you consistantly reprint some large
97: * document starting in the middle, in any of the output modes.
98: *
99: * Additional support for overprinting via <back-space> or <return> would
100: * be nice, but is not trivial across tab interpretation, output formatting
101: * and the different operating modes. Support for line-wrapping, either
102: * strict or word-wrapped would be really useful and not all that hard to
103: * kludge into the inln() implementation. The general notion is that -wc n
104: * would specify width and wrapping with a marker character c and -Wc n
105: * would add word wrapping with a minimum width n and delimiters c, defaulting
106: * to tab, blank, and -, and column width. Word wrapping always involves
107: * painful policy questions which are difficult to specify unless you just
108: * hardwire in some fixed rules. Think quotes, punctuation and white-space
109: * elimination and whether you'd do the same thing with a C program and
110: * something like columninated newspaper text.
111: *
112: * George Robbins <grr@tharsis.com> 4/22/97.
113: */
114:
115: /*
1.1 deraadt 116: * parameter variables
117: */
1.5 grr 118: int pgnm; /* starting page number */
119: int skipping; /* we're skipping to page pgnum */
120: int clcnt; /* number of columns */
121: int colwd; /* column data width - multiple columns */
122: int across; /* mult col flag; write across page */
123: int dspace; /* double space flag */
124: char inchar; /* expand input char */
125: int ingap; /* expand input gap */
126: int formfeed; /* use formfeed as trailer */
127: int inform; /* grok formfeeds in input */
128: char *header; /* header name instead of file name */
129: char ochar; /* contract output char */
130: int ogap; /* contract output gap */
131: int lines; /* number of lines per page */
132: int merge; /* merge multiple files in output */
133: char nmchar; /* line numbering append char */
134: int nmwd; /* width of line number field */
135: int offst; /* number of page offset spaces */
136: int nodiag; /* do not report file open errors */
137: char schar; /* text column separation character */
138: int sflag; /* -s option for multiple columns */
139: int nohead; /* do not write head and trailer */
140: int pgwd; /* page width with multiple col output */
141: char *timefrmt; /* time conversion string */
1.1 deraadt 142:
143: /*
144: * misc globals
145: */
1.12 ! deraadt 146: int ferr; /* error message delayed */
1.5 grr 147: int addone = 0; /* page length is odd with double space */
148: int errcnt = 0; /* error count on file processing */
149: int beheaded = 0; /* header / trailer link */
1.1 deraadt 150: char digs[] = "0123456789"; /* page number translation map */
151:
152: int
153: main(argc, argv)
1.5 grr 154: int argc;
155: char *argv[];
1.1 deraadt 156: {
1.5 grr 157: int ret_val;
1.1 deraadt 158:
1.5 grr 159: if (signal(SIGINT, SIG_IGN) != SIG_IGN)
160: (void)signal(SIGINT, terminate);
161: ret_val = setup(argc, argv);
162: if (!ret_val) {
163: /*
164: * select the output format based on options
165: */
166: if (merge)
167: ret_val = mulfile(argc, argv);
168: else if (clcnt == 1)
169: ret_val = onecol(argc, argv);
170: else if (across)
171: ret_val = horzcol(argc, argv);
172: else
173: ret_val = vertcol(argc, argv);
174: } else
175: usage();
176: flsh_errs();
177: if (errcnt || ret_val)
178: exit(1);
179: return(0);
1.1 deraadt 180: }
181:
182: /*
1.5 grr 183: * onecol: print files with only one column of output.
184: * Line length is unlimited.
1.1 deraadt 185: */
186: int
187: onecol(argc, argv)
1.5 grr 188: int argc;
189: char *argv[];
1.1 deraadt 190: {
1.5 grr 191: register int off;
192: register int lrgln;
193: register int linecnt;
194: register int num;
195: int cnt;
196: int rc;
197: int lncnt;
198: int pagecnt;
199: int ips;
200: int ops;
201: int cps;
202: char *obuf;
203: char *lbuf;
204: char *nbuf;
205: char *hbuf;
206: char *ohbuf;
207: FILE *inf;
208: char *fname;
209: int mor;
210:
211: if (nmwd)
212: num = nmwd + 1;
213: else
214: num = 0;
215: off = num + offst;
216:
217: /*
218: * allocate line buffer
219: */
220: if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) {
221: mfail();
222: return(1);
223: }
224:
225: /*
226: * allocate header buffer
227: */
228: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
229: mfail();
230: return(1);
231: }
232:
233: ohbuf = hbuf + offst;
234: nbuf = obuf + offst;
235: lbuf = nbuf + num;
236:
237: if (num)
238: nbuf[--num] = nmchar;
239:
240: if (offst) {
241: (void)memset(obuf, (int)' ', offst);
242: (void)memset(hbuf, (int)' ', offst);
243: }
244:
245: /*
246: * loop by file
247: */
248: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
249: pagecnt = 0;
250: lncnt = 0;
1.1 deraadt 251:
252: /*
1.5 grr 253: * loop by "form"
1.1 deraadt 254: */
1.5 grr 255: for(;;) {
1.1 deraadt 256:
1.5 grr 257: /*
258: * loop by page
259: */
260: for(;;) {
261: linecnt = 0;
262: lrgln = 0;
263: ops = 0;
264: ips = 0;
265: cps = 0;
1.1 deraadt 266:
267: /*
1.5 grr 268: * loop by line
1.1 deraadt 269: */
1.5 grr 270: while (linecnt < lines) {
271:
272: /*
273: * input next line
274: */
275: rc = inln(inf,lbuf,LBUF,&cnt,&cps,0,&mor);
276: if (cnt >= 0) {
277: if (!lrgln)
278: if (!linecnt && prhead(hbuf, fname, ++pagecnt))
279: return(1);
280:
281: /*
282: * start new line or continue a long one
283: */
284: if (!lrgln) {
285: if (num)
286: addnum(nbuf, num, ++lncnt);
287: if (otln(obuf,cnt+off, &ips, &ops, mor))
288: return(1);
289: } else
290: if (otln(lbuf, cnt, &ips, &ops, mor))
291: return(1);
1.1 deraadt 292:
293: /*
1.5 grr 294: * if line bigger than buffer, get more
1.1 deraadt 295: */
1.5 grr 296: if (mor) {
297: lrgln = 1;
298: } else {
299: /*
300: * whole line rcvd. reset tab proc. state
301: */
302: ++linecnt;
303: lrgln = 0;
304: ops = 0;
305: ips = 0;
306: }
307: }
1.1 deraadt 308:
1.5 grr 309: if (rc != NORMAL)
310: break;
311: }
1.1 deraadt 312:
1.5 grr 313: /*
314: * fill to end of page
315: */
316: if (prtail(lines - linecnt, lrgln))
317: return(1);
1.1 deraadt 318:
1.5 grr 319: /*
320: * unless END continue
321: */
322: if (rc == END)
323: break;
324: }
325:
326: /*
327: * On EOF go to next file
328: */
329: if (rc == END)
330: break;
331: }
332:
333: if (inf != stdin)
334: (void)fclose(inf);
335: }
336: /*
337: * If we didn't process all the files, return error
338: */
339: if (eoptind < argc)
340: return(1);
341: else
1.1 deraadt 342: return(0);
343: }
344:
345: /*
346: * vertcol: print files with more than one column of output down a page
1.5 grr 347: * the general approach is to buffer a page of data, then print
1.1 deraadt 348: */
349: int
350: vertcol(argc, argv)
1.5 grr 351: int argc;
352: char *argv[];
1.1 deraadt 353: {
1.5 grr 354: register char *ptbf;
355: register char **lstdat;
356: register int i;
357: register int j;
358: register int pln;
359: register int *indy;
360: int cnt;
361: int rc;
362: int cvc;
363: int *lindy;
364: int lncnt;
365: int stp;
366: int pagecnt;
367: int col = colwd + 1;
368: int mxlen = pgwd + offst + 1;
369: int mclcnt = clcnt - 1;
370: struct vcol *vc;
371: int mvc;
372: int tvc;
373: int cw = nmwd + 1;
374: int fullcol;
375: char *buf;
376: char *hbuf;
377: char *ohbuf;
378: char *fname;
379: FILE *inf;
380: int ips = 0;
381: int cps = 0;
382: int ops = 0;
383: int mor = 0;
384:
385: /*
386: * allocate page buffer
387: */
388: if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) {
389: mfail();
390: return(1);
391: }
392:
393: /*
394: * allocate page header
395: */
396: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
397: mfail();
398: return(1);
399: }
400:
401: ohbuf = hbuf + offst;
402: if (offst)
403: (void)memset(hbuf, (int)' ', offst);
404:
405: /*
406: * col pointers when no headers
407: */
408: mvc = lines * clcnt;
409: if ((vc=(struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) {
410: mfail();
411: return(1);
412: }
413:
414: /*
415: * pointer into page where last data per line is located
416: */
417: if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){
418: mfail();
419: return(1);
420: }
421:
422: /*
423: * fast index lookups to locate start of lines
424: */
425: if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
426: mfail();
427: return(1);
428: }
429: if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) {
430: mfail();
431: return(1);
432: }
433:
434: if (nmwd)
435: fullcol = col + cw;
436: else
437: fullcol = col;
438:
439: /*
440: * initialize buffer lookup indexes and offset area
441: */
442: for (j = 0; j < lines; ++j) {
443: lindy[j] = j * mxlen;
444: indy[j] = lindy[j] + offst;
445: if (offst) {
446: ptbf = buf + lindy[j];
447: (void)memset(ptbf, (int)' ', offst);
448: ptbf += offst;
449: } else
450: ptbf = buf + indy[j];
451: lstdat[j] = ptbf;
452: }
453:
454: /*
455: * loop by file
456: */
457: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
458: pagecnt = 0;
459: lncnt = 0;
1.1 deraadt 460:
461: /*
1.5 grr 462: * loop by "form"
1.1 deraadt 463: */
1.5 grr 464: for (;;) {
1.1 deraadt 465:
1.5 grr 466: /*
467: * loop by page
468: */
469: for(;;) {
1.1 deraadt 470:
1.5 grr 471: /*
472: * loop by column
473: */
474: cvc = 0;
475: for (i = 0; i < clcnt; ++i) {
476: j = 0;
477: /*
478: * if last column, do not pad
479: */
480: if (i == mclcnt)
481: stp = 1;
482: else
483: stp = 0;
484:
485: /*
486: * loop by line
487: */
488: for(;;) {
489: /*
490: * is this first column
491: */
492: if (!i) {
493: ptbf = buf + indy[j];
494: lstdat[j] = ptbf;
495: } else
496: ptbf = lstdat[j];
497: vc[cvc].pt = ptbf;
1.1 deraadt 498:
499: /*
1.5 grr 500: * add number
1.1 deraadt 501: */
1.5 grr 502: if (nmwd) {
503: addnum(ptbf, nmwd, ++lncnt);
504: ptbf += nmwd;
505: *ptbf++ = nmchar;
506: }
1.1 deraadt 507:
508: /*
1.5 grr 509: * input next line
1.1 deraadt 510: */
1.5 grr 511: rc = inln(inf,ptbf,colwd,&cnt,&cps,1,&mor);
512: vc[cvc++].cnt = cnt;
513: if (cnt >= 0) {
514: ptbf += cnt;
515:
516: /*
517: * pad all but last column on page
518: */
519: if (!stp) {
1.1 deraadt 520: /*
1.5 grr 521: * pad to end of column
1.1 deraadt 522: */
1.5 grr 523: if (sflag)
524: *ptbf++ = schar;
525: else if ((pln = col-cnt) > 0) {
526: (void)memset(ptbf,
527: (int)' ',pln);
528: ptbf += pln;
1.1 deraadt 529: }
1.5 grr 530: }
531:
532: /*
533: * remember last char in line
534: */
535: lstdat[j] = ptbf;
536: if (++j >= lines)
537: break;
538: } /* end of if cnt >= 0 */
539:
540: if (rc != NORMAL)
541: break;
542: } /* end of for line */
543:
544: if (rc != NORMAL)
545: break;
546: } /* end of for column */
547:
548: /*
549: * when -t (no header) is specified the spec requires
550: * the min number of lines. The last page may not have
551: * balanced length columns. To fix this we must reorder
552: * the columns. This is a very slow technique so it is
553: * only used under limited conditions. Without -t, the
554: * balancing of text columns is unspecified. To NOT
555: * balance the last page, add the global variable
556: * nohead to the if statement below e.g.
557: */
1.1 deraadt 558:
1.5 grr 559: /*
560: * print header iff we got anything on the first read
561: */
562: if (vc[0].cnt >= 0) {
563: if (prhead(hbuf, fname, ++pagecnt))
564: return(1);
565:
566: /*
567: * check to see if "last" page needs to be reordered
568: */
569: --cvc;
570: if ((rc != NORMAL) && cvc && ((mvc-cvc) >= clcnt)){
571: pln = cvc/clcnt;
572: if (cvc % clcnt)
573: ++pln;
1.1 deraadt 574:
1.5 grr 575: for (i = 0; i < pln; ++i) {
576: ips = 0;
577: ops = 0;
578: if (offst && otln(buf,offst,&ips,&ops,1))
579: return(1);
580: tvc = i;
1.1 deraadt 581:
1.5 grr 582: for (j = 0; j < clcnt; ++j) {
1.1 deraadt 583: /*
1.5 grr 584: * determine column length
1.1 deraadt 585: */
1.5 grr 586: if (j == mclcnt) {
587: /*
588: * last column
589: */
590: cnt = vc[tvc].cnt;
591: if (nmwd)
592: cnt += cw;
593: } else if (sflag) {
594: /*
595: * single ch between
596: */
597: cnt = vc[tvc].cnt + 1;
598: if (nmwd)
599: cnt += cw;
600: } else
601: cnt = fullcol;
602:
603: if (otln(vc[tvc].pt, cnt, &ips, &ops, 1))
604: return(1);
605: tvc += pln;
606: if (tvc > cvc)
607: break;
608: }
609: /*
610: * terminate line
611: */
612: if (otln(buf, 0, &ips, &ops, 0))
613: return(1);
1.1 deraadt 614: }
615:
1.5 grr 616: } else {
617:
1.1 deraadt 618: /*
1.5 grr 619: * just a normal page...
1.1 deraadt 620: * determine how many lines to output
621: */
622: if (i > 0)
1.5 grr 623: pln = lines;
1.1 deraadt 624: else
1.5 grr 625: pln = j;
1.1 deraadt 626:
627: /*
628: * output each line
629: */
630: for (i = 0; i < pln; ++i) {
1.5 grr 631: ptbf = buf + lindy[i];
632: if ((j = lstdat[i] - ptbf) <= offst)
633: break;
634: else {
635: ips = 0;
636: ops = 0;
1.1 deraadt 637: if (otln(ptbf, j, &ips, &ops, 0))
1.5 grr 638: return(1);
639: }
1.1 deraadt 640: }
1.5 grr 641: }
642: }
1.1 deraadt 643:
1.5 grr 644: /*
645: * pad to end of page
646: */
647: if (prtail((lines - pln), 0))
648: return(1);
1.1 deraadt 649:
1.5 grr 650: /*
651: * if FORM continue
652: */
653: if (rc != NORMAL)
654: break;
655: }
656:
657: /*
658: * if EOF go to next file
659: */
660: if (rc == END)
661: break;
1.1 deraadt 662: }
1.5 grr 663:
664: if (inf != stdin)
665: (void)fclose(inf);
666: }
667:
668: if (eoptind < argc)
669: return(1);
670: else
1.1 deraadt 671: return(0);
672: }
673:
674: /*
1.5 grr 675: * horzcol: print files with more than one column of output across a page
1.1 deraadt 676: */
677: int
678: horzcol(argc, argv)
1.5 grr 679: int argc;
680: char *argv[];
1.1 deraadt 681: {
1.5 grr 682: register char *ptbf;
683: register int pln;
684: register char *lstdat;
685: register int col = colwd + 1;
686: register int j;
687: register int i;
688: int cnt;
689: int rc;
690: int lncnt;
691: int pagecnt;
692: char *buf;
693: char *hbuf;
694: char *ohbuf;
695: char *fname;
696: FILE *inf;
697: int cps = 0;
698: int mor = 0;
699: int ips = 0;
700: int ops = 0;
701:
702: if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
703: mfail();
704: return(1);
705: }
706:
707: /*
708: * page header
709: */
710: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
711: mfail();
712: return(1);
713: }
714:
715: ohbuf = hbuf + offst;
716: if (offst) {
717: (void)memset(buf, (int)' ', offst);
718: (void)memset(hbuf, (int)' ', offst);
719: }
720:
721: /*
722: * loop by file
723: */
724: while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
725: pagecnt = 0;
726: lncnt = 0;
1.1 deraadt 727:
728: /*
1.5 grr 729: * loop by form
1.1 deraadt 730: */
1.5 grr 731: for (;;) {
1.1 deraadt 732:
1.5 grr 733: /*
734: * loop by page
735: */
736: for(;;) {
1.1 deraadt 737:
738: /*
1.5 grr 739: * loop by line
1.1 deraadt 740: */
1.5 grr 741: for (i = 0; i < lines; ++i) {
742: ptbf = buf + offst;
743: lstdat = ptbf;
744: j = 0;
745:
746: /*
747: * loop by col
748: */
749: for(;;) {
750: if (nmwd) {
751: /*
752: * add number to column
753: */
754: addnum(ptbf, nmwd, ++lncnt);
755: ptbf += nmwd;
756: *ptbf++ = nmchar;
757: }
758: /*
759: * input line
760: */
761: rc = inln(inf,ptbf,colwd,&cnt,&cps,1, &mor);
762: if (cnt >= 0) {
763: if (!i && !j && prhead(hbuf, fname, ++pagecnt))
764: return(1);
765:
766: ptbf += cnt;
767: lstdat = ptbf;
768:
769: /*
770: * if last line skip padding
771: */
772: if (++j >= clcnt)
773: break;
774:
775: /*
776: * pad to end of column
777: */
778: if (sflag)
779: *ptbf++ = schar;
780: else if ((pln = col - cnt) > 0) {
781: (void)memset(ptbf,(int)' ',pln);
782: ptbf += pln;
783: }
784: }
785: if (rc != NORMAL)
786: break;
787: }
788:
789: /*
790: * output line if any columns on it
791: */
792: if (j) {
793: if (otln(buf, lstdat-buf, &ips, &ops, 0))
794: return(1);
795: }
1.1 deraadt 796:
1.5 grr 797: if (rc != NORMAL)
798: break;
799: }
1.1 deraadt 800:
1.5 grr 801: /*
802: * pad to end of page
803: */
804: if (prtail(lines - i, 0))
805: return(1);
1.1 deraadt 806:
1.5 grr 807: /*
808: * if FORM continue
809: */
810: if (rc == END)
811: break;
812: }
813: /*
814: * if EOF go to next file
815: */
816: if (rc == END)
817: break;
1.1 deraadt 818: }
1.5 grr 819: if (inf != stdin)
820: (void)fclose(inf);
821: }
822: if (eoptind < argc)
823: return(1);
824: return(0);
1.1 deraadt 825: }
826:
1.12 ! deraadt 827: struct ferrlist {
! 828: struct ferrlist *next;
! 829: char *buf;
! 830: };
! 831: struct ferrlist *ferrhead, *ferrtail;
! 832:
! 833: /*
! 834: * flsh_errs(): output saved up diagnostic messages after all normal
! 835: * processing has completed
! 836: */
! 837: void
! 838: flsh_errs()
! 839: {
! 840: struct ferrlist *f;
! 841:
! 842: if (ferr) {
! 843: for (f = ferrhead; f; f = f->next)
! 844: (void)write(STDERR_FILENO, f->buf, strlen(f->buf));
! 845: }
! 846: }
! 847:
! 848: void
! 849: ferrout(char *fmt, ...)
! 850: {
! 851: sigset_t block, oblock;
! 852: struct ferrlist *f;
! 853: va_list ap;
! 854: char *p;
! 855:
! 856: va_start(ap, fmt);
! 857: if (ferr == NULL)
! 858: vfprintf(stderr, fmt, ap);
! 859: else {
! 860: sigemptyset(&block);
! 861: sigaddset(&block, SIGINT);
! 862: sigprocmask(SIG_BLOCK, &block, &oblock);
! 863:
! 864: vasprintf(&p, fmt, ap);
! 865: f = (struct ferrlist *)malloc(sizeof(*f));
! 866: f->next = NULL;
! 867: f->buf = p;
! 868: if (ferrhead == NULL)
! 869: ferrhead = f;
! 870: if (ferrtail)
! 871: ferrtail->next = f;
! 872: ferrtail = f;
! 873: sigprocmask(SIG_SETMASK, &oblock, NULL);
! 874: }
! 875: }
! 876:
1.1 deraadt 877: /*
1.5 grr 878: * mulfile: print files with more than one column of output and
879: * more than one file concurrently
1.1 deraadt 880: */
881: int
882: mulfile(argc, argv)
1.5 grr 883: int argc;
884: char *argv[];
1.1 deraadt 885: {
1.5 grr 886: register char *ptbf;
887: register int j;
888: register int pln;
889: int *rc;
890: int cnt;
891: register char *lstdat;
892: register int i;
893: FILE **fbuf;
894: int actf;
895: int lncnt;
896: int col;
897: int pagecnt;
898: int fproc;
899: char *buf;
900: char *hbuf;
901: char *ohbuf;
902: char *fname;
903: int ips = 0;
904: int cps = 0;
905: int ops = 0;
906: int mor = 0;
907:
908: /*
909: * array of FILE *, one for each operand
910: */
911: if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) {
912: mfail();
913: return(1);
914: }
915:
916: /*
917: * array of int *, one for each operand
918: */
919: if ((rc = (int *)malloc((unsigned)clcnt*sizeof(int))) == NULL) {
920: mfail();
921: return(1);
922: }
923:
924: /*
925: * page header
926: */
927: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
928: mfail();
929: return(1);
930: }
931: ohbuf = hbuf + offst;
932:
933: /*
934: * do not know how many columns yet. The number of operands provide an
935: * upper bound on the number of columns. We use the number of files
936: * we can open successfully to set the number of columns. The operation
937: * of the merge operation (-m) in relation to unsuccesful file opens
938: * is unspecified by posix.
939: *
940: * XXX - this seems moderately bogus, you'd think that specifying
941: * "pr -2 a b c d" would run though all the files in pairs, but
942: * the existing code says up two files, or fewer if one is bogus.
943: * fixing it would require modifying the looping structure, so be it.
944: */
945: j = 0;
946: while (j < clcnt) {
947: if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL) {
948: rc[j] = NORMAL;
949: j++;
950: }
951: }
952:
953: /*
954: * if no files, exit
955: */
956: if (j)
1.1 deraadt 957: clcnt = j;
1.5 grr 958: else
959: return(1);
1.1 deraadt 960:
1.5 grr 961: /*
962: * calculate page boundries based on open file count
963: */
964: if (nmwd) {
965: colwd = (pgwd - clcnt - nmwd)/clcnt;
966: pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
967: } else {
968: colwd = (pgwd + 1 - clcnt)/clcnt;
969: pgwd = ((colwd + 1) * clcnt) - 1;
970: }
971: if (colwd < 1) {
1.12 ! deraadt 972: ferrout("pr: page width too small for %d columns\n", clcnt);
1.5 grr 973: return(1);
974: }
975: col = colwd + 1;
976:
977: /*
978: * line buffer
979: */
980: if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
981: mfail();
982: return(1);
983: }
984: if (offst) {
985: (void)memset(buf, (int)' ', offst);
986: (void)memset(hbuf, (int)' ', offst);
987: }
988:
989: pagecnt = 0;
990: lncnt = 0;
991: actf = clcnt;
992:
993: /*
994: * continue to loop while any file still has data
995: */
996: while (actf > 0) {
997:
998: /*
999: * loop on "form"
1000: */
1001: for (;;) {
1002:
1003: /*
1004: * loop by line
1005: */
1006: for (i = 0; i < lines; ++i) {
1007: ptbf = buf + offst;
1008: lstdat = ptbf;
1009: if (nmwd) {
1010: /*
1011: * add line number to line
1012: */
1013: addnum(ptbf, nmwd, ++lncnt);
1014: ptbf += nmwd;
1015: *ptbf++ = nmchar;
1016: }
1.1 deraadt 1017:
1.5 grr 1018: fproc = 0;
1.1 deraadt 1019: /*
1.5 grr 1020: * loop by column
1.1 deraadt 1021: */
1.5 grr 1022: for (j = 0; j < clcnt; ++j) {
1023: if (rc[j] == NORMAL ) {
1024: rc[j] = inln(fbuf[j], ptbf, colwd, &cnt, &cps, 1, &mor);
1025: if (cnt >= 0) {
1026: /*
1027: * process file data
1028: */
1029: ptbf += cnt;
1030: lstdat = ptbf;
1031: fproc++;
1032: } else
1033: cnt = 0;
1034:
1035: if (rc[j] == END) {
1036: /*
1037: * EOF close file
1038: */
1039: if (fbuf[j] != stdin)
1040: (void)fclose(fbuf[j]);
1041: --actf;
1042: }
1043: } else
1044: cnt = 0;
1045:
1046: /*
1047: * if last ACTIVE column, done with line
1048: */
1049: if (fproc >= actf)
1050: break;
1.1 deraadt 1051:
1.5 grr 1052: /*
1053: * pad to end of column
1054: */
1055: if (sflag) {
1056: *ptbf++ = schar;
1057: } else {
1058: if (cnt >= 0)
1059: pln = col - cnt;
1060: else
1061: pln = col;
1062: if (pln > 0) {
1063: (void)memset(ptbf, (int)' ', pln);
1064: ptbf += pln;
1.1 deraadt 1065: }
1.5 grr 1066: }
1067: }
1.1 deraadt 1068:
1.5 grr 1069: /*
1070: * if there was anything to do, print it
1071: */
1072: if (fproc != 0) {
1073: if (!i && prhead(hbuf, fname, ++pagecnt))
1074: return(1);
1.1 deraadt 1075:
1.5 grr 1076: /*
1077: * output line
1078: */
1079: if (otln(buf, lstdat-buf, &ips, &ops, 0))
1080: return(1);
1081: } else
1082: break;
1083: }
1.1 deraadt 1084:
1.5 grr 1085: /*
1086: * pad to end of page
1087: */
1088: if (prtail(lines - i, 0))
1089: return(1);
1.1 deraadt 1090:
1.5 grr 1091: for (j = 0; j < clcnt; ++j)
1092: if (rc[j] != END)
1093: rc[j] = NORMAL;
1.1 deraadt 1094:
1.5 grr 1095: if (actf <= 0)
1096: break;
1.1 deraadt 1097: }
1.5 grr 1098: if (actf <= 0)
1099: break;
1100: }
1101: if (eoptind < argc)
1102: return(1);
1103: return(0);
1.1 deraadt 1104: }
1105:
1106: /*
1.5 grr 1107: * inln(): input a line of data (unlimited length lines supported)
1108: * Input is optionally expanded to spaces
1109: * Returns 0 if normal LF, FORM on Formfeed, and END on EOF
1.1 deraadt 1110: *
1.5 grr 1111: * inf: file
1112: * buf: buffer
1113: * lim: buffer length
1114: * cnt: line length or -1 if no line (EOF for example)
1115: * cps: column positon 1st char in buffer (large line support)
1116: * trnc: throw away data more than lim up to \n
1117: * mor: set if more data in line (not truncated)
1.1 deraadt 1118: */
1119: int
1.5 grr 1120: inln(inf, buf, lim, cnt, cps, trnc, mor)
1121: FILE *inf;
1122: char *buf;
1123: register int lim;
1124: int *cnt;
1125: int *cps;
1126: int trnc;
1127: int *mor;
1.1 deraadt 1128: {
1.5 grr 1129: register int col;
1130: register int gap = ingap;
1.6 millert 1131: register int ch = -1;
1.5 grr 1132: register char *ptbuf;
1133: register int chk = (int)inchar;
1134:
1135: ptbuf = buf;
1136:
1137: if (gap) {
1138: /*
1139: * expanding input option
1140: */
1141: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1142: /*
1143: * is this the input "tab" char
1144: */
1145: if (ch == chk) {
1.1 deraadt 1146: /*
1.5 grr 1147: * expand to number of spaces
1.1 deraadt 1148: */
1.5 grr 1149: col = (ptbuf - buf) + *cps;
1150: col = gap - (col % gap);
1.1 deraadt 1151:
1152: /*
1.5 grr 1153: * if more than this line, push back
1.1 deraadt 1154: */
1.5 grr 1155: if ((col > lim) && (ungetc(ch, inf) == EOF)) {
1156: *cnt = -1;
1157: return(END); /* shouldn't happen */
1.1 deraadt 1158: }
1.5 grr 1159:
1.1 deraadt 1160: /*
1.5 grr 1161: * expand to spaces
1.1 deraadt 1162: */
1.5 grr 1163: while ((--col >= 0) && (--lim >= 0))
1164: *ptbuf++ = ' ';
1165: continue;
1166: }
1167: if (ch == '\n' || inform && ch == INFF)
1168: break;
1169: *ptbuf++ = ch;
1170: }
1171: } else {
1172: /*
1173: * no expansion
1174: */
1175: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1176: if (ch == '\n' || inform && ch == INFF)
1177: break;
1178: *ptbuf++ = ch;
1.1 deraadt 1179: }
1.5 grr 1180: }
1181: col = ptbuf - buf;
1182: if (ch == EOF) {
1183: *mor = 0;
1184: *cps = 0;
1185: *cnt = col ? col : -1;
1186: return(END);
1187: }
1188: if (inform && ch == INFF) {
1189: *mor = 0;
1190: *cps = 0;
1191: *cnt = col;
1192: return(FORM);
1193: }
1194: if (ch == '\n') {
1195: /*
1196: * entire line processed
1197: */
1198: *mor = 0;
1199: *cps = 0;
1200: *cnt = col;
1201: return(NORMAL);
1202: }
1203:
1204: /*
1205: * line was larger than limit
1206: */
1207: if (trnc) {
1.1 deraadt 1208: /*
1.5 grr 1209: * throw away rest of line
1.1 deraadt 1210: */
1.5 grr 1211: while ((ch = getc(inf)) != EOF) {
1212: if (ch == '\n')
1213: break;
1.1 deraadt 1214: }
1.5 grr 1215: *cps = 0;
1216: *mor = 0;
1217: } else {
1218: /*
1219: * save column offset if not truncated
1220: */
1221: *cps += col;
1222: *mor = 1;
1223: }
1.1 deraadt 1224:
1.5 grr 1225: *cnt = col;
1226: return(NORMAL);
1.1 deraadt 1227: }
1228:
1229: /*
1.5 grr 1230: * otln(): output a line of data. (Supports unlimited length lines)
1231: * output is optionally contracted to tabs
1.1 deraadt 1232: *
1.5 grr 1233: * buf: output buffer with data
1234: * cnt: number of chars of valid data in buf
1235: * svips: buffer input column position (for large lines)
1236: * svops: buffer output column position (for large lines)
1237: * mor: output line not complete in this buf; more data to come.
1238: * 1 is more, 0 is complete, -1 is no \n's
1.1 deraadt 1239: */
1240: int
1241: otln(buf, cnt, svips, svops, mor)
1.5 grr 1242: register char *buf;
1243: int cnt;
1244: int *svops;
1245: int *svips;
1246: int mor;
1.1 deraadt 1247: {
1.5 grr 1248: register int ops; /* last col output */
1249: register int ips; /* last col in buf examined */
1250: register int gap = ogap;
1251: register int tbps;
1252: register char *endbuf;
1253:
1254: /* skipping is only changed at header time not mid-line! */
1255: if (skipping)
1256: return (0);
1257:
1258: if (ogap) {
1259: /*
1260: * contracting on output
1261: */
1262: endbuf = buf + cnt;
1263: ops = *svops;
1264: ips = *svips;
1265: while (buf < endbuf) {
1266: /*
1267: * count number of spaces and ochar in buffer
1268: */
1269: if (*buf == ' ') {
1270: ++ips;
1271: ++buf;
1272: continue;
1273: }
1274:
1275: /*
1276: * simulate ochar processing
1277: */
1278: if (*buf == ochar) {
1279: ips += gap - (ips % gap);
1280: ++buf;
1281: continue;
1282: }
1283:
1284: /*
1285: * got a non space char; contract out spaces
1286: */
1287: while (ops < ips) {
1.1 deraadt 1288: /*
1.10 pvalchev 1289: * use one space if necessary
1290: */
1291: if (ips - ops == 1) {
1292: putchar(' ');
1293: break;
1294: }
1295: /*
1.5 grr 1296: * use as many ochar as will fit
1.1 deraadt 1297: */
1.5 grr 1298: if ((tbps = ops + gap - (ops % gap)) > ips)
1299: break;
1300: if (putchar(ochar) == EOF) {
1301: pfail();
1302: return(1);
1.1 deraadt 1303: }
1.5 grr 1304: ops = tbps;
1305: }
1.1 deraadt 1306:
1.5 grr 1307: while (ops < ips) {
1.1 deraadt 1308: /*
1.5 grr 1309: * finish off with spaces
1.1 deraadt 1310: */
1.5 grr 1311: if (putchar(' ') == EOF) {
1312: pfail();
1313: return(1);
1314: }
1315: ++ops;
1316: }
1317:
1318: /*
1319: * output non space char
1320: */
1321: if (putchar(*buf++) == EOF) {
1.1 deraadt 1322: pfail();
1323: return(1);
1.5 grr 1324: }
1325: ++ips;
1326: ++ops;
1327: }
1328:
1329: if (mor > 0) {
1330: /*
1331: * if incomplete line, save position counts
1332: */
1333: *svops = ops;
1334: *svips = ips;
1335: return(0);
1.1 deraadt 1336: }
1.5 grr 1337:
1338: if (mor < 0) {
1339: while (ops < ips) {
1340: /*
1.10 pvalchev 1341: * use one space if necessary
1342: */
1343: if (ips - ops == 1) {
1344: putchar(' ');
1345: break;
1346: }
1347: /*
1.5 grr 1348: * use as many ochar as will fit
1349: */
1350: if ((tbps = ops + gap - (ops % gap)) > ips)
1351: break;
1352: if (putchar(ochar) == EOF) {
1353: pfail();
1354: return(1);
1355: }
1356: ops = tbps;
1357: }
1.10 pvalchev 1358:
1.5 grr 1359: while (ops < ips) {
1360: /*
1361: * finish off with spaces
1362: */
1363: if (putchar(' ') == EOF) {
1364: pfail();
1365: return(1);
1366: }
1367: ++ops;
1368: }
1369: return(0);
1370: }
1371: } else {
1372: /*
1373: * output is not contracted
1374: */
1375: if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1376: pfail();
1377: return(1);
1378: }
1379: if (mor != 0)
1380: return(0);
1381: }
1382:
1383: /*
1384: * process line end and double space as required
1385: */
1386: if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1387: pfail();
1388: return(1);
1389: }
1390: return(0);
1.1 deraadt 1391: }
1392:
1.5 grr 1393: #ifdef notused
1.1 deraadt 1394: /*
1.5 grr 1395: * inskip(): skip over pgcnt pages with lncnt lines per page
1396: * file is closed at EOF (if not stdin).
1.1 deraadt 1397: *
1.5 grr 1398: * inf FILE * to read from
1399: * pgcnt number of pages to skip
1400: * lncnt number of lines per page
1.1 deraadt 1401: */
1402: int
1403: inskip(inf, pgcnt, lncnt)
1.5 grr 1404: FILE *inf;
1405: register int pgcnt;
1406: register int lncnt;
1.1 deraadt 1407: {
1.5 grr 1408: register int c;
1409: register int cnt;
1.1 deraadt 1410:
1.5 grr 1411: while(--pgcnt > 0) {
1412: cnt = lncnt;
1413: while ((c = getc(inf)) != EOF) {
1414: if ((c == '\n') && (--cnt == 0))
1415: break;
1416: }
1417: if (c == EOF) {
1418: if (inf != stdin)
1419: (void)fclose(inf);
1420: return(1);
1.1 deraadt 1421: }
1.5 grr 1422: }
1423: return(0);
1.1 deraadt 1424: }
1.5 grr 1425: #endif
1.1 deraadt 1426:
1427: /*
1.5 grr 1428: * nxtfile: returns a FILE * to next file in arg list and sets the
1429: * time field for this file (or current date).
1.1 deraadt 1430: *
1.5 grr 1431: * buf array to store proper date for the header.
1432: * dt if set skips the date processing (used with -m)
1.1 deraadt 1433: */
1434: FILE *
1435: nxtfile(argc, argv, fname, buf, dt)
1.5 grr 1436: int argc;
1437: char **argv;
1438: char **fname;
1439: char *buf;
1440: int dt;
1.1 deraadt 1441: {
1.5 grr 1442: FILE *inf = NULL;
1443: struct timeval tv;
1444: struct timezone tz;
1445: struct tm *timeptr = NULL;
1446: struct stat statbuf;
1447: time_t curtime;
1448: static int twice = -1;
1449:
1450: ++twice;
1451: if (eoptind >= argc) {
1452: /*
1453: * no file listed; default, use standard input
1454: */
1455: if (twice)
1456: return(NULL);
1457: clearerr(stdin);
1458: inf = stdin;
1459: if (header != NULL)
1460: *fname = header;
1461: else
1462: *fname = FNAME;
1463: if (nohead)
1464: return(inf);
1465: if (gettimeofday(&tv, &tz) < 0) {
1466: ++errcnt;
1.12 ! deraadt 1467: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1468: strerror(errno));
1469: eoptind = argc - 1;
1470: return(NULL);
1471: }
1472: curtime = tv.tv_sec;
1473: timeptr = localtime(&curtime);
1474: }
1475: for (; eoptind < argc; ++eoptind) {
1476: if (strcmp(argv[eoptind], "-") == 0) {
1477: /*
1478: * process a "-" for filename
1479: */
1480: clearerr(stdin);
1481: inf = stdin;
1482: if (header != NULL)
1483: *fname = header;
1484: else
1485: *fname = FNAME;
1486: ++eoptind;
1487: if (nohead || (dt && twice))
1488: return(inf);
1489: if (gettimeofday(&tv, &tz) < 0) {
1490: ++errcnt;
1.12 ! deraadt 1491: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1492: strerror(errno));
1493: return(NULL);
1494: }
1495: curtime = tv.tv_sec;
1496: timeptr = localtime(&curtime);
1497: } else {
1498: /*
1499: * normal file processing
1500: */
1501: if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1502: ++errcnt;
1503: if (nodiag)
1504: continue;
1.12 ! deraadt 1505: ferrout("pr: Cannot open %s, %s\n",
1.5 grr 1506: argv[eoptind], strerror(errno));
1507: continue;
1508: }
1509: if (header != NULL)
1510: *fname = header;
1511: else if (dt)
1512: *fname = FNAME;
1513: else
1514: *fname = argv[eoptind];
1515: ++eoptind;
1516: if (nohead || (dt && twice))
1517: return(inf);
1.1 deraadt 1518:
1.5 grr 1519: if (dt) {
1.1 deraadt 1520: if (gettimeofday(&tv, &tz) < 0) {
1.5 grr 1521: ++errcnt;
1.12 ! deraadt 1522: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1523: strerror(errno));
1524: return(NULL);
1.1 deraadt 1525: }
1526: curtime = tv.tv_sec;
1527: timeptr = localtime(&curtime);
1.5 grr 1528: } else {
1529: if (fstat(fileno(inf), &statbuf) < 0) {
1530: ++errcnt;
1531: (void)fclose(inf);
1.12 ! deraadt 1532: ferrout("pr: Cannot stat %s, %s\n",
1.5 grr 1533: argv[eoptind], strerror(errno));
1534: return(NULL);
1535: }
1536: timeptr = localtime(&(statbuf.st_mtime));
1537: }
1538: }
1539: break;
1540: }
1541: if (inf == NULL)
1542: return(NULL);
1543:
1544: /*
1545: * set up time field used in header
1546: */
1547: if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1548: ++errcnt;
1549: if (inf != stdin)
1550: (void)fclose(inf);
1.12 ! deraadt 1551: ferrout("pr: time conversion failed\n");
1.5 grr 1552: return(NULL);
1553: }
1554: return(inf);
1.1 deraadt 1555: }
1556:
1557: /*
1.5 grr 1558: * addnum(): adds the line number to the column
1559: * Truncates from the front or pads with spaces as required.
1560: * Numbers are right justified.
1.1 deraadt 1561: *
1.5 grr 1562: * buf buffer to store the number
1563: * wdth width of buffer to fill
1564: * line line number
1.1 deraadt 1565: *
1.5 grr 1566: * NOTE: numbers occupy part of the column. The posix
1567: * spec does not specify if -i processing should or should not
1568: * occur on number padding. The spec does say it occupies
1569: * part of the column. The usage of addnum currently treats
1570: * numbers as part of the column so spaces may be replaced.
1.1 deraadt 1571: */
1572: void
1573: addnum(buf, wdth, line)
1.5 grr 1574: register char *buf;
1575: register int wdth;
1576: register int line;
1.1 deraadt 1577: {
1.5 grr 1578: register char *pt = buf + wdth;
1.1 deraadt 1579:
1.5 grr 1580: do {
1581: *--pt = digs[line % 10];
1582: line /= 10;
1583: } while (line && (pt > buf));
1584:
1585: /*
1586: * pad with space as required
1587: */
1588: while (pt > buf)
1589: *--pt = ' ';
1.1 deraadt 1590: }
1591:
1592: /*
1.5 grr 1593: * prhead(): prints the top of page header
1.1 deraadt 1594: *
1.5 grr 1595: * buf buffer with time field (and offset)
1596: * cnt number of chars in buf
1597: * fname fname field for header
1598: * pagcnt page number
1599: *
1600: * prhead() should be used carefully, we don't want to print out headers
1601: * for null input files or orphan headers at the end of files, and also
1602: * trailer processing is typically conditional on whether you've called
1603: * prhead() at least once for a file and incremented pagecnt.. Exactly
1604: * how to determine whether to print a header is a little different in
1605: * the context each output mode, but we let the caller figure that out.
1.1 deraadt 1606: */
1607: int
1608: prhead(buf, fname, pagcnt)
1.5 grr 1609: char *buf;
1610: char *fname;
1611: int pagcnt;
1.1 deraadt 1612: {
1.5 grr 1613: int ips = 0;
1614: int ops = 0;
1615:
1616: beheaded = 1;
1.1 deraadt 1617:
1.5 grr 1618: if (skipping && pagcnt >= pgnm)
1619: skipping = 0;
1620:
1621: if (nohead || skipping)
1622: return (0);
1623:
1624: if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1625: pfail();
1626: return(1);
1627: }
1628: /*
1629: * posix is not clear if the header is subject to line length
1630: * restrictions. The specification for header line format
1631: * in the spec clearly does not limit length. No pr currently
1632: * restricts header length. However if we need to truncate in
1633: * an reasonable way, adjust the length of the printf by
1634: * changing HDFMT to allow a length max as an arguement printf.
1635: * buf (which contains the offset spaces and time field could
1636: * also be trimmed
1637: *
1638: * note only the offset (if any) is processed for tab expansion
1639: */
1640: if (offst && otln(buf, offst, &ips, &ops, -1))
1641: return(1);
1642: (void)printf(HDFMT,buf+offst, fname, pagcnt);
1643: return(0);
1.1 deraadt 1644: }
1645:
1646: /*
1.5 grr 1647: * prtail(): pad page with empty lines (if required) and print page trailer
1648: * if requested
1649: *
1650: * cnt number of lines of padding needed
1651: * incomp was a '\n' missing from last line output
1.1 deraadt 1652: *
1.5 grr 1653: * prtail() can now be invoked unconditionally, with the notion that if
1654: * we haven't printed a hearder, these no need for a trailer
1.1 deraadt 1655: */
1656: int
1657: prtail(cnt, incomp)
1.5 grr 1658: register int cnt;
1659: int incomp;
1.1 deraadt 1660: {
1.5 grr 1661: /*
1662: * if were's skipping to page N or haven't put out anything yet just exit
1663: */
1664: if (skipping || beheaded == 0)
1665: return (0);
1666: beheaded = 0;
1667:
1668: /*
1669: * if noheaders, only terminate an incomplete last line
1670: */
1671: if (nohead) {
1672:
1673: if (incomp) {
1674: if (dspace)
1675: if (putchar('\n') == EOF) {
1676: pfail();
1677: return(1);
1.3 imp 1678: }
1.5 grr 1679: if (putchar('\n') == EOF) {
1680: pfail();
1681: return(1);
1682: }
1.1 deraadt 1683: }
1.5 grr 1684: /*
1685: * but honor the formfeed request
1686: */
1687: if (formfeed)
1688: if (putchar(OUTFF) == EOF) {
1689: pfail();
1690: return(1);
1691: }
1692:
1693: } else {
1.1 deraadt 1694:
1695: /*
1696: * if double space output two \n
1.5 grr 1697: *
1698: * XXX this all seems bogus, why are we doing it here???
1699: * page length is in terms of output lines and only the input is
1700: * supposed to be double spaced... otln() users should be doing
1701: * something like linect+=(dspace ? 2:1).
1.1 deraadt 1702: */
1703: if (dspace)
1.5 grr 1704: cnt *= 2;
1.1 deraadt 1705:
1706: /*
1707: * if an odd number of lines per page, add an extra \n
1708: */
1709: if (addone)
1.5 grr 1710: ++cnt;
1.1 deraadt 1711:
1712: /*
1.5 grr 1713: * either put out a form-feed or pad page with blanks
1.1 deraadt 1714: */
1715: if (formfeed) {
1.5 grr 1716: if (incomp)
1717: if (putchar('\n') == EOF) {
1718: pfail();
1719: return(1);
1.1 deraadt 1720: }
1.5 grr 1721: if (putchar(OUTFF) == EOF) {
1722: pfail();
1723: return(1);
1724: }
1725:
1726: } else {
1727:
1728: if (incomp)
1729: cnt++;
1730:
1731: cnt += TAILLEN;
1732: while (--cnt >= 0) {
1.1 deraadt 1733: if (putchar('\n') == EOF) {
1.5 grr 1734: pfail();
1735: return(1);
1.1 deraadt 1736: }
1.5 grr 1737: }
1.1 deraadt 1738: }
1.5 grr 1739: }
1740:
1741: return(0);
1.1 deraadt 1742: }
1743:
1744: /*
1.5 grr 1745: * terminate(): when a SIGINT is recvd
1.1 deraadt 1746: */
1747: void
1748: terminate(which_sig)
1.5 grr 1749: int which_sig;
1.1 deraadt 1750: {
1.12 ! deraadt 1751: flsh_errs();
1.11 deraadt 1752: _exit(1);
1.1 deraadt 1753: }
1754:
1755: void
1756: mfail()
1757: {
1.12 ! deraadt 1758: ferrout("pr: memory allocation failed\n");
1.1 deraadt 1759: }
1760:
1761: void
1762: pfail()
1763: {
1.12 ! deraadt 1764: ferrout("pr: write failure, %s\n", strerror(errno));
1.1 deraadt 1765: }
1766:
1767: void
1768: usage()
1769: {
1.12 ! deraadt 1770: ferrout(
! 1771: "usage: pr [+page] [-col] [-adfFmrt] [-e[ch][gap]] [-h header]\n");
! 1772: ferrout(
! 1773: " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n");
! 1774: ferrout(
1.9 deraadt 1775: " [-s[ch]] [-w width] [-] [file ...]\n", ferr);
1.1 deraadt 1776: }
1777:
1778: /*
1.5 grr 1779: * setup: Validate command args, initialize and perform sanity
1780: * checks on options
1.1 deraadt 1781: */
1782: int
1783: setup(argc, argv)
1.5 grr 1784: register int argc;
1785: register char **argv;
1.1 deraadt 1786: {
1.5 grr 1787: register int c;
1788: int eflag = 0;
1789: int iflag = 0;
1790: int wflag = 0;
1791: int cflag = 0;
1792:
1.12 ! deraadt 1793: if (isatty(fileno(stdout)))
! 1794: ferr = 1;
! 1795:
1.6 millert 1796: while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) {
1.5 grr 1797: switch (c) {
1798: case '+':
1799: if ((pgnm = atoi(eoptarg)) < 1) {
1.12 ! deraadt 1800: ferrout("pr: +page number must be 1 or more\n");
1.5 grr 1801: return(1);
1802: }
1803: ++skipping;
1804: break;
1805: case '-':
1806: if ((clcnt = atoi(eoptarg)) < 1) {
1.12 ! deraadt 1807: ferrout("pr: -columns must be 1 or more\n");
1.5 grr 1808: return(1);
1809: }
1810: if (clcnt > 1)
1811: ++cflag;
1812: break;
1813: case 'a':
1814: ++across;
1815: break;
1816: case 'd':
1817: ++dspace;
1818: break;
1819: case 'e':
1820: ++eflag;
1821: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1822: inchar = *eoptarg++;
1823: else
1824: inchar = INCHAR;
1825: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1826: if ((ingap = atoi(eoptarg)) < 0) {
1.12 ! deraadt 1827: ferrout("pr: -e gap must be 0 or more\n");
1.5 grr 1828: return(1);
1829: }
1830: if (ingap == 0)
1831: ingap = INGAP;
1832: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 ! deraadt 1833: ferrout("pr: invalid value for -e %s\n", eoptarg);
1.5 grr 1834: return(1);
1835: } else
1836: ingap = INGAP;
1837: break;
1838: case 'f':
1839: case 'F':
1840: ++formfeed;
1841: break;
1842: case 'h':
1843: header = eoptarg;
1844: break;
1845: case 'i':
1846: ++iflag;
1847: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1848: ochar = *eoptarg++;
1849: else
1850: ochar = OCHAR;
1851: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1852: if ((ogap = atoi(eoptarg)) < 0) {
1.12 ! deraadt 1853: ferrout("pr: -i gap must be 0 or more\n");
1.5 grr 1854: return(1);
1855: }
1856: if (ogap == 0)
1857: ogap = OGAP;
1858: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 ! deraadt 1859: ferrout("pr: invalid value for -i %s\n", eoptarg);
1.5 grr 1860: return(1);
1861: } else
1862: ogap = OGAP;
1863: break;
1864: case 'l':
1865: if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
1.12 ! deraadt 1866: ferrout("pr: Number of lines must be 1 or more\n");
1.5 grr 1867: return(1);
1868: }
1869: break;
1870: case 'm':
1871: ++merge;
1872: break;
1873: case 'n':
1874: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1875: nmchar = *eoptarg++;
1876: else
1877: nmchar = NMCHAR;
1878: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1879: if ((nmwd = atoi(eoptarg)) < 1) {
1.12 ! deraadt 1880: ferrout("pr: -n width must be 1 or more\n");
1.5 grr 1881: return(1);
1.1 deraadt 1882: }
1.5 grr 1883: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 ! deraadt 1884: ferrout("pr: invalid value for -n %s\n", eoptarg);
1.5 grr 1885: return(1);
1886: } else
1887: nmwd = NMWD;
1888: break;
1889: case 'o':
1890: if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){
1.12 ! deraadt 1891: ferrout("pr: -o offset must be 1 or more\n");
1.5 grr 1892: return(1);
1893: }
1894: break;
1895: case 'r':
1896: ++nodiag;
1897: break;
1898: case 's':
1899: ++sflag;
1900: if (eoptarg == NULL)
1901: schar = SCHAR;
1.7 deraadt 1902: else {
1.5 grr 1903: schar = *eoptarg++;
1.7 deraadt 1904: if (*eoptarg != '\0') {
1.12 ! deraadt 1905: ferrout("pr: invalid value for -s %s\n", eoptarg);
1.7 deraadt 1906: return(1);
1907: }
1.5 grr 1908: }
1909: break;
1910: case 't':
1911: ++nohead;
1912: break;
1913: case 'w':
1914: ++wflag;
1915: if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
1.12 ! deraadt 1916: ferrout("pr: -w width must be 1 or more \n");
1.5 grr 1917: return(1);
1918: }
1919: break;
1920: case '?':
1921: default:
1922: return(1);
1923: }
1924: }
1925:
1926: /*
1927: * default and sanity checks
1928: */
1929: inform++;
1930:
1931: if (!clcnt) {
1932: if (merge) {
1933: if ((clcnt = argc - eoptind) <= 1) {
1934: clcnt = CLCNT;
1935: #ifdef stupid
1936: merge = 0;
1937: #endif
1938: }
1.1 deraadt 1939: } else
1.5 grr 1940: clcnt = CLCNT;
1941: }
1942: if (across) {
1943: if (clcnt == 1) {
1.12 ! deraadt 1944: ferrout("pr: -a flag requires multiple columns\n");
1.5 grr 1945: return(1);
1946: }
1947: if (merge) {
1.12 ! deraadt 1948: ferrout("pr: -m cannot be used with -a\n");
1.5 grr 1949: return(1);
1950: }
1951: }
1952: if (!wflag) {
1953: if (sflag)
1954: pgwd = SPGWD;
1955: else
1956: pgwd = PGWD;
1957: }
1958: if (cflag || merge) {
1959: if (!eflag) {
1960: inchar = INCHAR;
1961: ingap = INGAP;
1962: }
1963: if (!iflag) {
1964: ochar = OCHAR;
1965: ogap = OGAP;
1966: }
1967: }
1968: if (cflag) {
1969: if (merge) {
1.12 ! deraadt 1970: ferrout("pr: -m cannot be used with multiple columns\n");
1.5 grr 1971: return(1);
1.1 deraadt 1972: }
1.5 grr 1973: if (nmwd) {
1974: colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1975: pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1976: } else {
1977: colwd = (pgwd + 1 - clcnt)/clcnt;
1978: pgwd = ((colwd + 1) * clcnt) - 1;
1.1 deraadt 1979: }
1.5 grr 1980: if (colwd < 1) {
1.12 ! deraadt 1981: ferrout("pr: page width is too small for %d columns\n",clcnt);
1.5 grr 1982: return(1);
1983: }
1984: }
1985: if (!lines)
1986: lines = LINES;
1987:
1988: /*
1989: * make sure long enough for headers. if not disable
1990: */
1991: if (lines <= HEADLEN + TAILLEN)
1992: ++nohead;
1993: else if (!nohead)
1994: lines -= HEADLEN + TAILLEN;
1995:
1996: /*
1997: * adjust for double space on odd length pages
1998: */
1999: if (dspace) {
2000: if (lines == 1)
2001: dspace = 0;
2002: else {
2003: if (lines & 1)
2004: ++addone;
2005: lines /= 2;
2006: }
2007: }
2008:
2009: if ((timefrmt = getenv("LC_TIME")) == NULL)
2010: timefrmt = TIMEFMT;
2011: return(0);
1.1 deraadt 2012: }