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