Annotation of src/usr.bin/pr/pr.c, Revision 1.15
1.15 ! deraadt 1: /* $OpenBSD: pr.c,v 1.14 2001/11/29 18:21:23 mpech 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.15 ! deraadt 48: static char *rcsid = "$OpenBSD: pr.c,v 1.14 2001/11/29 18:21:23 mpech 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.13 mpech 191: int off;
192: int lrgln;
193: int linecnt;
194: int num;
1.5 grr 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.13 mpech 354: char *ptbf;
355: char **lstdat;
356: int i;
357: int j;
358: int pln;
359: int *indy;
1.5 grr 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.13 mpech 682: char *ptbf;
683: int pln;
684: char *lstdat;
685: int col = colwd + 1;
686: int j;
687: int i;
1.5 grr 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: }
1.14 mpech 875: va_end(ap);
1.12 deraadt 876: }
877:
1.1 deraadt 878: /*
1.5 grr 879: * mulfile: print files with more than one column of output and
880: * more than one file concurrently
1.1 deraadt 881: */
882: int
883: mulfile(argc, argv)
1.5 grr 884: int argc;
885: char *argv[];
1.1 deraadt 886: {
1.13 mpech 887: char *ptbf;
888: int j;
889: int pln;
1.5 grr 890: int *rc;
891: int cnt;
1.13 mpech 892: char *lstdat;
893: int i;
1.5 grr 894: FILE **fbuf;
895: int actf;
896: int lncnt;
897: int col;
898: int pagecnt;
899: int fproc;
900: char *buf;
901: char *hbuf;
902: char *ohbuf;
903: char *fname;
904: int ips = 0;
905: int cps = 0;
906: int ops = 0;
907: int mor = 0;
908:
909: /*
910: * array of FILE *, one for each operand
911: */
912: if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) {
913: mfail();
914: return(1);
915: }
916:
917: /*
918: * array of int *, one for each operand
919: */
920: if ((rc = (int *)malloc((unsigned)clcnt*sizeof(int))) == NULL) {
921: mfail();
922: return(1);
923: }
924:
925: /*
926: * page header
927: */
928: if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) {
929: mfail();
930: return(1);
931: }
932: ohbuf = hbuf + offst;
933:
934: /*
935: * do not know how many columns yet. The number of operands provide an
936: * upper bound on the number of columns. We use the number of files
937: * we can open successfully to set the number of columns. The operation
938: * of the merge operation (-m) in relation to unsuccesful file opens
939: * is unspecified by posix.
940: *
941: * XXX - this seems moderately bogus, you'd think that specifying
942: * "pr -2 a b c d" would run though all the files in pairs, but
943: * the existing code says up two files, or fewer if one is bogus.
944: * fixing it would require modifying the looping structure, so be it.
945: */
946: j = 0;
947: while (j < clcnt) {
948: if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) != NULL) {
949: rc[j] = NORMAL;
950: j++;
951: }
952: }
953:
954: /*
955: * if no files, exit
956: */
957: if (j)
1.1 deraadt 958: clcnt = j;
1.5 grr 959: else
960: return(1);
1.1 deraadt 961:
1.5 grr 962: /*
963: * calculate page boundries based on open file count
964: */
965: if (nmwd) {
966: colwd = (pgwd - clcnt - nmwd)/clcnt;
967: pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
968: } else {
969: colwd = (pgwd + 1 - clcnt)/clcnt;
970: pgwd = ((colwd + 1) * clcnt) - 1;
971: }
972: if (colwd < 1) {
1.12 deraadt 973: ferrout("pr: page width too small for %d columns\n", clcnt);
1.5 grr 974: return(1);
975: }
976: col = colwd + 1;
977:
978: /*
979: * line buffer
980: */
981: if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) {
982: mfail();
983: return(1);
984: }
985: if (offst) {
986: (void)memset(buf, (int)' ', offst);
987: (void)memset(hbuf, (int)' ', offst);
988: }
989:
990: pagecnt = 0;
991: lncnt = 0;
992: actf = clcnt;
993:
994: /*
995: * continue to loop while any file still has data
996: */
997: while (actf > 0) {
998:
999: /*
1000: * loop on "form"
1001: */
1002: for (;;) {
1003:
1004: /*
1005: * loop by line
1006: */
1007: for (i = 0; i < lines; ++i) {
1008: ptbf = buf + offst;
1009: lstdat = ptbf;
1010: if (nmwd) {
1011: /*
1012: * add line number to line
1013: */
1014: addnum(ptbf, nmwd, ++lncnt);
1015: ptbf += nmwd;
1016: *ptbf++ = nmchar;
1017: }
1.1 deraadt 1018:
1.5 grr 1019: fproc = 0;
1.1 deraadt 1020: /*
1.5 grr 1021: * loop by column
1.1 deraadt 1022: */
1.5 grr 1023: for (j = 0; j < clcnt; ++j) {
1024: if (rc[j] == NORMAL ) {
1025: rc[j] = inln(fbuf[j], ptbf, colwd, &cnt, &cps, 1, &mor);
1026: if (cnt >= 0) {
1027: /*
1028: * process file data
1029: */
1030: ptbf += cnt;
1031: lstdat = ptbf;
1032: fproc++;
1033: } else
1034: cnt = 0;
1035:
1036: if (rc[j] == END) {
1037: /*
1038: * EOF close file
1039: */
1040: if (fbuf[j] != stdin)
1041: (void)fclose(fbuf[j]);
1042: --actf;
1043: }
1044: } else
1045: cnt = 0;
1046:
1047: /*
1048: * if last ACTIVE column, done with line
1049: */
1050: if (fproc >= actf)
1051: break;
1.1 deraadt 1052:
1.5 grr 1053: /*
1054: * pad to end of column
1055: */
1056: if (sflag) {
1057: *ptbf++ = schar;
1058: } else {
1059: if (cnt >= 0)
1060: pln = col - cnt;
1061: else
1062: pln = col;
1063: if (pln > 0) {
1064: (void)memset(ptbf, (int)' ', pln);
1065: ptbf += pln;
1.1 deraadt 1066: }
1.5 grr 1067: }
1068: }
1.1 deraadt 1069:
1.5 grr 1070: /*
1071: * if there was anything to do, print it
1072: */
1073: if (fproc != 0) {
1074: if (!i && prhead(hbuf, fname, ++pagecnt))
1075: return(1);
1.1 deraadt 1076:
1.5 grr 1077: /*
1078: * output line
1079: */
1080: if (otln(buf, lstdat-buf, &ips, &ops, 0))
1081: return(1);
1082: } else
1083: break;
1084: }
1.1 deraadt 1085:
1.5 grr 1086: /*
1087: * pad to end of page
1088: */
1089: if (prtail(lines - i, 0))
1090: return(1);
1.1 deraadt 1091:
1.5 grr 1092: for (j = 0; j < clcnt; ++j)
1093: if (rc[j] != END)
1094: rc[j] = NORMAL;
1.1 deraadt 1095:
1.5 grr 1096: if (actf <= 0)
1097: break;
1.1 deraadt 1098: }
1.5 grr 1099: if (actf <= 0)
1100: break;
1101: }
1102: if (eoptind < argc)
1103: return(1);
1104: return(0);
1.1 deraadt 1105: }
1106:
1107: /*
1.5 grr 1108: * inln(): input a line of data (unlimited length lines supported)
1109: * Input is optionally expanded to spaces
1110: * Returns 0 if normal LF, FORM on Formfeed, and END on EOF
1.1 deraadt 1111: *
1.5 grr 1112: * inf: file
1113: * buf: buffer
1114: * lim: buffer length
1115: * cnt: line length or -1 if no line (EOF for example)
1116: * cps: column positon 1st char in buffer (large line support)
1117: * trnc: throw away data more than lim up to \n
1118: * mor: set if more data in line (not truncated)
1.1 deraadt 1119: */
1120: int
1.5 grr 1121: inln(inf, buf, lim, cnt, cps, trnc, mor)
1122: FILE *inf;
1123: char *buf;
1.13 mpech 1124: int lim;
1.5 grr 1125: int *cnt;
1126: int *cps;
1127: int trnc;
1128: int *mor;
1.1 deraadt 1129: {
1.13 mpech 1130: int col;
1131: int gap = ingap;
1132: int ch = -1;
1133: char *ptbuf;
1134: int chk = (int)inchar;
1.5 grr 1135:
1136: ptbuf = buf;
1137:
1138: if (gap) {
1139: /*
1140: * expanding input option
1141: */
1142: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1143: /*
1144: * is this the input "tab" char
1145: */
1146: if (ch == chk) {
1.1 deraadt 1147: /*
1.5 grr 1148: * expand to number of spaces
1.1 deraadt 1149: */
1.5 grr 1150: col = (ptbuf - buf) + *cps;
1151: col = gap - (col % gap);
1.1 deraadt 1152:
1153: /*
1.5 grr 1154: * if more than this line, push back
1.1 deraadt 1155: */
1.5 grr 1156: if ((col > lim) && (ungetc(ch, inf) == EOF)) {
1157: *cnt = -1;
1158: return(END); /* shouldn't happen */
1.1 deraadt 1159: }
1.5 grr 1160:
1.1 deraadt 1161: /*
1.5 grr 1162: * expand to spaces
1.1 deraadt 1163: */
1.5 grr 1164: while ((--col >= 0) && (--lim >= 0))
1165: *ptbuf++ = ' ';
1166: continue;
1167: }
1168: if (ch == '\n' || inform && ch == INFF)
1169: break;
1170: *ptbuf++ = ch;
1171: }
1172: } else {
1173: /*
1174: * no expansion
1175: */
1176: while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1177: if (ch == '\n' || inform && ch == INFF)
1178: break;
1179: *ptbuf++ = ch;
1.1 deraadt 1180: }
1.5 grr 1181: }
1182: col = ptbuf - buf;
1183: if (ch == EOF) {
1184: *mor = 0;
1185: *cps = 0;
1186: *cnt = col ? col : -1;
1187: return(END);
1188: }
1189: if (inform && ch == INFF) {
1190: *mor = 0;
1191: *cps = 0;
1192: *cnt = col;
1193: return(FORM);
1194: }
1195: if (ch == '\n') {
1196: /*
1197: * entire line processed
1198: */
1199: *mor = 0;
1200: *cps = 0;
1201: *cnt = col;
1202: return(NORMAL);
1203: }
1204:
1205: /*
1206: * line was larger than limit
1207: */
1208: if (trnc) {
1.1 deraadt 1209: /*
1.5 grr 1210: * throw away rest of line
1.1 deraadt 1211: */
1.5 grr 1212: while ((ch = getc(inf)) != EOF) {
1213: if (ch == '\n')
1214: break;
1.1 deraadt 1215: }
1.5 grr 1216: *cps = 0;
1217: *mor = 0;
1218: } else {
1219: /*
1220: * save column offset if not truncated
1221: */
1222: *cps += col;
1223: *mor = 1;
1224: }
1.1 deraadt 1225:
1.5 grr 1226: *cnt = col;
1227: return(NORMAL);
1.1 deraadt 1228: }
1229:
1230: /*
1.5 grr 1231: * otln(): output a line of data. (Supports unlimited length lines)
1232: * output is optionally contracted to tabs
1.1 deraadt 1233: *
1.5 grr 1234: * buf: output buffer with data
1235: * cnt: number of chars of valid data in buf
1236: * svips: buffer input column position (for large lines)
1237: * svops: buffer output column position (for large lines)
1238: * mor: output line not complete in this buf; more data to come.
1239: * 1 is more, 0 is complete, -1 is no \n's
1.1 deraadt 1240: */
1241: int
1242: otln(buf, cnt, svips, svops, mor)
1.13 mpech 1243: char *buf;
1.5 grr 1244: int cnt;
1245: int *svops;
1246: int *svips;
1247: int mor;
1.1 deraadt 1248: {
1.13 mpech 1249: int ops; /* last col output */
1250: int ips; /* last col in buf examined */
1251: int gap = ogap;
1252: int tbps;
1253: char *endbuf;
1.5 grr 1254:
1255: /* skipping is only changed at header time not mid-line! */
1256: if (skipping)
1257: return (0);
1258:
1259: if (ogap) {
1260: /*
1261: * contracting on output
1262: */
1263: endbuf = buf + cnt;
1264: ops = *svops;
1265: ips = *svips;
1266: while (buf < endbuf) {
1267: /*
1268: * count number of spaces and ochar in buffer
1269: */
1270: if (*buf == ' ') {
1271: ++ips;
1272: ++buf;
1273: continue;
1274: }
1275:
1276: /*
1277: * simulate ochar processing
1278: */
1279: if (*buf == ochar) {
1280: ips += gap - (ips % gap);
1281: ++buf;
1282: continue;
1283: }
1284:
1285: /*
1286: * got a non space char; contract out spaces
1287: */
1288: while (ops < ips) {
1.1 deraadt 1289: /*
1.10 pvalchev 1290: * use one space if necessary
1291: */
1292: if (ips - ops == 1) {
1293: putchar(' ');
1294: break;
1295: }
1296: /*
1.5 grr 1297: * use as many ochar as will fit
1.1 deraadt 1298: */
1.5 grr 1299: if ((tbps = ops + gap - (ops % gap)) > ips)
1300: break;
1301: if (putchar(ochar) == EOF) {
1302: pfail();
1303: return(1);
1.1 deraadt 1304: }
1.5 grr 1305: ops = tbps;
1306: }
1.1 deraadt 1307:
1.5 grr 1308: while (ops < ips) {
1.1 deraadt 1309: /*
1.5 grr 1310: * finish off with spaces
1.1 deraadt 1311: */
1.5 grr 1312: if (putchar(' ') == EOF) {
1313: pfail();
1314: return(1);
1315: }
1316: ++ops;
1317: }
1318:
1319: /*
1320: * output non space char
1321: */
1322: if (putchar(*buf++) == EOF) {
1.1 deraadt 1323: pfail();
1324: return(1);
1.5 grr 1325: }
1326: ++ips;
1327: ++ops;
1328: }
1329:
1330: if (mor > 0) {
1331: /*
1332: * if incomplete line, save position counts
1333: */
1334: *svops = ops;
1335: *svips = ips;
1336: return(0);
1.1 deraadt 1337: }
1.5 grr 1338:
1339: if (mor < 0) {
1340: while (ops < ips) {
1341: /*
1.10 pvalchev 1342: * use one space if necessary
1343: */
1344: if (ips - ops == 1) {
1345: putchar(' ');
1346: break;
1347: }
1348: /*
1.5 grr 1349: * use as many ochar as will fit
1350: */
1351: if ((tbps = ops + gap - (ops % gap)) > ips)
1352: break;
1353: if (putchar(ochar) == EOF) {
1354: pfail();
1355: return(1);
1356: }
1357: ops = tbps;
1358: }
1.10 pvalchev 1359:
1.5 grr 1360: while (ops < ips) {
1361: /*
1362: * finish off with spaces
1363: */
1364: if (putchar(' ') == EOF) {
1365: pfail();
1366: return(1);
1367: }
1368: ++ops;
1369: }
1370: return(0);
1371: }
1372: } else {
1373: /*
1374: * output is not contracted
1375: */
1376: if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1377: pfail();
1378: return(1);
1379: }
1380: if (mor != 0)
1381: return(0);
1382: }
1383:
1384: /*
1385: * process line end and double space as required
1386: */
1387: if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1388: pfail();
1389: return(1);
1390: }
1391: return(0);
1.1 deraadt 1392: }
1393:
1.5 grr 1394: #ifdef notused
1.1 deraadt 1395: /*
1.5 grr 1396: * inskip(): skip over pgcnt pages with lncnt lines per page
1397: * file is closed at EOF (if not stdin).
1.1 deraadt 1398: *
1.5 grr 1399: * inf FILE * to read from
1400: * pgcnt number of pages to skip
1401: * lncnt number of lines per page
1.1 deraadt 1402: */
1403: int
1404: inskip(inf, pgcnt, lncnt)
1.5 grr 1405: FILE *inf;
1.13 mpech 1406: int pgcnt;
1407: int lncnt;
1.1 deraadt 1408: {
1.13 mpech 1409: int c;
1410: int cnt;
1.1 deraadt 1411:
1.5 grr 1412: while(--pgcnt > 0) {
1413: cnt = lncnt;
1414: while ((c = getc(inf)) != EOF) {
1415: if ((c == '\n') && (--cnt == 0))
1416: break;
1417: }
1418: if (c == EOF) {
1419: if (inf != stdin)
1420: (void)fclose(inf);
1421: return(1);
1.1 deraadt 1422: }
1.5 grr 1423: }
1424: return(0);
1.1 deraadt 1425: }
1.5 grr 1426: #endif
1.1 deraadt 1427:
1428: /*
1.5 grr 1429: * nxtfile: returns a FILE * to next file in arg list and sets the
1430: * time field for this file (or current date).
1.1 deraadt 1431: *
1.5 grr 1432: * buf array to store proper date for the header.
1433: * dt if set skips the date processing (used with -m)
1.1 deraadt 1434: */
1435: FILE *
1436: nxtfile(argc, argv, fname, buf, dt)
1.5 grr 1437: int argc;
1438: char **argv;
1439: char **fname;
1440: char *buf;
1441: int dt;
1.1 deraadt 1442: {
1.5 grr 1443: FILE *inf = NULL;
1444: struct timeval tv;
1445: struct timezone tz;
1446: struct tm *timeptr = NULL;
1447: struct stat statbuf;
1448: time_t curtime;
1449: static int twice = -1;
1450:
1451: ++twice;
1452: if (eoptind >= argc) {
1453: /*
1454: * no file listed; default, use standard input
1455: */
1456: if (twice)
1457: return(NULL);
1458: clearerr(stdin);
1459: inf = stdin;
1460: if (header != NULL)
1461: *fname = header;
1462: else
1463: *fname = FNAME;
1464: if (nohead)
1465: return(inf);
1466: if (gettimeofday(&tv, &tz) < 0) {
1467: ++errcnt;
1.12 deraadt 1468: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1469: strerror(errno));
1470: eoptind = argc - 1;
1471: return(NULL);
1472: }
1473: curtime = tv.tv_sec;
1474: timeptr = localtime(&curtime);
1475: }
1476: for (; eoptind < argc; ++eoptind) {
1477: if (strcmp(argv[eoptind], "-") == 0) {
1478: /*
1479: * process a "-" for filename
1480: */
1481: clearerr(stdin);
1482: inf = stdin;
1483: if (header != NULL)
1484: *fname = header;
1485: else
1486: *fname = FNAME;
1487: ++eoptind;
1488: if (nohead || (dt && twice))
1489: return(inf);
1490: if (gettimeofday(&tv, &tz) < 0) {
1491: ++errcnt;
1.12 deraadt 1492: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1493: strerror(errno));
1494: return(NULL);
1495: }
1496: curtime = tv.tv_sec;
1497: timeptr = localtime(&curtime);
1498: } else {
1499: /*
1500: * normal file processing
1501: */
1502: if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1503: ++errcnt;
1504: if (nodiag)
1505: continue;
1.12 deraadt 1506: ferrout("pr: Cannot open %s, %s\n",
1.5 grr 1507: argv[eoptind], strerror(errno));
1508: continue;
1509: }
1510: if (header != NULL)
1511: *fname = header;
1512: else if (dt)
1513: *fname = FNAME;
1514: else
1515: *fname = argv[eoptind];
1516: ++eoptind;
1517: if (nohead || (dt && twice))
1518: return(inf);
1.1 deraadt 1519:
1.5 grr 1520: if (dt) {
1.1 deraadt 1521: if (gettimeofday(&tv, &tz) < 0) {
1.5 grr 1522: ++errcnt;
1.12 deraadt 1523: ferrout("pr: cannot get time of day, %s\n",
1.5 grr 1524: strerror(errno));
1525: return(NULL);
1.1 deraadt 1526: }
1527: curtime = tv.tv_sec;
1528: timeptr = localtime(&curtime);
1.5 grr 1529: } else {
1530: if (fstat(fileno(inf), &statbuf) < 0) {
1531: ++errcnt;
1532: (void)fclose(inf);
1.12 deraadt 1533: ferrout("pr: Cannot stat %s, %s\n",
1.5 grr 1534: argv[eoptind], strerror(errno));
1535: return(NULL);
1536: }
1537: timeptr = localtime(&(statbuf.st_mtime));
1538: }
1539: }
1540: break;
1541: }
1542: if (inf == NULL)
1543: return(NULL);
1544:
1545: /*
1546: * set up time field used in header
1547: */
1548: if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1549: ++errcnt;
1550: if (inf != stdin)
1551: (void)fclose(inf);
1.12 deraadt 1552: ferrout("pr: time conversion failed\n");
1.5 grr 1553: return(NULL);
1554: }
1555: return(inf);
1.1 deraadt 1556: }
1557:
1558: /*
1.5 grr 1559: * addnum(): adds the line number to the column
1560: * Truncates from the front or pads with spaces as required.
1561: * Numbers are right justified.
1.1 deraadt 1562: *
1.5 grr 1563: * buf buffer to store the number
1564: * wdth width of buffer to fill
1565: * line line number
1.1 deraadt 1566: *
1.5 grr 1567: * NOTE: numbers occupy part of the column. The posix
1568: * spec does not specify if -i processing should or should not
1569: * occur on number padding. The spec does say it occupies
1570: * part of the column. The usage of addnum currently treats
1571: * numbers as part of the column so spaces may be replaced.
1.1 deraadt 1572: */
1573: void
1574: addnum(buf, wdth, line)
1.13 mpech 1575: char *buf;
1576: int wdth;
1577: int line;
1.1 deraadt 1578: {
1.13 mpech 1579: char *pt = buf + wdth;
1.1 deraadt 1580:
1.5 grr 1581: do {
1582: *--pt = digs[line % 10];
1583: line /= 10;
1584: } while (line && (pt > buf));
1585:
1586: /*
1587: * pad with space as required
1588: */
1589: while (pt > buf)
1590: *--pt = ' ';
1.1 deraadt 1591: }
1592:
1593: /*
1.5 grr 1594: * prhead(): prints the top of page header
1.1 deraadt 1595: *
1.5 grr 1596: * buf buffer with time field (and offset)
1597: * cnt number of chars in buf
1598: * fname fname field for header
1599: * pagcnt page number
1600: *
1601: * prhead() should be used carefully, we don't want to print out headers
1602: * for null input files or orphan headers at the end of files, and also
1603: * trailer processing is typically conditional on whether you've called
1604: * prhead() at least once for a file and incremented pagecnt.. Exactly
1605: * how to determine whether to print a header is a little different in
1606: * the context each output mode, but we let the caller figure that out.
1.1 deraadt 1607: */
1608: int
1609: prhead(buf, fname, pagcnt)
1.5 grr 1610: char *buf;
1611: char *fname;
1612: int pagcnt;
1.1 deraadt 1613: {
1.5 grr 1614: int ips = 0;
1615: int ops = 0;
1616:
1617: beheaded = 1;
1.1 deraadt 1618:
1.5 grr 1619: if (skipping && pagcnt >= pgnm)
1620: skipping = 0;
1621:
1622: if (nohead || skipping)
1623: return (0);
1624:
1625: if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1626: pfail();
1627: return(1);
1628: }
1629: /*
1630: * posix is not clear if the header is subject to line length
1631: * restrictions. The specification for header line format
1632: * in the spec clearly does not limit length. No pr currently
1633: * restricts header length. However if we need to truncate in
1634: * an reasonable way, adjust the length of the printf by
1.15 ! deraadt 1635: * changing HDFMT to allow a length max as an argument printf.
1.5 grr 1636: * buf (which contains the offset spaces and time field could
1637: * also be trimmed
1638: *
1639: * note only the offset (if any) is processed for tab expansion
1640: */
1641: if (offst && otln(buf, offst, &ips, &ops, -1))
1642: return(1);
1643: (void)printf(HDFMT,buf+offst, fname, pagcnt);
1644: return(0);
1.1 deraadt 1645: }
1646:
1647: /*
1.5 grr 1648: * prtail(): pad page with empty lines (if required) and print page trailer
1649: * if requested
1650: *
1651: * cnt number of lines of padding needed
1652: * incomp was a '\n' missing from last line output
1.1 deraadt 1653: *
1.5 grr 1654: * prtail() can now be invoked unconditionally, with the notion that if
1655: * we haven't printed a hearder, these no need for a trailer
1.1 deraadt 1656: */
1657: int
1658: prtail(cnt, incomp)
1.13 mpech 1659: int cnt;
1.5 grr 1660: int incomp;
1.1 deraadt 1661: {
1.5 grr 1662: /*
1663: * if were's skipping to page N or haven't put out anything yet just exit
1664: */
1665: if (skipping || beheaded == 0)
1666: return (0);
1667: beheaded = 0;
1668:
1669: /*
1670: * if noheaders, only terminate an incomplete last line
1671: */
1672: if (nohead) {
1673:
1674: if (incomp) {
1675: if (dspace)
1676: if (putchar('\n') == EOF) {
1677: pfail();
1678: return(1);
1.3 imp 1679: }
1.5 grr 1680: if (putchar('\n') == EOF) {
1681: pfail();
1682: return(1);
1683: }
1.1 deraadt 1684: }
1.5 grr 1685: /*
1686: * but honor the formfeed request
1687: */
1688: if (formfeed)
1689: if (putchar(OUTFF) == EOF) {
1690: pfail();
1691: return(1);
1692: }
1693:
1694: } else {
1.1 deraadt 1695:
1696: /*
1697: * if double space output two \n
1.5 grr 1698: *
1699: * XXX this all seems bogus, why are we doing it here???
1700: * page length is in terms of output lines and only the input is
1701: * supposed to be double spaced... otln() users should be doing
1702: * something like linect+=(dspace ? 2:1).
1.1 deraadt 1703: */
1704: if (dspace)
1.5 grr 1705: cnt *= 2;
1.1 deraadt 1706:
1707: /*
1708: * if an odd number of lines per page, add an extra \n
1709: */
1710: if (addone)
1.5 grr 1711: ++cnt;
1.1 deraadt 1712:
1713: /*
1.5 grr 1714: * either put out a form-feed or pad page with blanks
1.1 deraadt 1715: */
1716: if (formfeed) {
1.5 grr 1717: if (incomp)
1718: if (putchar('\n') == EOF) {
1719: pfail();
1720: return(1);
1.1 deraadt 1721: }
1.5 grr 1722: if (putchar(OUTFF) == EOF) {
1723: pfail();
1724: return(1);
1725: }
1726:
1727: } else {
1728:
1729: if (incomp)
1730: cnt++;
1731:
1732: cnt += TAILLEN;
1733: while (--cnt >= 0) {
1.1 deraadt 1734: if (putchar('\n') == EOF) {
1.5 grr 1735: pfail();
1736: return(1);
1.1 deraadt 1737: }
1.5 grr 1738: }
1.1 deraadt 1739: }
1.5 grr 1740: }
1741:
1742: return(0);
1.1 deraadt 1743: }
1744:
1745: /*
1.5 grr 1746: * terminate(): when a SIGINT is recvd
1.1 deraadt 1747: */
1748: void
1749: terminate(which_sig)
1.5 grr 1750: int which_sig;
1.1 deraadt 1751: {
1.12 deraadt 1752: flsh_errs();
1.11 deraadt 1753: _exit(1);
1.1 deraadt 1754: }
1755:
1756: void
1757: mfail()
1758: {
1.12 deraadt 1759: ferrout("pr: memory allocation failed\n");
1.1 deraadt 1760: }
1761:
1762: void
1763: pfail()
1764: {
1.12 deraadt 1765: ferrout("pr: write failure, %s\n", strerror(errno));
1.1 deraadt 1766: }
1767:
1768: void
1769: usage()
1770: {
1.12 deraadt 1771: ferrout(
1772: "usage: pr [+page] [-col] [-adfFmrt] [-e[ch][gap]] [-h header]\n");
1773: ferrout(
1774: " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n");
1775: ferrout(
1.9 deraadt 1776: " [-s[ch]] [-w width] [-] [file ...]\n", ferr);
1.1 deraadt 1777: }
1778:
1779: /*
1.5 grr 1780: * setup: Validate command args, initialize and perform sanity
1781: * checks on options
1.1 deraadt 1782: */
1783: int
1784: setup(argc, argv)
1.13 mpech 1785: int argc;
1786: char **argv;
1.1 deraadt 1787: {
1.13 mpech 1788: int c;
1.5 grr 1789: int eflag = 0;
1790: int iflag = 0;
1791: int wflag = 0;
1792: int cflag = 0;
1793:
1.12 deraadt 1794: if (isatty(fileno(stdout)))
1795: ferr = 1;
1796:
1.6 millert 1797: while ((c = egetopt(argc, argv, "#adfFmrte?h:i?l:n?o:s?w:")) != -1) {
1.5 grr 1798: switch (c) {
1799: case '+':
1800: if ((pgnm = atoi(eoptarg)) < 1) {
1.12 deraadt 1801: ferrout("pr: +page number must be 1 or more\n");
1.5 grr 1802: return(1);
1803: }
1804: ++skipping;
1805: break;
1806: case '-':
1807: if ((clcnt = atoi(eoptarg)) < 1) {
1.12 deraadt 1808: ferrout("pr: -columns must be 1 or more\n");
1.5 grr 1809: return(1);
1810: }
1811: if (clcnt > 1)
1812: ++cflag;
1813: break;
1814: case 'a':
1815: ++across;
1816: break;
1817: case 'd':
1818: ++dspace;
1819: break;
1820: case 'e':
1821: ++eflag;
1822: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1823: inchar = *eoptarg++;
1824: else
1825: inchar = INCHAR;
1826: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1827: if ((ingap = atoi(eoptarg)) < 0) {
1.12 deraadt 1828: ferrout("pr: -e gap must be 0 or more\n");
1.5 grr 1829: return(1);
1830: }
1831: if (ingap == 0)
1832: ingap = INGAP;
1833: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 deraadt 1834: ferrout("pr: invalid value for -e %s\n", eoptarg);
1.5 grr 1835: return(1);
1836: } else
1837: ingap = INGAP;
1838: break;
1839: case 'f':
1840: case 'F':
1841: ++formfeed;
1842: break;
1843: case 'h':
1844: header = eoptarg;
1845: break;
1846: case 'i':
1847: ++iflag;
1848: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1849: ochar = *eoptarg++;
1850: else
1851: ochar = OCHAR;
1852: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1853: if ((ogap = atoi(eoptarg)) < 0) {
1.12 deraadt 1854: ferrout("pr: -i gap must be 0 or more\n");
1.5 grr 1855: return(1);
1856: }
1857: if (ogap == 0)
1858: ogap = OGAP;
1859: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 deraadt 1860: ferrout("pr: invalid value for -i %s\n", eoptarg);
1.5 grr 1861: return(1);
1862: } else
1863: ogap = OGAP;
1864: break;
1865: case 'l':
1866: if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) {
1.12 deraadt 1867: ferrout("pr: Number of lines must be 1 or more\n");
1.5 grr 1868: return(1);
1869: }
1870: break;
1871: case 'm':
1872: ++merge;
1873: break;
1874: case 'n':
1875: if ((eoptarg != NULL) && !isdigit(*eoptarg))
1876: nmchar = *eoptarg++;
1877: else
1878: nmchar = NMCHAR;
1879: if ((eoptarg != NULL) && isdigit(*eoptarg)) {
1880: if ((nmwd = atoi(eoptarg)) < 1) {
1.12 deraadt 1881: ferrout("pr: -n width must be 1 or more\n");
1.5 grr 1882: return(1);
1.1 deraadt 1883: }
1.5 grr 1884: } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1.12 deraadt 1885: ferrout("pr: invalid value for -n %s\n", eoptarg);
1.5 grr 1886: return(1);
1887: } else
1888: nmwd = NMWD;
1889: break;
1890: case 'o':
1891: if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){
1.12 deraadt 1892: ferrout("pr: -o offset must be 1 or more\n");
1.5 grr 1893: return(1);
1894: }
1895: break;
1896: case 'r':
1897: ++nodiag;
1898: break;
1899: case 's':
1900: ++sflag;
1901: if (eoptarg == NULL)
1902: schar = SCHAR;
1.7 deraadt 1903: else {
1.5 grr 1904: schar = *eoptarg++;
1.7 deraadt 1905: if (*eoptarg != '\0') {
1.12 deraadt 1906: ferrout("pr: invalid value for -s %s\n", eoptarg);
1.7 deraadt 1907: return(1);
1908: }
1.5 grr 1909: }
1910: break;
1911: case 't':
1912: ++nohead;
1913: break;
1914: case 'w':
1915: ++wflag;
1916: if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){
1.12 deraadt 1917: ferrout("pr: -w width must be 1 or more \n");
1.5 grr 1918: return(1);
1919: }
1920: break;
1921: case '?':
1922: default:
1923: return(1);
1924: }
1925: }
1926:
1927: /*
1928: * default and sanity checks
1929: */
1930: inform++;
1931:
1932: if (!clcnt) {
1933: if (merge) {
1934: if ((clcnt = argc - eoptind) <= 1) {
1935: clcnt = CLCNT;
1936: #ifdef stupid
1937: merge = 0;
1938: #endif
1939: }
1.1 deraadt 1940: } else
1.5 grr 1941: clcnt = CLCNT;
1942: }
1943: if (across) {
1944: if (clcnt == 1) {
1.12 deraadt 1945: ferrout("pr: -a flag requires multiple columns\n");
1.5 grr 1946: return(1);
1947: }
1948: if (merge) {
1.12 deraadt 1949: ferrout("pr: -m cannot be used with -a\n");
1.5 grr 1950: return(1);
1951: }
1952: }
1953: if (!wflag) {
1954: if (sflag)
1955: pgwd = SPGWD;
1956: else
1957: pgwd = PGWD;
1958: }
1959: if (cflag || merge) {
1960: if (!eflag) {
1961: inchar = INCHAR;
1962: ingap = INGAP;
1963: }
1964: if (!iflag) {
1965: ochar = OCHAR;
1966: ogap = OGAP;
1967: }
1968: }
1969: if (cflag) {
1970: if (merge) {
1.12 deraadt 1971: ferrout("pr: -m cannot be used with multiple columns\n");
1.5 grr 1972: return(1);
1.1 deraadt 1973: }
1.5 grr 1974: if (nmwd) {
1975: colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1976: pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1977: } else {
1978: colwd = (pgwd + 1 - clcnt)/clcnt;
1979: pgwd = ((colwd + 1) * clcnt) - 1;
1.1 deraadt 1980: }
1.5 grr 1981: if (colwd < 1) {
1.12 deraadt 1982: ferrout("pr: page width is too small for %d columns\n",clcnt);
1.5 grr 1983: return(1);
1984: }
1985: }
1986: if (!lines)
1987: lines = LINES;
1988:
1989: /*
1990: * make sure long enough for headers. if not disable
1991: */
1992: if (lines <= HEADLEN + TAILLEN)
1993: ++nohead;
1994: else if (!nohead)
1995: lines -= HEADLEN + TAILLEN;
1996:
1997: /*
1998: * adjust for double space on odd length pages
1999: */
2000: if (dspace) {
2001: if (lines == 1)
2002: dspace = 0;
2003: else {
2004: if (lines & 1)
2005: ++addone;
2006: lines /= 2;
2007: }
2008: }
2009:
2010: if ((timefrmt = getenv("LC_TIME")) == NULL)
2011: timefrmt = TIMEFMT;
2012: return(0);
1.1 deraadt 2013: }