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