retire unstack & datasizes in favour of addr2line
[minix3.git] / usr.bin / pr / pr.c
blob91e80752713cf709acfeebde1b2fc3689a8555b6
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 (nmwd)
191 num = nmwd + 1;
192 else
193 num = 0;
194 off = num + offst;
197 * allocate line buffer
199 if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL)
200 goto oomem;
202 * allocate header buffer
204 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
205 goto oomem;
207 ohbuf = hbuf + offst;
208 nbuf = obuf + offst;
209 lbuf = nbuf + num;
210 if (num)
211 nbuf[--num] = nmchar;
212 if (offst) {
213 (void)memset(obuf, (int)' ', offst);
214 (void)memset(hbuf, (int)' ', offst);
218 * loop by file
220 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
221 if (pgnm) {
223 * skip to specified page
225 if (inskip(inf, pgnm, lines))
226 continue;
227 pagecnt = pgnm;
228 } else
229 pagecnt = 1;
230 lncnt = 0;
233 * loop by page
235 for(;;) {
236 linecnt = 0;
237 lrgln = 0;
238 ops = 0;
239 ips = 0;
240 cps = 0;
243 * loop by line
245 while (linecnt < lines) {
247 * input next line
249 if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
250 break;
251 if (!linecnt) {
252 if (pgpause)
253 prpause(pagecnt);
255 if (!nohead &&
256 prhead(hbuf, fname, pagecnt))
257 goto out;
261 * start of new line.
263 if (!lrgln) {
264 if (num)
265 addnum(nbuf, num, ++lncnt);
266 if (otln(obuf,cnt+off, &ips, &ops, mor))
267 goto out;
268 } else if (otln(lbuf, cnt, &ips, &ops, mor))
269 goto out;
272 * if line bigger than buffer, get more
274 if (mor) {
275 lrgln = 1;
276 continue;
280 * whole line rcvd. reset tab proc. state
282 ++linecnt;
283 lrgln = 0;
284 ops = 0;
285 ips = 0;
289 * fill to end of page
291 if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
292 goto out;
295 * On EOF go to next file
297 if (cnt < 0)
298 break;
299 ++pagecnt;
301 if (inf != stdin)
302 (void)fclose(inf);
304 if (eoptind < argc)
305 goto out;
306 error = 0;
307 goto out;
308 oomem:
309 mfail();
310 out:
311 free(obuf);
312 free(hbuf);
313 if (inf != NULL && inf != stdin)
314 (void)fclose(inf);
315 return error;
319 * vertcol: print files with more than one column of output down a page
321 static int
322 vertcol(int argc, char *argv[])
324 char *ptbf;
325 char **lstdat = NULL;
326 int i;
327 int j;
328 int cnt = -1;
329 int pln;
330 int *indy = NULL;
331 int cvc;
332 int *lindy = NULL;
333 int lncnt;
334 int stp;
335 int pagecnt;
336 int col = colwd + 1;
337 int mxlen = pgwd + offst + 1;
338 int mclcnt = clcnt - 1;
339 struct vcol *vc = NULL;
340 int mvc;
341 int tvc;
342 int cw = nmwd + 1;
343 int fullcol;
344 char *buf = NULL;
345 char *hbuf = NULL;
346 char *ohbuf;
347 const char *fname;
348 FILE *inf = NULL;
349 int ips = 0;
350 int cps = 0;
351 int ops = 0;
352 int mor = 0;
353 int error = 1;
356 * allocate page buffer
358 if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL)
359 goto oomem;
362 * allocate page header
364 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
365 goto oomem;
366 ohbuf = hbuf + offst;
367 if (offst)
368 (void)memset(hbuf, (int)' ', offst);
371 * col pointers when no headers
373 mvc = lines * clcnt;
374 if ((vc = malloc((unsigned)mvc*sizeof(struct vcol))) == NULL)
375 goto oomem;
378 * pointer into page where last data per line is located
380 if ((lstdat = malloc((unsigned)lines*sizeof(char *))) == NULL)
381 goto oomem;
384 * fast index lookups to locate start of lines
386 if ((indy = malloc((unsigned)lines*sizeof(int))) == NULL)
387 goto oomem;
388 if ((lindy = malloc((unsigned)lines*sizeof(int))) == NULL)
389 goto oomem;
391 if (nmwd)
392 fullcol = col + cw;
393 else
394 fullcol = col;
397 * initialize buffer lookup indexes and offset area
399 for (j = 0; j < lines; ++j) {
400 lindy[j] = j * mxlen;
401 indy[j] = lindy[j] + offst;
402 if (offst) {
403 ptbf = buf + lindy[j];
404 (void)memset(ptbf, (int)' ', offst);
405 ptbf += offst;
406 } else
407 ptbf = buf + indy[j];
408 lstdat[j] = ptbf;
412 * loop by file
414 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
415 if (pgnm) {
417 * skip to requested page
419 if (inskip(inf, pgnm, lines))
420 continue;
421 pagecnt = pgnm;
422 } else
423 pagecnt = 1;
424 lncnt = 0;
427 * loop by page
429 for(;;) {
431 * loop by column
433 cvc = 0;
434 for (i = 0; i < clcnt; ++i) {
435 j = 0;
437 * if last column, do not pad
439 if (i == mclcnt)
440 stp = 1;
441 else
442 stp = 0;
444 * loop by line
446 for(;;) {
448 * is this first column
450 if (!i) {
451 ptbf = buf + indy[j];
452 lstdat[j] = ptbf;
453 } else
454 ptbf = lstdat[j];
455 vc[cvc].pt = ptbf;
458 * add number
460 if (nmwd) {
461 addnum(ptbf, nmwd, ++lncnt);
462 ptbf += nmwd;
463 *ptbf++ = nmchar;
467 * input next line
469 cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
470 vc[cvc++].cnt = cnt;
471 if (cnt < 0)
472 break;
473 ptbf += cnt;
476 * pad all but last column on page
478 if (!stp) {
480 * pad to end of column
482 if (sflag)
483 *ptbf++ = schar;
484 else if ((pln = col-cnt) > 0) {
485 (void)memset(ptbf,
486 (int)' ',pln);
487 ptbf += pln;
491 * remember last char in line
493 lstdat[j] = ptbf;
494 if (++j >= lines)
495 break;
497 if (cnt < 0)
498 break;
502 * when -t (no header) is specified the spec requires
503 * the min number of lines. The last page may not have
504 * balanced length columns. To fix this we must reorder
505 * the columns. This is a very slow technique so it is
506 * only used under limited conditions. Without -t, the
507 * balancing of text columns is unspecified. To NOT
508 * balance the last page, add the global variable
509 * nohead to the if statement below e.g.
511 * if ((cnt < 0) && nohead && cvc ......
513 --cvc;
516 * check to see if last page needs to be reordered
518 if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
519 pln = cvc/clcnt;
520 if (cvc % clcnt)
521 ++pln;
523 if (pgpause)
524 prpause(pagecnt);
527 * print header
529 if (!nohead && prhead(hbuf, fname, pagecnt))
530 goto out;
531 for (i = 0; i < pln; ++i) {
532 ips = 0;
533 ops = 0;
534 if (offst&& otln(buf,offst,&ips,&ops,1)) {
535 error = 1;
536 goto out;
538 tvc = i;
540 for (j = 0; j < clcnt; ++j) {
542 * determine column length
544 if (j == mclcnt) {
546 * last column
548 cnt = vc[tvc].cnt;
549 if (nmwd)
550 cnt += cw;
551 } else if (sflag) {
553 * single ch between
555 cnt = vc[tvc].cnt + 1;
556 if (nmwd)
557 cnt += cw;
558 } else
559 cnt = fullcol;
560 if (otln(vc[tvc].pt, cnt, &ips,
561 &ops, 1))
562 goto out;
563 tvc += pln;
564 if (tvc >= cvc)
565 break;
568 * terminate line
570 if (otln(buf, 0, &ips, &ops, 0))
571 goto out;
574 * pad to end of page
576 if (prtail((lines - pln), 0))
577 goto out;
579 * done with output, go to next file
581 break;
585 * determine how many lines to output
587 if (i > 0)
588 pln = lines;
589 else
590 pln = j;
593 * print header
595 if (pln) {
596 if (pgpause)
597 prpause(pagecnt);
599 if (!nohead && prhead(hbuf, fname, pagecnt))
600 goto out;
604 * output each line
606 for (i = 0; i < pln; ++i) {
607 ptbf = buf + lindy[i];
608 if ((j = lstdat[i] - ptbf) <= offst)
609 break;
610 if (otln(ptbf, j, &ips, &ops, 0))
611 goto out;
615 * pad to end of page
617 if (pln && prtail((lines - pln), 0))
618 goto out;
621 * if EOF go to next file
623 if (cnt < 0)
624 break;
625 ++pagecnt;
627 if (inf != stdin)
628 (void)fclose(inf);
630 if (eoptind < argc)
631 goto out;
632 error = 0;
633 goto out;
634 oomem:
635 mfail();
636 out:
637 free(buf);
638 free(hbuf);
639 free(vc);
640 free(lstdat);
641 free(lindy);
642 if (inf != NULL && inf != stdin)
643 (void)fclose(inf);
644 return error;
648 * horzcol: print files with more than one column of output across a page
650 static int
651 horzcol(int argc, char *argv[])
653 char *ptbf;
654 int pln;
655 int cnt = -1;
656 char *lstdat;
657 int col = colwd + 1;
658 int j;
659 int i;
660 int lncnt;
661 int pagecnt;
662 char *buf = NULL;
663 char *hbuf = NULL;
664 char *ohbuf;
665 const char *fname;
666 FILE *inf = NULL;
667 int ips = 0;
668 int cps = 0;
669 int ops = 0;
670 int mor = 0;
671 int error = 1;
673 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
674 goto oomem;
677 * page header
679 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
680 goto oomem;
681 ohbuf = hbuf + offst;
682 if (offst) {
683 (void)memset(buf, (int)' ', offst);
684 (void)memset(hbuf, (int)' ', offst);
688 * loop by file
690 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
691 if (pgnm) {
692 if (inskip(inf, pgnm, lines))
693 continue;
694 pagecnt = pgnm;
695 } else
696 pagecnt = 1;
697 lncnt = 0;
700 * loop by page
702 for(;;) {
704 * loop by line
706 for (i = 0; i < lines; ++i) {
707 ptbf = buf + offst;
708 lstdat = ptbf;
709 j = 0;
711 * loop by col
713 for(;;) {
714 if (nmwd) {
716 * add number to column
718 addnum(ptbf, nmwd, ++lncnt);
719 ptbf += nmwd;
720 *ptbf++ = nmchar;
723 * input line
725 if ((cnt = inln(inf,ptbf,colwd,&cps,1,
726 &mor)) < 0)
727 break;
728 ptbf += cnt;
729 lstdat = ptbf;
732 * if last line skip padding
734 if (++j >= clcnt)
735 break;
738 * pad to end of column
740 if (sflag)
741 *ptbf++ = schar;
742 else if ((pln = col - cnt) > 0) {
743 (void)memset(ptbf,(int)' ',pln);
744 ptbf += pln;
749 * determine line length
751 if ((j = lstdat - buf) <= offst)
752 break;
753 if (!i) {
754 if (pgpause)
755 prpause(pagecnt);
757 if (!nohead &&
758 prhead(hbuf, fname, pagecnt))
759 goto out;
762 * output line
764 if (otln(buf, j, &ips, &ops, 0))
765 goto out;
769 * pad to end of page
771 if (i && prtail(lines-i, 0))
772 goto out;
775 * if EOF go to next file
777 if (cnt < 0)
778 break;
779 ++pagecnt;
781 if (inf != stdin)
782 (void)fclose(inf);
784 if (eoptind < argc)
785 goto out;
786 error = 0;
787 goto out;
788 oomem:
789 mfail();
790 out:
791 free(buf);
792 free(hbuf);
793 if (inf != NULL && inf != stdin)
794 (void)fclose(inf);
795 return error;
799 * mulfile: print files with more than one column of output and
800 * more than one file concurrently
802 static int
803 mulfile(int argc, char *argv[])
805 char *ptbf;
806 int j;
807 int pln;
808 int cnt;
809 char *lstdat;
810 int i;
811 FILE **fbuf = NULL;
812 int actf;
813 int lncnt;
814 int col;
815 int pagecnt;
816 int fproc;
817 char *buf = NULL;
818 char *hbuf = NULL;
819 char *ohbuf;
820 const char *fname;
821 int ips = 0;
822 int cps = 0;
823 int ops = 0;
824 int mor = 0;
825 int error = 1;
828 * array of FILE *, one for each operand
830 if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL)
831 goto oomem;
834 * page header
836 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
837 goto oomem;
838 ohbuf = hbuf + offst;
841 * do not know how many columns yet. The number of operands provide an
842 * upper bound on the number of columns. We use the number of files
843 * we can open successfully to set the number of columns. The operation
844 * of the merge operation (-m) in relation to unsuccesful file opens
845 * is unspecified by posix.
847 j = 0;
848 while (j < clcnt) {
849 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
850 break;
851 if (pgnm && (inskip(fbuf[j], pgnm, lines)))
852 fbuf[j] = NULL;
853 ++j;
857 * if no files, exit
859 if (!j)
860 goto out;
863 * calculate page boundries based on open file count
865 clcnt = j;
866 if (nmwd) {
867 colwd = (pgwd - clcnt - nmwd)/clcnt;
868 pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
869 } else {
870 colwd = (pgwd + 1 - clcnt)/clcnt;
871 pgwd = ((colwd + 1) * clcnt) - 1;
873 if (colwd < 1) {
874 (void)fprintf(errf,
875 "pr: page width too small for %d columns\n", clcnt);
876 goto out;
878 actf = clcnt;
879 col = colwd + 1;
882 * line buffer
884 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
885 goto out;
886 if (offst) {
887 (void)memset(buf, (int)' ', offst);
888 (void)memset(hbuf, (int)' ', offst);
890 if (pgnm)
891 pagecnt = pgnm;
892 else
893 pagecnt = 1;
894 lncnt = 0;
897 * continue to loop while any file still has data
899 while (actf > 0) {
901 * loop by line
903 for (i = 0; i < lines; ++i) {
904 ptbf = buf + offst;
905 lstdat = ptbf;
906 if (nmwd) {
908 * add line number to line
910 addnum(ptbf, nmwd, ++lncnt);
911 ptbf += nmwd;
912 *ptbf++ = nmchar;
914 j = 0;
915 fproc = 0;
918 * loop by column
920 for (j = 0; j < clcnt; ++j) {
921 if (fbuf[j] == NULL) {
923 * empty column; EOF
925 cnt = 0;
926 } else if ((cnt = inln(fbuf[j], ptbf, colwd,
927 &cps, 1, &mor)) < 0) {
929 * EOF hit; no data
931 if (fbuf[j] != stdin)
932 (void)fclose(fbuf[j]);
933 fbuf[j] = NULL;
934 --actf;
935 cnt = 0;
936 } else {
938 * process file data
940 ptbf += cnt;
941 lstdat = ptbf;
942 fproc++;
946 * if last ACTIVE column, done with line
948 if (fproc >= actf)
949 break;
952 * pad to end of column
954 if (sflag) {
955 *ptbf++ = schar;
956 } else if ((pln = col - cnt) > 0) {
957 (void)memset(ptbf, (int)' ', pln);
958 ptbf += pln;
963 * calculate data in line
965 if ((j = lstdat - buf) <= offst)
966 break;
968 if (!i) {
969 if (pgpause)
970 prpause(pagecnt);
972 if (!nohead && prhead(hbuf, fname, pagecnt))
973 goto out;
977 * output line
979 if (otln(buf, j, &ips, &ops, 0))
980 goto out;
983 * if no more active files, done
985 if (actf <= 0) {
986 ++i;
987 break;
992 * pad to end of page
994 if (i && prtail(lines-i, 0))
995 goto out;
996 ++pagecnt;
998 if (eoptind < argc)
999 goto out;
1000 error = 0;
1001 goto out;
1002 oomem:
1003 mfail();
1004 out:
1005 if (fbuf) {
1006 for (j = 0; j < clcnt; j++)
1007 if (fbuf[j] && fbuf[j] != stdin)
1008 (void)fclose(fbuf[j]);
1009 free(fbuf);
1011 free(hbuf);
1012 free(buf);
1013 return error;
1017 * inln(): input a line of data (unlimited length lines supported)
1018 * Input is optionally expanded to spaces
1020 * inf: file
1021 * buf: buffer
1022 * lim: buffer length
1023 * cps: column positon 1st char in buffer (large line support)
1024 * trnc: throw away data more than lim up to \n
1025 * mor: set if more data in line (not truncated)
1027 static int
1028 inln(FILE *inf, char *buf, int lim, int *cps, int trnc, int *mor)
1030 int col;
1031 int gap = ingap;
1032 int ch = EOF;
1033 char *ptbuf;
1034 int chk = (int)inchar;
1036 ptbuf = buf;
1038 if (gap) {
1040 * expanding input option
1042 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1044 * is this the input "tab" char
1046 if (ch == chk) {
1048 * expand to number of spaces
1050 col = (ptbuf - buf) + *cps;
1051 col = gap - (col % gap);
1054 * if more than this line, push back
1056 if ((col > lim) && (ungetc(ch, inf) == EOF))
1057 return(1);
1060 * expand to spaces
1062 while ((--col >= 0) && (--lim >= 0))
1063 *ptbuf++ = ' ';
1064 continue;
1066 if (ch == '\n')
1067 break;
1068 *ptbuf++ = ch;
1070 } else {
1072 * no expansion
1074 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1075 if (ch == '\n')
1076 break;
1077 *ptbuf++ = ch;
1080 col = ptbuf - buf;
1081 if (ch == EOF) {
1082 *mor = 0;
1083 *cps = 0;
1084 if (!col)
1085 return(-1);
1086 return(col);
1088 if (ch == '\n') {
1090 * entire line processed
1092 *mor = 0;
1093 *cps = 0;
1094 return(col);
1098 * line was larger than limit
1100 if (trnc) {
1102 * throw away rest of line
1104 while ((ch = getc(inf)) != EOF) {
1105 if (ch == '\n')
1106 break;
1108 *cps = 0;
1109 *mor = 0;
1110 } else {
1112 * save column offset if not truncated
1114 *cps += col;
1115 *mor = 1;
1118 return(col);
1122 * otln(): output a line of data. (Supports unlimited length lines)
1123 * output is optionally contracted to tabs
1125 * buf: output buffer with data
1126 * cnt: number of chars of valid data in buf
1127 * svips: buffer input column position (for large lines)
1128 * svops: buffer output column position (for large lines)
1129 * mor: output line not complete in this buf; more data to come.
1130 * 1 is more, 0 is complete, -1 is no \n's
1132 static int
1133 otln(char *buf, int cnt, int *svips, int *svops, int mor)
1135 int ops; /* last col output */
1136 int ips; /* last col in buf examined */
1137 int gap = ogap;
1138 int tbps;
1139 char *endbuf;
1141 if (ogap) {
1143 * contracting on output
1145 endbuf = buf + cnt;
1146 ops = *svops;
1147 ips = *svips;
1148 while (buf < endbuf) {
1150 * count number of spaces and ochar in buffer
1152 if (*buf == ' ') {
1153 ++ips;
1154 ++buf;
1155 continue;
1159 * simulate ochar processing
1161 if (*buf == ochar) {
1162 ips += gap - (ips % gap);
1163 ++buf;
1164 continue;
1168 * got a non space char; contract out spaces
1170 while (ips - ops > 1) {
1172 * use as many ochar as will fit
1174 if ((tbps = ops + gap - (ops % gap)) > ips)
1175 break;
1176 if (putchar(ochar) == EOF) {
1177 pfail();
1178 return(1);
1180 ops = tbps;
1183 while (ops < ips) {
1185 * finish off with spaces
1187 if (putchar(' ') == EOF) {
1188 pfail();
1189 return(1);
1191 ++ops;
1195 * output non space char
1197 if (putchar(*buf++) == EOF) {
1198 pfail();
1199 return(1);
1201 ++ips;
1202 ++ops;
1205 if (mor > 0) {
1207 * if incomplete line, save position counts
1209 *svops = ops;
1210 *svips = ips;
1211 return(0);
1214 if (mor < 0) {
1215 while (ips - ops > 1) {
1217 * use as many ochar as will fit
1219 if ((tbps = ops + gap - (ops % gap)) > ips)
1220 break;
1221 if (putchar(ochar) == EOF) {
1222 pfail();
1223 return(1);
1225 ops = tbps;
1227 while (ops < ips) {
1229 * finish off with spaces
1231 if (putchar(' ') == EOF) {
1232 pfail();
1233 return(1);
1235 ++ops;
1237 return(0);
1239 } else {
1241 * output is not contracted
1243 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1244 pfail();
1245 return(1);
1247 if (mor != 0)
1248 return(0);
1252 * process line end and double space as required
1254 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1255 pfail();
1256 return(1);
1258 return(0);
1262 * inskip(): skip over pgcnt pages with lncnt lines per page
1263 * file is closed at EOF (if not stdin).
1265 * inf FILE * to read from
1266 * pgcnt number of pages to skip
1267 * lncnt number of lines per page
1269 static int
1270 inskip(FILE *inf, int pgcnt, int lncnt)
1272 int c;
1273 int cnt;
1275 while(--pgcnt > 0) {
1276 cnt = lncnt;
1277 while ((c = getc(inf)) != EOF) {
1278 if ((c == '\n') && (--cnt == 0))
1279 break;
1281 if (c == EOF) {
1282 if (inf != stdin)
1283 (void)fclose(inf);
1284 return(1);
1287 return(0);
1291 * nxtfile: returns a FILE * to next file in arg list and sets the
1292 * time field for this file (or current date).
1294 * buf array to store proper date for the header.
1295 * dt if set skips the date processing (used with -m)
1297 static FILE *
1298 nxtfile(int argc, char **argv, const char **fname, char *buf, int dt)
1300 FILE *inf = NULL;
1301 struct timeval tv;
1302 struct timezone tz;
1303 struct tm *timeptr = NULL;
1304 struct stat statbuf;
1305 time_t curtime;
1306 static int twice = -1;
1308 ++twice;
1309 if (eoptind >= argc) {
1311 * no file listed; default, use standard input
1313 if (twice)
1314 return(NULL);
1315 clearerr(stdin);
1316 inf = stdin;
1317 if (header != NULL)
1318 *fname = header;
1319 else
1320 *fname = FNAME;
1321 if (nohead)
1322 return(inf);
1323 if (gettimeofday(&tv, &tz) < 0) {
1324 ++errcnt;
1325 (void)fprintf(errf, "pr: cannot get time of day, %s\n",
1326 strerror(errno));
1327 eoptind = argc - 1;
1328 return(NULL);
1330 curtime = tv.tv_sec;
1331 timeptr = localtime(&curtime);
1333 for (; eoptind < argc; ++eoptind) {
1334 if (strcmp(argv[eoptind], "-") == 0) {
1336 * process a "-" for filename
1338 clearerr(stdin);
1339 inf = stdin;
1340 if (header != NULL)
1341 *fname = header;
1342 else
1343 *fname = FNAME;
1344 ++eoptind;
1345 if (nohead || (dt && twice))
1346 return(inf);
1347 if (gettimeofday(&tv, &tz) < 0) {
1348 ++errcnt;
1349 (void)fprintf(errf,
1350 "pr: cannot get time of day, %s\n",
1351 strerror(errno));
1352 return(NULL);
1354 curtime = tv.tv_sec;
1355 timeptr = localtime(&curtime);
1356 } else {
1358 * normal file processing
1360 if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1361 ++errcnt;
1362 if (nodiag)
1363 continue;
1364 (void)fprintf(errf, "pr: Cannot open %s, %s\n",
1365 argv[eoptind], strerror(errno));
1366 continue;
1368 if (header != NULL)
1369 *fname = header;
1370 else if (dt)
1371 *fname = FNAME;
1372 else
1373 *fname = argv[eoptind];
1374 ++eoptind;
1375 if (nohead || (dt && twice))
1376 return(inf);
1378 if (dt) {
1379 if (gettimeofday(&tv, &tz) < 0) {
1380 ++errcnt;
1381 (void)fprintf(errf,
1382 "pr: cannot get time of day, %s\n",
1383 strerror(errno));
1384 return(NULL);
1386 curtime = tv.tv_sec;
1387 timeptr = localtime(&curtime);
1388 } else {
1389 if (fstat(fileno(inf), &statbuf) < 0) {
1390 ++errcnt;
1391 (void)fclose(inf);
1392 (void)fprintf(errf,
1393 "pr: Cannot stat %s, %s\n",
1394 argv[eoptind], strerror(errno));
1395 return(NULL);
1397 timeptr = localtime(&(statbuf.st_mtime));
1400 break;
1402 if (inf == NULL)
1403 return(NULL);
1406 * set up time field used in header
1408 if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1409 ++errcnt;
1410 if (inf != stdin)
1411 (void)fclose(inf);
1412 (void)fputs("pr: time conversion failed\n", errf);
1413 return(NULL);
1415 return(inf);
1419 * addnum(): adds the line number to the column
1420 * Truncates from the front or pads with spaces as required.
1421 * Numbers are right justified.
1423 * buf buffer to store the number
1424 * wdth width of buffer to fill
1425 * line line number
1427 * NOTE: numbers occupy part of the column. The posix
1428 * spec does not specify if -i processing should or should not
1429 * occur on number padding. The spec does say it occupies
1430 * part of the column. The usage of addnum currently treats
1431 * numbers as part of the column so spaces may be replaced.
1433 void
1434 addnum(char *buf, int wdth, int line)
1436 char *pt = buf + wdth;
1438 do {
1439 *--pt = digs[line % 10];
1440 line /= 10;
1441 } while (line && (pt > buf));
1444 * pad with space as required
1446 while (pt > buf)
1447 *--pt = ' ';
1451 * prpause(): pause before printing each page
1453 * pagcnt page number
1455 static void
1456 prpause(int pagcnt)
1459 if (ttyout) {
1460 int c;
1462 (void)putc('\a', stderr);
1463 (void)fflush(stderr);
1465 while ((c = getc(ttyinf)) != '\n' && c != EOF)
1469 * pause ONLY before first page of first file
1471 if (pgpause == FIRSTPAGE && pagcnt == 1)
1472 pgpause = NO_PAUSE;
1477 * prhead(): prints the top of page header
1479 * buf buffer with time field (and offset)
1480 * cnt number of chars in buf
1481 * fname fname field for header
1482 * pagcnt page number
1484 static int
1485 prhead(char *buf, const char *fname, int pagcnt)
1487 int ips = 0;
1488 int ops = 0;
1490 if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1491 pfail();
1492 return(1);
1495 * posix is not clear if the header is subject to line length
1496 * restrictions. The specification for header line format
1497 * in the spec clearly does not limit length. No pr currently
1498 * restricts header length. However if we need to truncate in
1499 * an reasonable way, adjust the length of the printf by
1500 * changing HDFMT to allow a length max as an argument printf.
1501 * buf (which contains the offset spaces and time field could
1502 * also be trimmed
1504 * note only the offset (if any) is processed for tab expansion
1506 if (offst && otln(buf, offst, &ips, &ops, -1))
1507 return(1);
1508 (void)printf(HDFMT,buf+offst, fname, pagcnt);
1509 return(0);
1513 * prtail(): pad page with empty lines (if required) and print page trailer
1514 * if requested
1516 * cnt number of lines of padding needed
1517 * incomp was a '\n' missing from last line output
1519 static int
1520 prtail(int cnt, int incomp)
1522 if (nohead) {
1524 * only pad with no headers when incomplete last line
1526 if (!incomp)
1527 return(0);
1528 if ((dspace && (putchar('\n') == EOF)) ||
1529 (putchar('\n') == EOF)) {
1530 pfail();
1531 return(1);
1533 return(0);
1537 * if double space output two \n
1539 if (dspace)
1540 cnt *= 2;
1543 * if an odd number of lines per page, add an extra \n
1545 if (addone)
1546 ++cnt;
1549 * pad page
1551 if (formfeed) {
1552 if ((incomp && (putchar('\n') == EOF)) ||
1553 (putchar('\f') == EOF)) {
1554 pfail();
1555 return(1);
1557 return(0);
1559 cnt += TAILLEN;
1560 while (--cnt >= 0) {
1561 if (putchar('\n') == EOF) {
1562 pfail();
1563 return(1);
1566 return(0);
1570 * terminate(): when a SIGINT is recvd
1572 static void
1573 terminate(int which_sig)
1575 flsh_errs();
1576 (void)raise_default_signal(which_sig);
1577 exit(1);
1582 * flsh_errs(): output saved up diagnostic messages after all normal
1583 * processing has completed
1585 static void
1586 flsh_errs(void)
1588 char buf[BUFSIZ];
1590 (void)fflush(stdout);
1591 (void)fflush(errf);
1592 if (errf == stderr)
1593 return;
1594 rewind(errf);
1595 while (fgets(buf, BUFSIZ, errf) != NULL)
1596 (void)fputs(buf, stderr);
1599 static void
1600 mfail(void)
1602 (void)fputs("pr: memory allocation failed\n", errf);
1605 static void
1606 pfail(void)
1608 (void)fprintf(errf, "pr: write failure, %s\n", strerror(errno));
1611 static void
1612 usage(void)
1614 (void)fputs(
1615 "usage: pr [+page] [-col] [-adFfmprt] [-e[ch][gap]] [-h header]\n",
1616 errf);
1617 (void)fputs(
1618 " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",
1619 errf);
1620 (void)fputs(
1621 " [-s[ch]] [-T timefmt] [-w width] [-] [file ...]\n",
1622 errf);
1626 * setup: Validate command args, initialize and perform sanity
1627 * checks on options
1629 static int
1630 setup(int argc, char **argv)
1632 int c;
1633 int eflag = 0;
1634 int iflag = 0;
1635 int wflag = 0;
1636 int cflag = 0;
1638 ttyinf = stdin;
1640 if (isatty(fileno(stdout))) {
1642 * defer diagnostics until processing is done
1644 if ((errf = tmpfile()) == NULL) {
1645 (void)fputs("Cannot defer diagnostic messages\n",stderr);
1646 return(1);
1648 ttyout = 1;
1649 } else
1650 errf = stderr;
1651 while ((c = egetopt(argc, argv, "#adFfmrte?h:i?l:n?o:ps?T:w:")) != -1) {
1652 switch (c) {
1653 case '+':
1654 if ((pgnm = atoi(eoptarg)) < 1) {
1655 (void)fputs("pr: +page number must be 1 or more\n",
1656 errf);
1657 return(1);
1659 break;
1660 case '-':
1661 if ((clcnt = atoi(eoptarg)) < 1) {
1662 (void)fputs("pr: -columns must be 1 or more\n",errf);
1663 return(1);
1665 if (clcnt > 1)
1666 ++cflag;
1667 break;
1668 case 'a':
1669 ++across;
1670 break;
1671 case 'd':
1672 ++dspace;
1673 break;
1674 case 'e':
1675 ++eflag;
1676 if ((eoptarg != NULL) &&
1677 !isdigit((unsigned char)*eoptarg))
1678 inchar = *eoptarg++;
1679 else
1680 inchar = INCHAR;
1681 if ((eoptarg != NULL) &&
1682 isdigit((unsigned char)*eoptarg)) {
1683 if ((ingap = atoi(eoptarg)) < 0) {
1684 (void)fputs(
1685 "pr: -e gap must be 0 or more\n", errf);
1686 return(1);
1688 if (ingap == 0)
1689 ingap = INGAP;
1690 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1691 (void)fprintf(errf,
1692 "pr: invalid value for -e %s\n", eoptarg);
1693 return(1);
1694 } else
1695 ingap = INGAP;
1696 break;
1697 case 'f':
1698 pgpause |= FIRSTPAGE;
1699 /*FALLTHROUGH*/
1700 case 'F':
1701 ++formfeed;
1702 break;
1703 case 'h':
1704 header = eoptarg;
1705 break;
1706 case 'i':
1707 ++iflag;
1708 if ((eoptarg != NULL) &&
1709 !isdigit((unsigned char)*eoptarg))
1710 ochar = *eoptarg++;
1711 else
1712 ochar = OCHAR;
1713 if ((eoptarg != NULL) &&
1714 isdigit((unsigned char)*eoptarg)) {
1715 if ((ogap = atoi(eoptarg)) < 0) {
1716 (void)fputs(
1717 "pr: -i gap must be 0 or more\n", errf);
1718 return(1);
1720 if (ogap == 0)
1721 ogap = OGAP;
1722 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1723 (void)fprintf(errf,
1724 "pr: invalid value for -i %s\n", eoptarg);
1725 return(1);
1726 } else
1727 ogap = OGAP;
1728 break;
1729 case 'l':
1730 if (!isdigit((unsigned char)*eoptarg) ||
1731 ((lines=atoi(eoptarg)) < 1)) {
1732 (void)fputs(
1733 "pr: Number of lines must be 1 or more\n",errf);
1734 return(1);
1736 break;
1737 case 'm':
1738 ++merge;
1739 break;
1740 case 'n':
1741 if ((eoptarg != NULL) &&
1742 !isdigit((unsigned char)*eoptarg))
1743 nmchar = *eoptarg++;
1744 else
1745 nmchar = NMCHAR;
1746 if ((eoptarg != NULL) &&
1747 isdigit((unsigned char)*eoptarg)) {
1748 if ((nmwd = atoi(eoptarg)) < 1) {
1749 (void)fputs(
1750 "pr: -n width must be 1 or more\n",errf);
1751 return(1);
1753 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1754 (void)fprintf(errf,
1755 "pr: invalid value for -n %s\n", eoptarg);
1756 return(1);
1757 } else
1758 nmwd = NMWD;
1759 break;
1760 case 'o':
1761 if (!isdigit((unsigned char)*eoptarg) ||
1762 ((offst = atoi(eoptarg))< 1)){
1763 (void)fputs("pr: -o offset must be 1 or more\n",
1764 errf);
1765 return(1);
1767 break;
1768 case 'p':
1769 pgpause |= EACHPAGE;
1770 break;
1771 case 'r':
1772 ++nodiag;
1773 break;
1774 case 's':
1775 ++sflag;
1776 if (eoptarg == NULL)
1777 schar = SCHAR;
1778 else
1779 schar = *eoptarg++;
1780 if (eoptarg && *eoptarg != '\0') {
1781 (void)fprintf(errf,
1782 "pr: invalid value for -s %s\n", eoptarg);
1783 return(1);
1785 break;
1786 case 'T':
1787 timefrmt = eoptarg;
1788 break;
1789 case 't':
1790 ++nohead;
1791 break;
1792 case 'w':
1793 ++wflag;
1794 if (!isdigit((unsigned char)*eoptarg) ||
1795 ((pgwd = atoi(eoptarg)) < 1)){
1796 (void)fputs(
1797 "pr: -w width must be 1 or more \n",errf);
1798 return(1);
1800 break;
1801 case '?':
1802 default:
1803 return(1);
1808 * default and sanity checks
1810 if (!clcnt) {
1811 if (merge) {
1812 if ((clcnt = argc - eoptind) <= 1) {
1813 clcnt = CLCNT;
1814 merge = 0;
1816 } else
1817 clcnt = CLCNT;
1819 if (across) {
1820 if (clcnt == 1) {
1821 (void)fputs("pr: -a flag requires multiple columns\n",
1822 errf);
1823 return(1);
1825 if (merge) {
1826 (void)fputs("pr: -m cannot be used with -a\n", errf);
1827 return(1);
1830 if (!wflag) {
1831 if (sflag)
1832 pgwd = SPGWD;
1833 else
1834 pgwd = PGWD;
1836 if (cflag || merge) {
1837 if (!eflag) {
1838 inchar = INCHAR;
1839 ingap = INGAP;
1841 if (!iflag) {
1842 ochar = OCHAR;
1843 ogap = OGAP;
1846 if (cflag) {
1847 if (merge) {
1848 (void)fputs(
1849 "pr: -m cannot be used with multiple columns\n", errf);
1850 return(1);
1852 if (nmwd) {
1853 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1854 pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1855 } else {
1856 colwd = (pgwd + 1 - clcnt)/clcnt;
1857 pgwd = ((colwd + 1) * clcnt) - 1;
1859 if (colwd < 1) {
1860 (void)fprintf(errf,
1861 "pr: page width is too small for %d columns\n",clcnt);
1862 return(1);
1865 if (!lines)
1866 lines = LINES;
1869 * make sure long enough for headers. if not disable
1871 if (lines <= HEADLEN + TAILLEN)
1872 ++nohead;
1873 else if (!nohead)
1874 lines -= HEADLEN + TAILLEN;
1877 * adjust for double space on odd length pages
1879 if (dspace) {
1880 if (lines == 1)
1881 dspace = 0;
1882 else {
1883 if (lines & 1)
1884 ++addone;
1885 lines /= 2;
1890 * open /dev/tty if we are to pause before each page
1891 * but only if stdout is a terminal and stdin is not a terminal
1893 if (ttyout && pgpause && !isatty(fileno(stdin))) {
1894 if ((ttyinf = fopen("/dev/tty", "r")) == NULL) {
1895 (void)fprintf(errf, "pr: cannot open terminal\n");
1896 return(1);
1900 return(0);