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