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