Drop main() prototype. Syncs with NetBSD-8
[minix.git] / usr.bin / pr / pr.c
blobc17b02c6924ae67a677803d1675fe145b7d8bcc4
1 /* $NetBSD: pr.c,v 1.24 2012/08/01 02:27:48 ginsbach Exp $ */
3 /*-
4 * Copyright (c) 1991 Keith Muller.
5 * Copyright (c) 1993
6 * The Regents of the University of California. All rights reserved.
7 * Copyright (c) 2012
8 * The NetBSD Foundation, Inc.
10 * This code is derived from software contributed to Berkeley by
11 * Keith Muller of the University of California, San Diego.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include <sys/cdefs.h>
39 #ifndef lint
40 __COPYRIGHT("@(#) Copyright (c) 1993\
41 The Regents of the University of California. All rights reserved.");
42 #endif /* not lint */
44 #ifndef lint
45 #if 0
46 from: static char sccsid[] = "@(#)pr.c 8.1 (Berkeley) 6/6/93";
47 #else
48 __RCSID("$NetBSD: pr.c,v 1.24 2012/08/01 02:27:48 ginsbach Exp $");
49 #endif
50 #endif /* not lint */
52 #include <sys/types.h>
53 #include <sys/time.h>
54 #include <sys/stat.h>
56 #include <ctype.h>
57 #include <errno.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <time.h>
63 #include <unistd.h>
64 #include <util.h>
66 #include "pr.h"
67 #include "extern.h"
70 * pr: a printing and pagination filter. If multiple input files
71 * are specified, each is read, formatted, and written to standard
72 * output. By default, input is separated into 66-line pages, each
73 * with a header that includes the page number, date, time and the
74 * files pathname.
76 * Complies with posix P1003.2/D11
80 * parameter variables
82 static int pgnm; /* starting page number */
83 static int clcnt; /* number of columns */
84 static int colwd; /* column data width - multiple columns */
85 static int across; /* mult col flag; write across page */
86 static int dspace; /* double space flag */
87 static char inchar; /* expand input char */
88 static int ingap; /* expand input gap */
89 static int formfeed; /* use formfeed as trailer */
90 static char *header; /* header name instead of file name */
91 static char ochar; /* contract output char */
92 static int ogap; /* contract output gap */
93 static int lines; /* number of lines per page */
94 static int merge; /* merge multiple files in output */
95 static char nmchar; /* line numbering append char */
96 static int nmwd; /* width of line number field */
97 static int offst; /* number of page offset spaces */
98 static int nodiag; /* do not report file open errors */
99 static char schar; /* text column separation character */
100 static int sflag; /* -s option for multiple columns */
101 static int ttyout; /* output is a tty */
102 static int nohead; /* do not write head and trailer */
103 static int pgpause; /* pause before each page */
104 static int pgwd; /* page width with multiple col output */
105 static const char *timefrmt = TIMEFMT; /* time conversion string */
106 static FILE *ttyinf; /* input terminal for page pauses */
109 * misc globals
111 static FILE *errf; /* error message file pointer */
112 static int addone; /* page length is odd with double space */
113 static int errcnt; /* error count on file processing */
114 static const char digs[] = "0123456789"; /* page number translation map */
116 static void addnum(char *, int, int);
117 static void flsh_errs(void);
118 static int horzcol(int, char **);
119 static int inln(FILE *, char *, int, int *, int, int *);
120 static int inskip(FILE *, int, int);
121 static void mfail(void);
122 static int mulfile(int, char **);
123 static FILE *nxtfile(int, char **, const char **, char *, int);
124 static int onecol(int, char **);
125 static int otln(char *, int, int *, int *, int);
126 static void pfail(void);
127 static int prhead(char *, const char *, int);
128 static void prpause(int);
129 static int prtail(int, int);
130 static int setup(int, char **);
131 __dead static void terminate(int);
132 static void usage(void);
133 static int vertcol(int, char **);
136 main(int argc, char *argv[])
138 int ret_val;
140 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
141 (void)signal(SIGINT, terminate);
142 ret_val = setup(argc, argv);
143 if (!ret_val) {
145 * select the output format based on options
147 if (merge)
148 ret_val = mulfile(argc, argv);
149 else if (clcnt == 1)
150 ret_val = onecol(argc, argv);
151 else if (across)
152 ret_val = horzcol(argc, argv);
153 else
154 ret_val = vertcol(argc, argv);
155 } else
156 usage();
157 flsh_errs();
158 if (errcnt || ret_val)
159 exit(1);
160 return(0);
164 * onecol: print files with only one column of output.
165 * Line length is unlimited.
167 static int
168 onecol(int argc, char *argv[])
170 int cnt = -1;
171 int off;
172 int lrgln;
173 int linecnt;
174 int num;
175 int lncnt;
176 int pagecnt;
177 int ips;
178 int ops;
179 int cps;
180 char *obuf = NULL;
181 char *lbuf;
182 char *nbuf;
183 char *hbuf = NULL;
184 char *ohbuf;
185 FILE *inf = NULL;
186 const char *fname;
187 int mor;
188 int error = 1;
190 #if defined(__minix)
191 /* LSC: -Werror=maybe-uninitialized, when compiling with -O3 */
192 mor = 0;
193 #endif /* defined(__minix) */
194 if (nmwd)
195 num = nmwd + 1;
196 else
197 num = 0;
198 off = num + offst;
201 * allocate line buffer
203 if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL)
204 goto oomem;
206 * allocate header buffer
208 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
209 goto oomem;
211 ohbuf = hbuf + offst;
212 nbuf = obuf + offst;
213 lbuf = nbuf + num;
214 if (num)
215 nbuf[--num] = nmchar;
216 if (offst) {
217 (void)memset(obuf, (int)' ', offst);
218 (void)memset(hbuf, (int)' ', offst);
222 * loop by file
224 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
225 if (pgnm) {
227 * skip to specified page
229 if (inskip(inf, pgnm, lines))
230 continue;
231 pagecnt = pgnm;
232 } else
233 pagecnt = 1;
234 lncnt = 0;
237 * loop by page
239 for(;;) {
240 linecnt = 0;
241 lrgln = 0;
242 ops = 0;
243 ips = 0;
244 cps = 0;
247 * loop by line
249 while (linecnt < lines) {
251 * input next line
253 if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
254 break;
255 if (!linecnt) {
256 if (pgpause)
257 prpause(pagecnt);
259 if (!nohead &&
260 prhead(hbuf, fname, pagecnt))
261 goto out;
265 * start of new line.
267 if (!lrgln) {
268 if (num)
269 addnum(nbuf, num, ++lncnt);
270 if (otln(obuf,cnt+off, &ips, &ops, mor))
271 goto out;
272 } else if (otln(lbuf, cnt, &ips, &ops, mor))
273 goto out;
276 * if line bigger than buffer, get more
278 if (mor) {
279 lrgln = 1;
280 continue;
284 * whole line rcvd. reset tab proc. state
286 ++linecnt;
287 lrgln = 0;
288 ops = 0;
289 ips = 0;
293 * fill to end of page
295 if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
296 goto out;
299 * On EOF go to next file
301 if (cnt < 0)
302 break;
303 ++pagecnt;
305 if (inf != stdin)
306 (void)fclose(inf);
308 if (eoptind < argc)
309 goto out;
310 error = 0;
311 goto out;
312 oomem:
313 mfail();
314 out:
315 free(obuf);
316 free(hbuf);
317 if (inf != NULL && inf != stdin)
318 (void)fclose(inf);
319 return error;
323 * vertcol: print files with more than one column of output down a page
325 static int
326 vertcol(int argc, char *argv[])
328 char *ptbf;
329 char **lstdat = NULL;
330 int i;
331 int j;
332 int cnt = -1;
333 int pln;
334 int *indy = NULL;
335 int cvc;
336 int *lindy = NULL;
337 int lncnt;
338 int stp;
339 int pagecnt;
340 int col = colwd + 1;
341 int mxlen = pgwd + offst + 1;
342 int mclcnt = clcnt - 1;
343 struct vcol *vc = NULL;
344 int mvc;
345 int tvc;
346 int cw = nmwd + 1;
347 int fullcol;
348 char *buf = NULL;
349 char *hbuf = NULL;
350 char *ohbuf;
351 const char *fname;
352 FILE *inf = NULL;
353 int ips = 0;
354 int cps = 0;
355 int ops = 0;
356 int mor = 0;
357 int error = 1;
360 * allocate page buffer
362 if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL)
363 goto oomem;
366 * allocate page header
368 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
369 goto oomem;
370 ohbuf = hbuf + offst;
371 if (offst)
372 (void)memset(hbuf, (int)' ', offst);
375 * col pointers when no headers
377 mvc = lines * clcnt;
378 if ((vc = malloc((unsigned)mvc*sizeof(struct vcol))) == NULL)
379 goto oomem;
382 * pointer into page where last data per line is located
384 if ((lstdat = malloc((unsigned)lines*sizeof(char *))) == NULL)
385 goto oomem;
388 * fast index lookups to locate start of lines
390 if ((indy = malloc((unsigned)lines*sizeof(int))) == NULL)
391 goto oomem;
392 if ((lindy = malloc((unsigned)lines*sizeof(int))) == NULL)
393 goto oomem;
395 if (nmwd)
396 fullcol = col + cw;
397 else
398 fullcol = col;
401 * initialize buffer lookup indexes and offset area
403 for (j = 0; j < lines; ++j) {
404 lindy[j] = j * mxlen;
405 indy[j] = lindy[j] + offst;
406 if (offst) {
407 ptbf = buf + lindy[j];
408 (void)memset(ptbf, (int)' ', offst);
409 ptbf += offst;
410 } else
411 ptbf = buf + indy[j];
412 lstdat[j] = ptbf;
416 * loop by file
418 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
419 if (pgnm) {
421 * skip to requested page
423 if (inskip(inf, pgnm, lines))
424 continue;
425 pagecnt = pgnm;
426 } else
427 pagecnt = 1;
428 lncnt = 0;
431 * loop by page
433 for(;;) {
435 * loop by column
437 cvc = 0;
438 for (i = 0; i < clcnt; ++i) {
439 j = 0;
441 * if last column, do not pad
443 if (i == mclcnt)
444 stp = 1;
445 else
446 stp = 0;
448 * loop by line
450 for(;;) {
452 * is this first column
454 if (!i) {
455 ptbf = buf + indy[j];
456 lstdat[j] = ptbf;
457 } else
458 ptbf = lstdat[j];
459 vc[cvc].pt = ptbf;
462 * add number
464 if (nmwd) {
465 addnum(ptbf, nmwd, ++lncnt);
466 ptbf += nmwd;
467 *ptbf++ = nmchar;
471 * input next line
473 cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
474 vc[cvc++].cnt = cnt;
475 if (cnt < 0)
476 break;
477 ptbf += cnt;
480 * pad all but last column on page
482 if (!stp) {
484 * pad to end of column
486 if (sflag)
487 *ptbf++ = schar;
488 else if ((pln = col-cnt) > 0) {
489 (void)memset(ptbf,
490 (int)' ',pln);
491 ptbf += pln;
495 * remember last char in line
497 lstdat[j] = ptbf;
498 if (++j >= lines)
499 break;
501 if (cnt < 0)
502 break;
506 * when -t (no header) is specified the spec requires
507 * the min number of lines. The last page may not have
508 * balanced length columns. To fix this we must reorder
509 * the columns. This is a very slow technique so it is
510 * only used under limited conditions. Without -t, the
511 * balancing of text columns is unspecified. To NOT
512 * balance the last page, add the global variable
513 * nohead to the if statement below e.g.
515 * if ((cnt < 0) && nohead && cvc ......
517 --cvc;
520 * check to see if last page needs to be reordered
522 if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
523 pln = cvc/clcnt;
524 if (cvc % clcnt)
525 ++pln;
527 if (pgpause)
528 prpause(pagecnt);
531 * print header
533 if (!nohead && prhead(hbuf, fname, pagecnt))
534 goto out;
535 for (i = 0; i < pln; ++i) {
536 ips = 0;
537 ops = 0;
538 if (offst&& otln(buf,offst,&ips,&ops,1)) {
539 error = 1;
540 goto out;
542 tvc = i;
544 for (j = 0; j < clcnt; ++j) {
546 * determine column length
548 if (j == mclcnt) {
550 * last column
552 cnt = vc[tvc].cnt;
553 if (nmwd)
554 cnt += cw;
555 } else if (sflag) {
557 * single ch between
559 cnt = vc[tvc].cnt + 1;
560 if (nmwd)
561 cnt += cw;
562 } else
563 cnt = fullcol;
564 if (otln(vc[tvc].pt, cnt, &ips,
565 &ops, 1))
566 goto out;
567 tvc += pln;
568 if (tvc >= cvc)
569 break;
572 * terminate line
574 if (otln(buf, 0, &ips, &ops, 0))
575 goto out;
578 * pad to end of page
580 if (prtail((lines - pln), 0))
581 goto out;
583 * done with output, go to next file
585 break;
589 * determine how many lines to output
591 if (i > 0)
592 pln = lines;
593 else
594 pln = j;
597 * print header
599 if (pln) {
600 if (pgpause)
601 prpause(pagecnt);
603 if (!nohead && prhead(hbuf, fname, pagecnt))
604 goto out;
608 * output each line
610 for (i = 0; i < pln; ++i) {
611 ptbf = buf + lindy[i];
612 if ((j = lstdat[i] - ptbf) <= offst)
613 break;
614 if (otln(ptbf, j, &ips, &ops, 0))
615 goto out;
619 * pad to end of page
621 if (pln && prtail((lines - pln), 0))
622 goto out;
625 * if EOF go to next file
627 if (cnt < 0)
628 break;
629 ++pagecnt;
631 if (inf != stdin)
632 (void)fclose(inf);
634 if (eoptind < argc)
635 goto out;
636 error = 0;
637 goto out;
638 oomem:
639 mfail();
640 out:
641 free(buf);
642 free(hbuf);
643 free(vc);
644 free(lstdat);
645 free(lindy);
646 if (inf != NULL && inf != stdin)
647 (void)fclose(inf);
648 return error;
652 * horzcol: print files with more than one column of output across a page
654 static int
655 horzcol(int argc, char *argv[])
657 char *ptbf;
658 int pln;
659 int cnt = -1;
660 char *lstdat;
661 int col = colwd + 1;
662 int j;
663 int i;
664 int lncnt;
665 int pagecnt;
666 char *buf = NULL;
667 char *hbuf = NULL;
668 char *ohbuf;
669 const char *fname;
670 FILE *inf = NULL;
671 int ips = 0;
672 int cps = 0;
673 int ops = 0;
674 int mor = 0;
675 int error = 1;
677 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
678 goto oomem;
681 * page header
683 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
684 goto oomem;
685 ohbuf = hbuf + offst;
686 if (offst) {
687 (void)memset(buf, (int)' ', offst);
688 (void)memset(hbuf, (int)' ', offst);
692 * loop by file
694 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
695 if (pgnm) {
696 if (inskip(inf, pgnm, lines))
697 continue;
698 pagecnt = pgnm;
699 } else
700 pagecnt = 1;
701 lncnt = 0;
704 * loop by page
706 for(;;) {
708 * loop by line
710 for (i = 0; i < lines; ++i) {
711 ptbf = buf + offst;
712 lstdat = ptbf;
713 j = 0;
715 * loop by col
717 for(;;) {
718 if (nmwd) {
720 * add number to column
722 addnum(ptbf, nmwd, ++lncnt);
723 ptbf += nmwd;
724 *ptbf++ = nmchar;
727 * input line
729 if ((cnt = inln(inf,ptbf,colwd,&cps,1,
730 &mor)) < 0)
731 break;
732 ptbf += cnt;
733 lstdat = ptbf;
736 * if last line skip padding
738 if (++j >= clcnt)
739 break;
742 * pad to end of column
744 if (sflag)
745 *ptbf++ = schar;
746 else if ((pln = col - cnt) > 0) {
747 (void)memset(ptbf,(int)' ',pln);
748 ptbf += pln;
753 * determine line length
755 if ((j = lstdat - buf) <= offst)
756 break;
757 if (!i) {
758 if (pgpause)
759 prpause(pagecnt);
761 if (!nohead &&
762 prhead(hbuf, fname, pagecnt))
763 goto out;
766 * output line
768 if (otln(buf, j, &ips, &ops, 0))
769 goto out;
773 * pad to end of page
775 if (i && prtail(lines-i, 0))
776 goto out;
779 * if EOF go to next file
781 if (cnt < 0)
782 break;
783 ++pagecnt;
785 if (inf != stdin)
786 (void)fclose(inf);
788 if (eoptind < argc)
789 goto out;
790 error = 0;
791 goto out;
792 oomem:
793 mfail();
794 out:
795 free(buf);
796 free(hbuf);
797 if (inf != NULL && inf != stdin)
798 (void)fclose(inf);
799 return error;
803 * mulfile: print files with more than one column of output and
804 * more than one file concurrently
806 static int
807 mulfile(int argc, char *argv[])
809 char *ptbf;
810 int j;
811 int pln;
812 int cnt;
813 char *lstdat;
814 int i;
815 FILE **fbuf = NULL;
816 int actf;
817 int lncnt;
818 int col;
819 int pagecnt;
820 int fproc;
821 char *buf = NULL;
822 char *hbuf = NULL;
823 char *ohbuf;
824 const char *fname;
825 int ips = 0;
826 int cps = 0;
827 int ops = 0;
828 int mor = 0;
829 int error = 1;
832 * array of FILE *, one for each operand
834 if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL)
835 goto oomem;
838 * page header
840 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
841 goto oomem;
842 ohbuf = hbuf + offst;
845 * do not know how many columns yet. The number of operands provide an
846 * upper bound on the number of columns. We use the number of files
847 * we can open successfully to set the number of columns. The operation
848 * of the merge operation (-m) in relation to unsuccesful file opens
849 * is unspecified by posix.
851 j = 0;
852 while (j < clcnt) {
853 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
854 break;
855 if (pgnm && (inskip(fbuf[j], pgnm, lines)))
856 fbuf[j] = NULL;
857 ++j;
861 * if no files, exit
863 if (!j)
864 goto out;
867 * calculate page boundries based on open file count
869 clcnt = j;
870 if (nmwd) {
871 colwd = (pgwd - clcnt - nmwd)/clcnt;
872 pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
873 } else {
874 colwd = (pgwd + 1 - clcnt)/clcnt;
875 pgwd = ((colwd + 1) * clcnt) - 1;
877 if (colwd < 1) {
878 (void)fprintf(errf,
879 "pr: page width too small for %d columns\n", clcnt);
880 goto out;
882 actf = clcnt;
883 col = colwd + 1;
886 * line buffer
888 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
889 goto out;
890 if (offst) {
891 (void)memset(buf, (int)' ', offst);
892 (void)memset(hbuf, (int)' ', offst);
894 if (pgnm)
895 pagecnt = pgnm;
896 else
897 pagecnt = 1;
898 lncnt = 0;
901 * continue to loop while any file still has data
903 while (actf > 0) {
905 * loop by line
907 for (i = 0; i < lines; ++i) {
908 ptbf = buf + offst;
909 lstdat = ptbf;
910 if (nmwd) {
912 * add line number to line
914 addnum(ptbf, nmwd, ++lncnt);
915 ptbf += nmwd;
916 *ptbf++ = nmchar;
918 j = 0;
919 fproc = 0;
922 * loop by column
924 for (j = 0; j < clcnt; ++j) {
925 if (fbuf[j] == NULL) {
927 * empty column; EOF
929 cnt = 0;
930 } else if ((cnt = inln(fbuf[j], ptbf, colwd,
931 &cps, 1, &mor)) < 0) {
933 * EOF hit; no data
935 if (fbuf[j] != stdin)
936 (void)fclose(fbuf[j]);
937 fbuf[j] = NULL;
938 --actf;
939 cnt = 0;
940 } else {
942 * process file data
944 ptbf += cnt;
945 lstdat = ptbf;
946 fproc++;
950 * if last ACTIVE column, done with line
952 if (fproc >= actf)
953 break;
956 * pad to end of column
958 if (sflag) {
959 *ptbf++ = schar;
960 } else if ((pln = col - cnt) > 0) {
961 (void)memset(ptbf, (int)' ', pln);
962 ptbf += pln;
967 * calculate data in line
969 if ((j = lstdat - buf) <= offst)
970 break;
972 if (!i) {
973 if (pgpause)
974 prpause(pagecnt);
976 if (!nohead && prhead(hbuf, fname, pagecnt))
977 goto out;
981 * output line
983 if (otln(buf, j, &ips, &ops, 0))
984 goto out;
987 * if no more active files, done
989 if (actf <= 0) {
990 ++i;
991 break;
996 * pad to end of page
998 if (i && prtail(lines-i, 0))
999 goto out;
1000 ++pagecnt;
1002 if (eoptind < argc)
1003 goto out;
1004 error = 0;
1005 goto out;
1006 oomem:
1007 mfail();
1008 out:
1009 if (fbuf) {
1010 for (j = 0; j < clcnt; j++)
1011 if (fbuf[j] && fbuf[j] != stdin)
1012 (void)fclose(fbuf[j]);
1013 free(fbuf);
1015 free(hbuf);
1016 free(buf);
1017 return error;
1021 * inln(): input a line of data (unlimited length lines supported)
1022 * Input is optionally expanded to spaces
1024 * inf: file
1025 * buf: buffer
1026 * lim: buffer length
1027 * cps: column positon 1st char in buffer (large line support)
1028 * trnc: throw away data more than lim up to \n
1029 * mor: set if more data in line (not truncated)
1031 static int
1032 inln(FILE *inf, char *buf, int lim, int *cps, int trnc, int *mor)
1034 int col;
1035 int gap = ingap;
1036 int ch = EOF;
1037 char *ptbuf;
1038 int chk = (int)inchar;
1040 ptbuf = buf;
1042 if (gap) {
1044 * expanding input option
1046 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1048 * is this the input "tab" char
1050 if (ch == chk) {
1052 * expand to number of spaces
1054 col = (ptbuf - buf) + *cps;
1055 col = gap - (col % gap);
1058 * if more than this line, push back
1060 if ((col > lim) && (ungetc(ch, inf) == EOF))
1061 return(1);
1064 * expand to spaces
1066 while ((--col >= 0) && (--lim >= 0))
1067 *ptbuf++ = ' ';
1068 continue;
1070 if (ch == '\n')
1071 break;
1072 *ptbuf++ = ch;
1074 } else {
1076 * no expansion
1078 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1079 if (ch == '\n')
1080 break;
1081 *ptbuf++ = ch;
1084 col = ptbuf - buf;
1085 if (ch == EOF) {
1086 *mor = 0;
1087 *cps = 0;
1088 if (!col)
1089 return(-1);
1090 return(col);
1092 if (ch == '\n') {
1094 * entire line processed
1096 *mor = 0;
1097 *cps = 0;
1098 return(col);
1102 * line was larger than limit
1104 if (trnc) {
1106 * throw away rest of line
1108 while ((ch = getc(inf)) != EOF) {
1109 if (ch == '\n')
1110 break;
1112 *cps = 0;
1113 *mor = 0;
1114 } else {
1116 * save column offset if not truncated
1118 *cps += col;
1119 *mor = 1;
1122 return(col);
1126 * otln(): output a line of data. (Supports unlimited length lines)
1127 * output is optionally contracted to tabs
1129 * buf: output buffer with data
1130 * cnt: number of chars of valid data in buf
1131 * svips: buffer input column position (for large lines)
1132 * svops: buffer output column position (for large lines)
1133 * mor: output line not complete in this buf; more data to come.
1134 * 1 is more, 0 is complete, -1 is no \n's
1136 static int
1137 otln(char *buf, int cnt, int *svips, int *svops, int mor)
1139 int ops; /* last col output */
1140 int ips; /* last col in buf examined */
1141 int gap = ogap;
1142 int tbps;
1143 char *endbuf;
1145 if (ogap) {
1147 * contracting on output
1149 endbuf = buf + cnt;
1150 ops = *svops;
1151 ips = *svips;
1152 while (buf < endbuf) {
1154 * count number of spaces and ochar in buffer
1156 if (*buf == ' ') {
1157 ++ips;
1158 ++buf;
1159 continue;
1163 * simulate ochar processing
1165 if (*buf == ochar) {
1166 ips += gap - (ips % gap);
1167 ++buf;
1168 continue;
1172 * got a non space char; contract out spaces
1174 while (ips - ops > 1) {
1176 * use as many ochar as will fit
1178 if ((tbps = ops + gap - (ops % gap)) > ips)
1179 break;
1180 if (putchar(ochar) == EOF) {
1181 pfail();
1182 return(1);
1184 ops = tbps;
1187 while (ops < ips) {
1189 * finish off with spaces
1191 if (putchar(' ') == EOF) {
1192 pfail();
1193 return(1);
1195 ++ops;
1199 * output non space char
1201 if (putchar(*buf++) == EOF) {
1202 pfail();
1203 return(1);
1205 ++ips;
1206 ++ops;
1209 if (mor > 0) {
1211 * if incomplete line, save position counts
1213 *svops = ops;
1214 *svips = ips;
1215 return(0);
1218 if (mor < 0) {
1219 while (ips - ops > 1) {
1221 * use as many ochar as will fit
1223 if ((tbps = ops + gap - (ops % gap)) > ips)
1224 break;
1225 if (putchar(ochar) == EOF) {
1226 pfail();
1227 return(1);
1229 ops = tbps;
1231 while (ops < ips) {
1233 * finish off with spaces
1235 if (putchar(' ') == EOF) {
1236 pfail();
1237 return(1);
1239 ++ops;
1241 return(0);
1243 } else {
1245 * output is not contracted
1247 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1248 pfail();
1249 return(1);
1251 if (mor != 0)
1252 return(0);
1256 * process line end and double space as required
1258 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1259 pfail();
1260 return(1);
1262 return(0);
1266 * inskip(): skip over pgcnt pages with lncnt lines per page
1267 * file is closed at EOF (if not stdin).
1269 * inf FILE * to read from
1270 * pgcnt number of pages to skip
1271 * lncnt number of lines per page
1273 static int
1274 inskip(FILE *inf, int pgcnt, int lncnt)
1276 int c;
1277 int cnt;
1279 while(--pgcnt > 0) {
1280 cnt = lncnt;
1281 while ((c = getc(inf)) != EOF) {
1282 if ((c == '\n') && (--cnt == 0))
1283 break;
1285 if (c == EOF) {
1286 if (inf != stdin)
1287 (void)fclose(inf);
1288 return(1);
1291 return(0);
1295 * nxtfile: returns a FILE * to next file in arg list and sets the
1296 * time field for this file (or current date).
1298 * buf array to store proper date for the header.
1299 * dt if set skips the date processing (used with -m)
1301 static FILE *
1302 nxtfile(int argc, char **argv, const char **fname, char *buf, int dt)
1304 FILE *inf = NULL;
1305 struct timeval tv;
1306 struct timezone tz;
1307 struct tm *timeptr = NULL;
1308 struct stat statbuf;
1309 time_t curtime;
1310 static int twice = -1;
1312 ++twice;
1313 if (eoptind >= argc) {
1315 * no file listed; default, use standard input
1317 if (twice)
1318 return(NULL);
1319 clearerr(stdin);
1320 inf = stdin;
1321 if (header != NULL)
1322 *fname = header;
1323 else
1324 *fname = FNAME;
1325 if (nohead)
1326 return(inf);
1327 if (gettimeofday(&tv, &tz) < 0) {
1328 ++errcnt;
1329 (void)fprintf(errf, "pr: cannot get time of day, %s\n",
1330 strerror(errno));
1331 eoptind = argc - 1;
1332 return(NULL);
1334 curtime = tv.tv_sec;
1335 timeptr = localtime(&curtime);
1337 for (; eoptind < argc; ++eoptind) {
1338 if (strcmp(argv[eoptind], "-") == 0) {
1340 * process a "-" for filename
1342 clearerr(stdin);
1343 inf = stdin;
1344 if (header != NULL)
1345 *fname = header;
1346 else
1347 *fname = FNAME;
1348 ++eoptind;
1349 if (nohead || (dt && twice))
1350 return(inf);
1351 if (gettimeofday(&tv, &tz) < 0) {
1352 ++errcnt;
1353 (void)fprintf(errf,
1354 "pr: cannot get time of day, %s\n",
1355 strerror(errno));
1356 return(NULL);
1358 curtime = tv.tv_sec;
1359 timeptr = localtime(&curtime);
1360 } else {
1362 * normal file processing
1364 if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1365 ++errcnt;
1366 if (nodiag)
1367 continue;
1368 (void)fprintf(errf, "pr: Cannot open %s, %s\n",
1369 argv[eoptind], strerror(errno));
1370 continue;
1372 if (header != NULL)
1373 *fname = header;
1374 else if (dt)
1375 *fname = FNAME;
1376 else
1377 *fname = argv[eoptind];
1378 ++eoptind;
1379 if (nohead || (dt && twice))
1380 return(inf);
1382 if (dt) {
1383 if (gettimeofday(&tv, &tz) < 0) {
1384 ++errcnt;
1385 (void)fprintf(errf,
1386 "pr: cannot get time of day, %s\n",
1387 strerror(errno));
1388 return(NULL);
1390 curtime = tv.tv_sec;
1391 timeptr = localtime(&curtime);
1392 } else {
1393 if (fstat(fileno(inf), &statbuf) < 0) {
1394 ++errcnt;
1395 (void)fclose(inf);
1396 (void)fprintf(errf,
1397 "pr: Cannot stat %s, %s\n",
1398 argv[eoptind], strerror(errno));
1399 return(NULL);
1401 timeptr = localtime(&(statbuf.st_mtime));
1404 break;
1406 if (inf == NULL)
1407 return(NULL);
1410 * set up time field used in header
1412 if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1413 ++errcnt;
1414 if (inf != stdin)
1415 (void)fclose(inf);
1416 (void)fputs("pr: time conversion failed\n", errf);
1417 return(NULL);
1419 return(inf);
1423 * addnum(): adds the line number to the column
1424 * Truncates from the front or pads with spaces as required.
1425 * Numbers are right justified.
1427 * buf buffer to store the number
1428 * wdth width of buffer to fill
1429 * line line number
1431 * NOTE: numbers occupy part of the column. The posix
1432 * spec does not specify if -i processing should or should not
1433 * occur on number padding. The spec does say it occupies
1434 * part of the column. The usage of addnum currently treats
1435 * numbers as part of the column so spaces may be replaced.
1437 void
1438 addnum(char *buf, int wdth, int line)
1440 char *pt = buf + wdth;
1442 do {
1443 *--pt = digs[line % 10];
1444 line /= 10;
1445 } while (line && (pt > buf));
1448 * pad with space as required
1450 while (pt > buf)
1451 *--pt = ' ';
1455 * prpause(): pause before printing each page
1457 * pagcnt page number
1459 static void
1460 prpause(int pagcnt)
1463 if (ttyout) {
1464 int c;
1466 (void)putc('\a', stderr);
1467 (void)fflush(stderr);
1469 while ((c = getc(ttyinf)) != '\n' && c != EOF)
1473 * pause ONLY before first page of first file
1475 if (pgpause == FIRSTPAGE && pagcnt == 1)
1476 pgpause = NO_PAUSE;
1481 * prhead(): prints the top of page header
1483 * buf buffer with time field (and offset)
1484 * cnt number of chars in buf
1485 * fname fname field for header
1486 * pagcnt page number
1488 static int
1489 prhead(char *buf, const char *fname, int pagcnt)
1491 int ips = 0;
1492 int ops = 0;
1494 if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1495 pfail();
1496 return(1);
1499 * posix is not clear if the header is subject to line length
1500 * restrictions. The specification for header line format
1501 * in the spec clearly does not limit length. No pr currently
1502 * restricts header length. However if we need to truncate in
1503 * an reasonable way, adjust the length of the printf by
1504 * changing HDFMT to allow a length max as an argument printf.
1505 * buf (which contains the offset spaces and time field could
1506 * also be trimmed
1508 * note only the offset (if any) is processed for tab expansion
1510 if (offst && otln(buf, offst, &ips, &ops, -1))
1511 return(1);
1512 (void)printf(HDFMT,buf+offst, fname, pagcnt);
1513 return(0);
1517 * prtail(): pad page with empty lines (if required) and print page trailer
1518 * if requested
1520 * cnt number of lines of padding needed
1521 * incomp was a '\n' missing from last line output
1523 static int
1524 prtail(int cnt, int incomp)
1526 if (nohead) {
1528 * only pad with no headers when incomplete last line
1530 if (!incomp)
1531 return(0);
1532 if ((dspace && (putchar('\n') == EOF)) ||
1533 (putchar('\n') == EOF)) {
1534 pfail();
1535 return(1);
1537 return(0);
1541 * if double space output two \n
1543 if (dspace)
1544 cnt *= 2;
1547 * if an odd number of lines per page, add an extra \n
1549 if (addone)
1550 ++cnt;
1553 * pad page
1555 if (formfeed) {
1556 if ((incomp && (putchar('\n') == EOF)) ||
1557 (putchar('\f') == EOF)) {
1558 pfail();
1559 return(1);
1561 return(0);
1563 cnt += TAILLEN;
1564 while (--cnt >= 0) {
1565 if (putchar('\n') == EOF) {
1566 pfail();
1567 return(1);
1570 return(0);
1574 * terminate(): when a SIGINT is recvd
1576 static void
1577 terminate(int which_sig)
1579 flsh_errs();
1580 (void)raise_default_signal(which_sig);
1581 exit(1);
1586 * flsh_errs(): output saved up diagnostic messages after all normal
1587 * processing has completed
1589 static void
1590 flsh_errs(void)
1592 char buf[BUFSIZ];
1594 (void)fflush(stdout);
1595 (void)fflush(errf);
1596 if (errf == stderr)
1597 return;
1598 rewind(errf);
1599 while (fgets(buf, BUFSIZ, errf) != NULL)
1600 (void)fputs(buf, stderr);
1603 static void
1604 mfail(void)
1606 (void)fputs("pr: memory allocation failed\n", errf);
1609 static void
1610 pfail(void)
1612 (void)fprintf(errf, "pr: write failure, %s\n", strerror(errno));
1615 static void
1616 usage(void)
1618 (void)fputs(
1619 "usage: pr [+page] [-col] [-adFfmprt] [-e[ch][gap]] [-h header]\n",
1620 errf);
1621 (void)fputs(
1622 " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",
1623 errf);
1624 (void)fputs(
1625 " [-s[ch]] [-T timefmt] [-w width] [-] [file ...]\n",
1626 errf);
1630 * setup: Validate command args, initialize and perform sanity
1631 * checks on options
1633 static int
1634 setup(int argc, char **argv)
1636 int c;
1637 int eflag = 0;
1638 int iflag = 0;
1639 int wflag = 0;
1640 int cflag = 0;
1642 ttyinf = stdin;
1644 if (isatty(fileno(stdout))) {
1646 * defer diagnostics until processing is done
1648 if ((errf = tmpfile()) == NULL) {
1649 (void)fputs("Cannot defer diagnostic messages\n",stderr);
1650 return(1);
1652 ttyout = 1;
1653 } else
1654 errf = stderr;
1655 while ((c = egetopt(argc, argv, "#adFfmrte?h:i?l:n?o:ps?T:w:")) != -1) {
1656 switch (c) {
1657 case '+':
1658 if ((pgnm = atoi(eoptarg)) < 1) {
1659 (void)fputs("pr: +page number must be 1 or more\n",
1660 errf);
1661 return(1);
1663 break;
1664 case '-':
1665 if ((clcnt = atoi(eoptarg)) < 1) {
1666 (void)fputs("pr: -columns must be 1 or more\n",errf);
1667 return(1);
1669 if (clcnt > 1)
1670 ++cflag;
1671 break;
1672 case 'a':
1673 ++across;
1674 break;
1675 case 'd':
1676 ++dspace;
1677 break;
1678 case 'e':
1679 ++eflag;
1680 if ((eoptarg != NULL) &&
1681 !isdigit((unsigned char)*eoptarg))
1682 inchar = *eoptarg++;
1683 else
1684 inchar = INCHAR;
1685 if ((eoptarg != NULL) &&
1686 isdigit((unsigned char)*eoptarg)) {
1687 if ((ingap = atoi(eoptarg)) < 0) {
1688 (void)fputs(
1689 "pr: -e gap must be 0 or more\n", errf);
1690 return(1);
1692 if (ingap == 0)
1693 ingap = INGAP;
1694 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1695 (void)fprintf(errf,
1696 "pr: invalid value for -e %s\n", eoptarg);
1697 return(1);
1698 } else
1699 ingap = INGAP;
1700 break;
1701 case 'f':
1702 pgpause |= FIRSTPAGE;
1703 /*FALLTHROUGH*/
1704 case 'F':
1705 ++formfeed;
1706 break;
1707 case 'h':
1708 header = eoptarg;
1709 break;
1710 case 'i':
1711 ++iflag;
1712 if ((eoptarg != NULL) &&
1713 !isdigit((unsigned char)*eoptarg))
1714 ochar = *eoptarg++;
1715 else
1716 ochar = OCHAR;
1717 if ((eoptarg != NULL) &&
1718 isdigit((unsigned char)*eoptarg)) {
1719 if ((ogap = atoi(eoptarg)) < 0) {
1720 (void)fputs(
1721 "pr: -i gap must be 0 or more\n", errf);
1722 return(1);
1724 if (ogap == 0)
1725 ogap = OGAP;
1726 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1727 (void)fprintf(errf,
1728 "pr: invalid value for -i %s\n", eoptarg);
1729 return(1);
1730 } else
1731 ogap = OGAP;
1732 break;
1733 case 'l':
1734 if (!isdigit((unsigned char)*eoptarg) ||
1735 ((lines=atoi(eoptarg)) < 1)) {
1736 (void)fputs(
1737 "pr: Number of lines must be 1 or more\n",errf);
1738 return(1);
1740 break;
1741 case 'm':
1742 ++merge;
1743 break;
1744 case 'n':
1745 if ((eoptarg != NULL) &&
1746 !isdigit((unsigned char)*eoptarg))
1747 nmchar = *eoptarg++;
1748 else
1749 nmchar = NMCHAR;
1750 if ((eoptarg != NULL) &&
1751 isdigit((unsigned char)*eoptarg)) {
1752 if ((nmwd = atoi(eoptarg)) < 1) {
1753 (void)fputs(
1754 "pr: -n width must be 1 or more\n",errf);
1755 return(1);
1757 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1758 (void)fprintf(errf,
1759 "pr: invalid value for -n %s\n", eoptarg);
1760 return(1);
1761 } else
1762 nmwd = NMWD;
1763 break;
1764 case 'o':
1765 if (!isdigit((unsigned char)*eoptarg) ||
1766 ((offst = atoi(eoptarg))< 1)){
1767 (void)fputs("pr: -o offset must be 1 or more\n",
1768 errf);
1769 return(1);
1771 break;
1772 case 'p':
1773 pgpause |= EACHPAGE;
1774 break;
1775 case 'r':
1776 ++nodiag;
1777 break;
1778 case 's':
1779 ++sflag;
1780 if (eoptarg == NULL)
1781 schar = SCHAR;
1782 else
1783 schar = *eoptarg++;
1784 if (eoptarg && *eoptarg != '\0') {
1785 (void)fprintf(errf,
1786 "pr: invalid value for -s %s\n", eoptarg);
1787 return(1);
1789 break;
1790 case 'T':
1791 timefrmt = eoptarg;
1792 break;
1793 case 't':
1794 ++nohead;
1795 break;
1796 case 'w':
1797 ++wflag;
1798 if (!isdigit((unsigned char)*eoptarg) ||
1799 ((pgwd = atoi(eoptarg)) < 1)){
1800 (void)fputs(
1801 "pr: -w width must be 1 or more \n",errf);
1802 return(1);
1804 break;
1805 case '?':
1806 default:
1807 return(1);
1812 * default and sanity checks
1814 if (!clcnt) {
1815 if (merge) {
1816 if ((clcnt = argc - eoptind) <= 1) {
1817 clcnt = CLCNT;
1818 merge = 0;
1820 } else
1821 clcnt = CLCNT;
1823 if (across) {
1824 if (clcnt == 1) {
1825 (void)fputs("pr: -a flag requires multiple columns\n",
1826 errf);
1827 return(1);
1829 if (merge) {
1830 (void)fputs("pr: -m cannot be used with -a\n", errf);
1831 return(1);
1834 if (!wflag) {
1835 if (sflag)
1836 pgwd = SPGWD;
1837 else
1838 pgwd = PGWD;
1840 if (cflag || merge) {
1841 if (!eflag) {
1842 inchar = INCHAR;
1843 ingap = INGAP;
1845 if (!iflag) {
1846 ochar = OCHAR;
1847 ogap = OGAP;
1850 if (cflag) {
1851 if (merge) {
1852 (void)fputs(
1853 "pr: -m cannot be used with multiple columns\n", errf);
1854 return(1);
1856 if (nmwd) {
1857 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1858 pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1859 } else {
1860 colwd = (pgwd + 1 - clcnt)/clcnt;
1861 pgwd = ((colwd + 1) * clcnt) - 1;
1863 if (colwd < 1) {
1864 (void)fprintf(errf,
1865 "pr: page width is too small for %d columns\n",clcnt);
1866 return(1);
1869 if (!lines)
1870 lines = LINES;
1873 * make sure long enough for headers. if not disable
1875 if (lines <= HEADLEN + TAILLEN)
1876 ++nohead;
1877 else if (!nohead)
1878 lines -= HEADLEN + TAILLEN;
1881 * adjust for double space on odd length pages
1883 if (dspace) {
1884 if (lines == 1)
1885 dspace = 0;
1886 else {
1887 if (lines & 1)
1888 ++addone;
1889 lines /= 2;
1894 * open /dev/tty if we are to pause before each page
1895 * but only if stdout is a terminal and stdin is not a terminal
1897 if (ttyout && pgpause && !isatty(fileno(stdin))) {
1898 if ((ttyinf = fopen("/dev/tty", "r")) == NULL) {
1899 (void)fprintf(errf, "pr: cannot open terminal\n");
1900 return(1);
1904 return(0);