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