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