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