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