Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.bin / pr / pr.c
blob2a4e83ce7205f5cbb34d997c5161cf7a20fd0b8d
1 /* $NetBSD: pr.c,v 1.19 2008/07/21 14:19:24 lukem Exp $ */
3 /*-
4 * Copyright (c) 1991 Keith Muller.
5 * Copyright (c) 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Keith Muller of the University of California, San Diego.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1993\
39 The Regents of the University of California. All rights reserved.");
40 #endif /* not lint */
42 #ifndef lint
43 #if 0
44 from: static char sccsid[] = "@(#)pr.c 8.1 (Berkeley) 6/6/93";
45 #else
46 __RCSID("$NetBSD: pr.c,v 1.19 2008/07/21 14:19:24 lukem Exp $");
47 #endif
48 #endif /* not lint */
50 #include <sys/types.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <time.h>
61 #include <unistd.h>
62 #include <util.h>
64 #include "pr.h"
65 #include "extern.h"
68 * pr: a printing and pagination filter. If multiple input files
69 * are specified, each is read, formatted, and written to standard
70 * output. By default, input is separated into 66-line pages, each
71 * with a header that includes the page number, date, time and the
72 * files pathname.
74 * Complies with posix P1003.2/D11
78 * parameter variables
80 int pgnm; /* starting page number */
81 int clcnt; /* number of columns */
82 int colwd; /* column data width - multiple columns */
83 int across; /* mult col flag; write across page */
84 int dspace; /* double space flag */
85 char inchar; /* expand input char */
86 int ingap; /* expand input gap */
87 int formfeed; /* use formfeed as trailer */
88 char *header; /* header name instead of file name */
89 char ochar; /* contract output char */
90 int ogap; /* contract output gap */
91 int lines; /* number of lines per page */
92 int merge; /* merge multiple files in output */
93 char nmchar; /* line numbering append char */
94 int nmwd; /* width of line number field */
95 int offst; /* number of page offset spaces */
96 int nodiag; /* do not report file open errors */
97 char schar; /* text column separation character */
98 int sflag; /* -s option for multiple columns */
99 int nohead; /* do not write head and trailer */
100 int pgwd; /* page width with multiple col output */
101 const char *timefrmt = TIMEFMT; /* time conversion string */
104 * misc globals
106 FILE *err; /* error message file pointer */
107 int addone; /* page length is odd with double space */
108 int errcnt; /* error count on file processing */
109 char digs[] = "0123456789"; /* page number translation map */
111 int main __P((int, char **));
114 main(argc, argv)
115 int argc;
116 char *argv[];
118 int ret_val;
120 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
121 (void)signal(SIGINT, terminate);
122 ret_val = setup(argc, argv);
123 if (!ret_val) {
125 * select the output format based on options
127 if (merge)
128 ret_val = mulfile(argc, argv);
129 else if (clcnt == 1)
130 ret_val = onecol(argc, argv);
131 else if (across)
132 ret_val = horzcol(argc, argv);
133 else
134 ret_val = vertcol(argc, argv);
135 } else
136 usage();
137 flsh_errs();
138 if (errcnt || ret_val)
139 exit(1);
140 return(0);
144 * onecol: print files with only one column of output.
145 * Line length is unlimited.
148 onecol(argc, argv)
149 int argc;
150 char *argv[];
152 int cnt = -1;
153 int off;
154 int lrgln;
155 int linecnt;
156 int num;
157 int lncnt;
158 int pagecnt;
159 int ips;
160 int ops;
161 int cps;
162 char *obuf = NULL;
163 char *lbuf;
164 char *nbuf;
165 char *hbuf = NULL;
166 char *ohbuf;
167 FILE *inf = NULL;
168 const char *fname;
169 int mor;
170 int error = 1;
172 if (nmwd)
173 num = nmwd + 1;
174 else
175 num = 0;
176 off = num + offst;
179 * allocate line buffer
181 if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL)
182 goto oomem;
184 * allocate header buffer
186 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
187 goto oomem;
189 ohbuf = hbuf + offst;
190 nbuf = obuf + offst;
191 lbuf = nbuf + num;
192 if (num)
193 nbuf[--num] = nmchar;
194 if (offst) {
195 (void)memset(obuf, (int)' ', offst);
196 (void)memset(hbuf, (int)' ', offst);
200 * loop by file
202 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
203 if (pgnm) {
205 * skip to specified page
207 if (inskip(inf, pgnm, lines))
208 continue;
209 pagecnt = pgnm;
210 } else
211 pagecnt = 1;
212 lncnt = 0;
215 * loop by page
217 for(;;) {
218 linecnt = 0;
219 lrgln = 0;
220 ops = 0;
221 ips = 0;
222 cps = 0;
225 * loop by line
227 while (linecnt < lines) {
229 * input next line
231 if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0)
232 break;
233 if (!linecnt && !nohead &&
234 prhead(hbuf, fname, pagecnt))
235 goto out;
238 * start of new line.
240 if (!lrgln) {
241 if (num)
242 addnum(nbuf, num, ++lncnt);
243 if (otln(obuf,cnt+off, &ips, &ops, mor))
244 goto out;
245 } else if (otln(lbuf, cnt, &ips, &ops, mor))
246 goto out;
249 * if line bigger than buffer, get more
251 if (mor) {
252 lrgln = 1;
253 continue;
257 * whole line rcvd. reset tab proc. state
259 ++linecnt;
260 lrgln = 0;
261 ops = 0;
262 ips = 0;
266 * fill to end of page
268 if (linecnt && prtail(lines-linecnt-lrgln, lrgln))
269 goto out;
272 * On EOF go to next file
274 if (cnt < 0)
275 break;
276 ++pagecnt;
278 if (inf != stdin)
279 (void)fclose(inf);
281 if (eoptind < argc)
282 goto out;
283 error = 0;
284 goto out;
285 oomem:
286 mfail();
287 out:
288 if (obuf)
289 free(obuf);
290 if (hbuf)
291 free(hbuf);
292 if (inf != NULL && inf != stdin)
293 (void)fclose(inf);
294 return error;
298 * vertcol: print files with more than one column of output down a page
301 vertcol(argc, argv)
302 int argc;
303 char *argv[];
305 char *ptbf;
306 char **lstdat = NULL;
307 int i;
308 int j;
309 int cnt = -1;
310 int pln;
311 int *indy = NULL;
312 int cvc;
313 int *lindy = NULL;
314 int lncnt;
315 int stp;
316 int pagecnt;
317 int col = colwd + 1;
318 int mxlen = pgwd + offst + 1;
319 int mclcnt = clcnt - 1;
320 struct vcol *vc = NULL;
321 int mvc;
322 int tvc;
323 int cw = nmwd + 1;
324 int fullcol;
325 char *buf = NULL;
326 char *hbuf = NULL;
327 char *ohbuf;
328 const char *fname;
329 FILE *inf = NULL;
330 int ips = 0;
331 int cps = 0;
332 int ops = 0;
333 int mor = 0;
334 int error = 1;
337 * allocate page buffer
339 if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL)
340 goto oomem;
343 * allocate page header
345 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
346 goto oomem;
347 ohbuf = hbuf + offst;
348 if (offst)
349 (void)memset(hbuf, (int)' ', offst);
352 * col pointers when no headers
354 mvc = lines * clcnt;
355 if ((vc = malloc((unsigned)mvc*sizeof(struct vcol))) == NULL)
356 goto oomem;
359 * pointer into page where last data per line is located
361 if ((lstdat = malloc((unsigned)lines*sizeof(char *))) == NULL)
362 goto oomem;
365 * fast index lookups to locate start of lines
367 if ((indy = malloc((unsigned)lines*sizeof(int))) == NULL)
368 goto oomem;
369 if ((lindy = malloc((unsigned)lines*sizeof(int))) == NULL)
370 goto oomem;
372 if (nmwd)
373 fullcol = col + cw;
374 else
375 fullcol = col;
378 * initialize buffer lookup indexes and offset area
380 for (j = 0; j < lines; ++j) {
381 lindy[j] = j * mxlen;
382 indy[j] = lindy[j] + offst;
383 if (offst) {
384 ptbf = buf + lindy[j];
385 (void)memset(ptbf, (int)' ', offst);
386 ptbf += offst;
387 } else
388 ptbf = buf + indy[j];
389 lstdat[j] = ptbf;
393 * loop by file
395 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
396 if (pgnm) {
398 * skip to requested page
400 if (inskip(inf, pgnm, lines))
401 continue;
402 pagecnt = pgnm;
403 } else
404 pagecnt = 1;
405 lncnt = 0;
408 * loop by page
410 for(;;) {
412 * loop by column
414 cvc = 0;
415 for (i = 0; i < clcnt; ++i) {
416 j = 0;
418 * if last column, do not pad
420 if (i == mclcnt)
421 stp = 1;
422 else
423 stp = 0;
425 * loop by line
427 for(;;) {
429 * is this first column
431 if (!i) {
432 ptbf = buf + indy[j];
433 lstdat[j] = ptbf;
434 } else
435 ptbf = lstdat[j];
436 vc[cvc].pt = ptbf;
439 * add number
441 if (nmwd) {
442 addnum(ptbf, nmwd, ++lncnt);
443 ptbf += nmwd;
444 *ptbf++ = nmchar;
448 * input next line
450 cnt = inln(inf,ptbf,colwd,&cps,1,&mor);
451 vc[cvc++].cnt = cnt;
452 if (cnt < 0)
453 break;
454 ptbf += cnt;
457 * pad all but last column on page
459 if (!stp) {
461 * pad to end of column
463 if (sflag)
464 *ptbf++ = schar;
465 else if ((pln = col-cnt) > 0) {
466 (void)memset(ptbf,
467 (int)' ',pln);
468 ptbf += pln;
472 * remember last char in line
474 lstdat[j] = ptbf;
475 if (++j >= lines)
476 break;
478 if (cnt < 0)
479 break;
483 * when -t (no header) is specified the spec requires
484 * the min number of lines. The last page may not have
485 * balanced length columns. To fix this we must reorder
486 * the columns. This is a very slow technique so it is
487 * only used under limited conditions. Without -t, the
488 * balancing of text columns is unspecified. To NOT
489 * balance the last page, add the global variable
490 * nohead to the if statement below e.g.
492 * if ((cnt < 0) && nohead && cvc ......
494 --cvc;
497 * check to see if last page needs to be reordered
499 if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){
500 pln = cvc/clcnt;
501 if (cvc % clcnt)
502 ++pln;
505 * print header
507 if (!nohead && prhead(hbuf, fname, pagecnt))
508 goto out;
509 for (i = 0; i < pln; ++i) {
510 ips = 0;
511 ops = 0;
512 if (offst&& otln(buf,offst,&ips,&ops,1)) {
513 error = 1;
514 goto out;
516 tvc = i;
518 for (j = 0; j < clcnt; ++j) {
520 * determine column length
522 if (j == mclcnt) {
524 * last column
526 cnt = vc[tvc].cnt;
527 if (nmwd)
528 cnt += cw;
529 } else if (sflag) {
531 * single ch between
533 cnt = vc[tvc].cnt + 1;
534 if (nmwd)
535 cnt += cw;
536 } else
537 cnt = fullcol;
538 if (otln(vc[tvc].pt, cnt, &ips,
539 &ops, 1))
540 goto out;
541 tvc += pln;
542 if (tvc >= cvc)
543 break;
546 * terminate line
548 if (otln(buf, 0, &ips, &ops, 0))
549 goto out;
552 * pad to end of page
554 if (prtail((lines - pln), 0))
555 goto out;
557 * done with output, go to next file
559 break;
563 * determine how many lines to output
565 if (i > 0)
566 pln = lines;
567 else
568 pln = j;
571 * print header
573 if (pln && !nohead && prhead(hbuf, fname, pagecnt))
574 goto out;
577 * output each line
579 for (i = 0; i < pln; ++i) {
580 ptbf = buf + lindy[i];
581 if ((j = lstdat[i] - ptbf) <= offst)
582 break;
583 if (otln(ptbf, j, &ips, &ops, 0))
584 goto out;
588 * pad to end of page
590 if (pln && prtail((lines - pln), 0))
591 goto out;
594 * if EOF go to next file
596 if (cnt < 0)
597 break;
598 ++pagecnt;
600 if (inf != stdin)
601 (void)fclose(inf);
603 if (eoptind < argc)
604 goto out;
605 error = 0;
606 goto out;
607 oomem:
608 mfail();
609 out:
610 if (buf)
611 free(buf);
612 if (hbuf)
613 free(hbuf);
614 if (vc)
615 free(vc);
616 if (lstdat)
617 free(lstdat);
618 if (indy)
619 free(lindy);
620 if (inf != NULL && inf != stdin)
621 (void)fclose(inf);
622 return error;
626 * horzcol: print files with more than one column of output across a page
629 horzcol(argc, argv)
630 int argc;
631 char *argv[];
633 char *ptbf;
634 int pln;
635 int cnt = -1;
636 char *lstdat;
637 int col = colwd + 1;
638 int j;
639 int i;
640 int lncnt;
641 int pagecnt;
642 char *buf = NULL;
643 char *hbuf = NULL;
644 char *ohbuf;
645 const char *fname;
646 FILE *inf = NULL;
647 int ips = 0;
648 int cps = 0;
649 int ops = 0;
650 int mor = 0;
651 int error = 1;
653 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
654 goto oomem;
657 * page header
659 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
660 goto oomem;
661 ohbuf = hbuf + offst;
662 if (offst) {
663 (void)memset(buf, (int)' ', offst);
664 (void)memset(hbuf, (int)' ', offst);
668 * loop by file
670 while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) {
671 if (pgnm) {
672 if (inskip(inf, pgnm, lines))
673 continue;
674 pagecnt = pgnm;
675 } else
676 pagecnt = 1;
677 lncnt = 0;
680 * loop by page
682 for(;;) {
684 * loop by line
686 for (i = 0; i < lines; ++i) {
687 ptbf = buf + offst;
688 lstdat = ptbf;
689 j = 0;
691 * loop by col
693 for(;;) {
694 if (nmwd) {
696 * add number to column
698 addnum(ptbf, nmwd, ++lncnt);
699 ptbf += nmwd;
700 *ptbf++ = nmchar;
703 * input line
705 if ((cnt = inln(inf,ptbf,colwd,&cps,1,
706 &mor)) < 0)
707 break;
708 ptbf += cnt;
709 lstdat = ptbf;
712 * if last line skip padding
714 if (++j >= clcnt)
715 break;
718 * pad to end of column
720 if (sflag)
721 *ptbf++ = schar;
722 else if ((pln = col - cnt) > 0) {
723 (void)memset(ptbf,(int)' ',pln);
724 ptbf += pln;
729 * determine line length
731 if ((j = lstdat - buf) <= offst)
732 break;
733 if (!i && !nohead &&
734 prhead(hbuf, fname, pagecnt))
735 goto out;
737 * output line
739 if (otln(buf, j, &ips, &ops, 0))
740 goto out;
744 * pad to end of page
746 if (i && prtail(lines-i, 0))
747 goto out;
750 * if EOF go to next file
752 if (cnt < 0)
753 break;
754 ++pagecnt;
756 if (inf != stdin)
757 (void)fclose(inf);
759 if (eoptind < argc)
760 goto out;
761 error = 0;
762 goto out;
763 oomem:
764 mfail();
765 out:
766 if (buf)
767 free(buf);
768 if (hbuf)
769 free(hbuf);
770 if (inf != NULL && inf != stdin)
771 (void)fclose(inf);
772 return error;
776 * mulfile: print files with more than one column of output and
777 * more than one file concurrently
780 mulfile(argc, argv)
781 int argc;
782 char *argv[];
784 char *ptbf;
785 int j;
786 int pln;
787 int cnt;
788 char *lstdat;
789 int i;
790 FILE **fbuf = NULL;
791 int actf;
792 int lncnt;
793 int col;
794 int pagecnt;
795 int fproc;
796 char *buf = NULL;
797 char *hbuf = NULL;
798 char *ohbuf;
799 const char *fname;
800 int ips = 0;
801 int cps = 0;
802 int ops = 0;
803 int mor = 0;
804 int error = 1;
807 * array of FILE *, one for each operand
809 if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL)
810 goto oomem;
813 * page header
815 if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL)
816 goto oomem;
817 ohbuf = hbuf + offst;
820 * do not know how many columns yet. The number of operands provide an
821 * upper bound on the number of columns. We use the number of files
822 * we can open successfully to set the number of columns. The operation
823 * of the merge operation (-m) in relation to unsuccesful file opens
824 * is unspecified by posix.
826 j = 0;
827 while (j < clcnt) {
828 if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL)
829 break;
830 if (pgnm && (inskip(fbuf[j], pgnm, lines)))
831 fbuf[j] = NULL;
832 ++j;
836 * if no files, exit
838 if (!j)
839 goto out;
842 * calculate page boundries based on open file count
844 clcnt = j;
845 if (nmwd) {
846 colwd = (pgwd - clcnt - nmwd)/clcnt;
847 pgwd = ((colwd + 1) * clcnt) - nmwd - 2;
848 } else {
849 colwd = (pgwd + 1 - clcnt)/clcnt;
850 pgwd = ((colwd + 1) * clcnt) - 1;
852 if (colwd < 1) {
853 (void)fprintf(err,
854 "pr: page width too small for %d columns\n", clcnt);
855 goto out;
857 actf = clcnt;
858 col = colwd + 1;
861 * line buffer
863 if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL)
864 goto out;
865 if (offst) {
866 (void)memset(buf, (int)' ', offst);
867 (void)memset(hbuf, (int)' ', offst);
869 if (pgnm)
870 pagecnt = pgnm;
871 else
872 pagecnt = 1;
873 lncnt = 0;
876 * continue to loop while any file still has data
878 while (actf > 0) {
880 * loop by line
882 for (i = 0; i < lines; ++i) {
883 ptbf = buf + offst;
884 lstdat = ptbf;
885 if (nmwd) {
887 * add line number to line
889 addnum(ptbf, nmwd, ++lncnt);
890 ptbf += nmwd;
891 *ptbf++ = nmchar;
893 j = 0;
894 fproc = 0;
897 * loop by column
899 for (j = 0; j < clcnt; ++j) {
900 if (fbuf[j] == NULL) {
902 * empty column; EOF
904 cnt = 0;
905 } else if ((cnt = inln(fbuf[j], ptbf, colwd,
906 &cps, 1, &mor)) < 0) {
908 * EOF hit; no data
910 if (fbuf[j] != stdin)
911 (void)fclose(fbuf[j]);
912 fbuf[j] = NULL;
913 --actf;
914 cnt = 0;
915 } else {
917 * process file data
919 ptbf += cnt;
920 lstdat = ptbf;
921 fproc++;
925 * if last ACTIVE column, done with line
927 if (fproc >= actf)
928 break;
931 * pad to end of column
933 if (sflag) {
934 *ptbf++ = schar;
935 } else if ((pln = col - cnt) > 0) {
936 (void)memset(ptbf, (int)' ', pln);
937 ptbf += pln;
942 * calculate data in line
944 if ((j = lstdat - buf) <= offst)
945 break;
947 if (!i && !nohead && prhead(hbuf, fname, pagecnt))
948 goto out;
951 * output line
953 if (otln(buf, j, &ips, &ops, 0))
954 goto out;
957 * if no more active files, done
959 if (actf <= 0) {
960 ++i;
961 break;
966 * pad to end of page
968 if (i && prtail(lines-i, 0))
969 goto out;
970 ++pagecnt;
972 if (eoptind < argc)
973 goto out;
974 error = 0;
975 goto out;
976 oomem:
977 mfail();
978 out:
979 if (fbuf) {
980 for (j = 0; j < clcnt; j++)
981 if (fbuf[j] && fbuf[j] != stdin)
982 (void)fclose(fbuf[j]);
983 free(fbuf);
985 if (hbuf)
986 free(hbuf);
987 if (buf)
988 free(buf);
989 return error;
993 * inln(): input a line of data (unlimited length lines supported)
994 * Input is optionally expanded to spaces
996 * inf: file
997 * buf: buffer
998 * lim: buffer length
999 * cps: column positon 1st char in buffer (large line support)
1000 * trnc: throw away data more than lim up to \n
1001 * mor: set if more data in line (not truncated)
1004 inln(inf, buf, lim, cps, trnc, mor)
1005 FILE *inf;
1006 char *buf;
1007 int lim;
1008 int *cps;
1009 int trnc;
1010 int *mor;
1012 int col;
1013 int gap = ingap;
1014 int ch = EOF;
1015 char *ptbuf;
1016 int chk = (int)inchar;
1018 ptbuf = buf;
1020 if (gap) {
1022 * expanding input option
1024 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1026 * is this the input "tab" char
1028 if (ch == chk) {
1030 * expand to number of spaces
1032 col = (ptbuf - buf) + *cps;
1033 col = gap - (col % gap);
1036 * if more than this line, push back
1038 if ((col > lim) && (ungetc(ch, inf) == EOF))
1039 return(1);
1042 * expand to spaces
1044 while ((--col >= 0) && (--lim >= 0))
1045 *ptbuf++ = ' ';
1046 continue;
1048 if (ch == '\n')
1049 break;
1050 *ptbuf++ = ch;
1052 } else {
1054 * no expansion
1056 while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) {
1057 if (ch == '\n')
1058 break;
1059 *ptbuf++ = ch;
1062 col = ptbuf - buf;
1063 if (ch == EOF) {
1064 *mor = 0;
1065 *cps = 0;
1066 if (!col)
1067 return(-1);
1068 return(col);
1070 if (ch == '\n') {
1072 * entire line processed
1074 *mor = 0;
1075 *cps = 0;
1076 return(col);
1080 * line was larger than limit
1082 if (trnc) {
1084 * throw away rest of line
1086 while ((ch = getc(inf)) != EOF) {
1087 if (ch == '\n')
1088 break;
1090 *cps = 0;
1091 *mor = 0;
1092 } else {
1094 * save column offset if not truncated
1096 *cps += col;
1097 *mor = 1;
1100 return(col);
1104 * otln(): output a line of data. (Supports unlimited length lines)
1105 * output is optionally contracted to tabs
1107 * buf: output buffer with data
1108 * cnt: number of chars of valid data in buf
1109 * svips: buffer input column position (for large lines)
1110 * svops: buffer output column position (for large lines)
1111 * mor: output line not complete in this buf; more data to come.
1112 * 1 is more, 0 is complete, -1 is no \n's
1115 otln(buf, cnt, svips, svops, mor)
1116 char *buf;
1117 int cnt;
1118 int *svops;
1119 int *svips;
1120 int mor;
1122 int ops; /* last col output */
1123 int ips; /* last col in buf examined */
1124 int gap = ogap;
1125 int tbps;
1126 char *endbuf;
1128 if (ogap) {
1130 * contracting on output
1132 endbuf = buf + cnt;
1133 ops = *svops;
1134 ips = *svips;
1135 while (buf < endbuf) {
1137 * count number of spaces and ochar in buffer
1139 if (*buf == ' ') {
1140 ++ips;
1141 ++buf;
1142 continue;
1146 * simulate ochar processing
1148 if (*buf == ochar) {
1149 ips += gap - (ips % gap);
1150 ++buf;
1151 continue;
1155 * got a non space char; contract out spaces
1157 while (ops < ips) {
1159 * use one space if necessary
1161 if (ips - ops == 1) {
1162 putchar(' ');
1163 break;
1166 * use as many ochar as will fit
1168 if ((tbps = ops + gap - (ops % gap)) > ips)
1169 break;
1170 if (putchar(ochar) == EOF) {
1171 pfail();
1172 return(1);
1174 ops = tbps;
1177 while (ops < ips) {
1179 * finish off with spaces
1181 if (putchar(' ') == EOF) {
1182 pfail();
1183 return(1);
1185 ++ops;
1189 * output non space char
1191 if (putchar(*buf++) == EOF) {
1192 pfail();
1193 return(1);
1195 ++ips;
1196 ++ops;
1199 if (mor > 0) {
1201 * if incomplete line, save position counts
1203 *svops = ops;
1204 *svips = ips;
1205 return(0);
1208 if (mor < 0) {
1209 while (ops < ips) {
1211 * use one space if necessary
1213 if (ips - ops == 1) {
1214 putchar(' ');
1215 break;
1218 * use as many ochar as will fit
1220 if ((tbps = ops + gap - (ops % gap)) > ips)
1221 break;
1222 if (putchar(ochar) == EOF) {
1223 pfail();
1224 return(1);
1226 ops = tbps;
1228 while (ops < ips) {
1230 * finish off with spaces
1232 if (putchar(' ') == EOF) {
1233 pfail();
1234 return(1);
1236 ++ops;
1238 return(0);
1240 } else {
1242 * output is not contracted
1244 if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) {
1245 pfail();
1246 return(1);
1248 if (mor != 0)
1249 return(0);
1253 * process line end and double space as required
1255 if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) {
1256 pfail();
1257 return(1);
1259 return(0);
1263 * inskip(): skip over pgcnt pages with lncnt lines per page
1264 * file is closed at EOF (if not stdin).
1266 * inf FILE * to read from
1267 * pgcnt number of pages to skip
1268 * lncnt number of lines per page
1271 inskip(inf, pgcnt, lncnt)
1272 FILE *inf;
1273 int pgcnt;
1274 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 FILE *
1302 nxtfile(argc, argv, fname, buf, dt)
1303 int argc;
1304 char **argv;
1305 const char **fname;
1306 char *buf;
1307 int dt;
1309 FILE *inf = NULL;
1310 struct timeval tv;
1311 struct timezone tz;
1312 struct tm *timeptr = NULL;
1313 struct stat statbuf;
1314 time_t curtime;
1315 static int twice = -1;
1317 ++twice;
1318 if (eoptind >= argc) {
1320 * no file listed; default, use standard input
1322 if (twice)
1323 return(NULL);
1324 clearerr(stdin);
1325 inf = stdin;
1326 if (header != NULL)
1327 *fname = header;
1328 else
1329 *fname = FNAME;
1330 if (nohead)
1331 return(inf);
1332 if (gettimeofday(&tv, &tz) < 0) {
1333 ++errcnt;
1334 (void)fprintf(err, "pr: cannot get time of day, %s\n",
1335 strerror(errno));
1336 eoptind = argc - 1;
1337 return(NULL);
1339 curtime = tv.tv_sec;
1340 timeptr = localtime(&curtime);
1342 for (; eoptind < argc; ++eoptind) {
1343 if (strcmp(argv[eoptind], "-") == 0) {
1345 * process a "-" for filename
1347 clearerr(stdin);
1348 inf = stdin;
1349 if (header != NULL)
1350 *fname = header;
1351 else
1352 *fname = FNAME;
1353 ++eoptind;
1354 if (nohead || (dt && twice))
1355 return(inf);
1356 if (gettimeofday(&tv, &tz) < 0) {
1357 ++errcnt;
1358 (void)fprintf(err,
1359 "pr: cannot get time of day, %s\n",
1360 strerror(errno));
1361 return(NULL);
1363 curtime = tv.tv_sec;
1364 timeptr = localtime(&curtime);
1365 } else {
1367 * normal file processing
1369 if ((inf = fopen(argv[eoptind], "r")) == NULL) {
1370 ++errcnt;
1371 if (nodiag)
1372 continue;
1373 (void)fprintf(err, "pr: Cannot open %s, %s\n",
1374 argv[eoptind], strerror(errno));
1375 continue;
1377 if (header != NULL)
1378 *fname = header;
1379 else if (dt)
1380 *fname = FNAME;
1381 else
1382 *fname = argv[eoptind];
1383 ++eoptind;
1384 if (nohead || (dt && twice))
1385 return(inf);
1387 if (dt) {
1388 if (gettimeofday(&tv, &tz) < 0) {
1389 ++errcnt;
1390 (void)fprintf(err,
1391 "pr: cannot get time of day, %s\n",
1392 strerror(errno));
1393 return(NULL);
1395 curtime = tv.tv_sec;
1396 timeptr = localtime(&curtime);
1397 } else {
1398 if (fstat(fileno(inf), &statbuf) < 0) {
1399 ++errcnt;
1400 (void)fclose(inf);
1401 (void)fprintf(err,
1402 "pr: Cannot stat %s, %s\n",
1403 argv[eoptind], strerror(errno));
1404 return(NULL);
1406 timeptr = localtime(&(statbuf.st_mtime));
1409 break;
1411 if (inf == NULL)
1412 return(NULL);
1415 * set up time field used in header
1417 if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) {
1418 ++errcnt;
1419 if (inf != stdin)
1420 (void)fclose(inf);
1421 (void)fputs("pr: time conversion failed\n", err);
1422 return(NULL);
1424 return(inf);
1428 * addnum(): adds the line number to the column
1429 * Truncates from the front or pads with spaces as required.
1430 * Numbers are right justified.
1432 * buf buffer to store the number
1433 * wdth width of buffer to fill
1434 * line line number
1436 * NOTE: numbers occupy part of the column. The posix
1437 * spec does not specify if -i processing should or should not
1438 * occur on number padding. The spec does say it occupies
1439 * part of the column. The usage of addnum currently treats
1440 * numbers as part of the column so spaces may be replaced.
1442 void
1443 addnum(buf, wdth, line)
1444 char *buf;
1445 int wdth;
1446 int line;
1448 char *pt = buf + wdth;
1450 do {
1451 *--pt = digs[line % 10];
1452 line /= 10;
1453 } while (line && (pt > buf));
1456 * pad with space as required
1458 while (pt > buf)
1459 *--pt = ' ';
1463 * prhead(): prints the top of page header
1465 * buf buffer with time field (and offset)
1466 * cnt number of chars in buf
1467 * fname fname field for header
1468 * pagcnt page number
1471 prhead(buf, fname, pagcnt)
1472 char *buf;
1473 const char *fname;
1474 int pagcnt;
1476 int ips = 0;
1477 int ops = 0;
1479 if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) {
1480 pfail();
1481 return(1);
1484 * posix is not clear if the header is subject to line length
1485 * restrictions. The specification for header line format
1486 * in the spec clearly does not limit length. No pr currently
1487 * restricts header length. However if we need to truncate in
1488 * an reasonable way, adjust the length of the printf by
1489 * changing HDFMT to allow a length max as an argument printf.
1490 * buf (which contains the offset spaces and time field could
1491 * also be trimmed
1493 * note only the offset (if any) is processed for tab expansion
1495 if (offst && otln(buf, offst, &ips, &ops, -1))
1496 return(1);
1497 (void)printf(HDFMT,buf+offst, fname, pagcnt);
1498 return(0);
1502 * prtail(): pad page with empty lines (if required) and print page trailer
1503 * if requested
1505 * cnt number of lines of padding needed
1506 * incomp was a '\n' missing from last line output
1509 prtail(cnt, incomp)
1510 int cnt;
1511 int incomp;
1513 if (nohead) {
1515 * only pad with no headers when incomplete last line
1517 if (!incomp)
1518 return(0);
1519 if ((dspace && (putchar('\n') == EOF)) ||
1520 (putchar('\n') == EOF)) {
1521 pfail();
1522 return(1);
1524 return(0);
1528 * if double space output two \n
1530 if (dspace)
1531 cnt *= 2;
1534 * if an odd number of lines per page, add an extra \n
1536 if (addone)
1537 ++cnt;
1540 * pad page
1542 if (formfeed) {
1543 if ((incomp && (putchar('\n') == EOF)) ||
1544 (putchar('\f') == EOF)) {
1545 pfail();
1546 return(1);
1548 return(0);
1550 cnt += TAILLEN;
1551 while (--cnt >= 0) {
1552 if (putchar('\n') == EOF) {
1553 pfail();
1554 return(1);
1557 return(0);
1561 * terminate(): when a SIGINT is recvd
1563 void
1564 terminate(which_sig)
1565 int which_sig;
1567 flsh_errs();
1568 (void)raise_default_signal(which_sig);
1569 exit(1);
1574 * flsh_errs(): output saved up diagnostic messages after all normal
1575 * processing has completed
1577 void
1578 flsh_errs()
1580 char buf[BUFSIZ];
1582 (void)fflush(stdout);
1583 (void)fflush(err);
1584 if (err == stderr)
1585 return;
1586 rewind(err);
1587 while (fgets(buf, BUFSIZ, err) != NULL)
1588 (void)fputs(buf, stderr);
1591 void
1592 mfail()
1594 (void)fputs("pr: memory allocation failed\n", err);
1597 void
1598 pfail()
1600 (void)fprintf(err, "pr: write failure, %s\n", strerror(errno));
1603 void
1604 usage()
1606 (void)fputs(
1607 "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n",err);
1608 (void)fputs(
1609 " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err);
1610 (void)fputs(
1611 " [-s[ch]] [-w width] [-] [file ...]\n", err);
1615 * setup: Validate command args, initialize and perform sanity
1616 * checks on options
1619 setup(argc, argv)
1620 int argc;
1621 char **argv;
1623 int c;
1624 int eflag = 0;
1625 int iflag = 0;
1626 int wflag = 0;
1627 int cflag = 0;
1629 if (isatty(fileno(stdout))) {
1631 * defer diagnostics until processing is done
1633 if ((err = tmpfile()) == NULL) {
1634 (void)fputs("Cannot defer diagnostic messages\n",stderr);
1635 return(1);
1637 } else
1638 err = stderr;
1639 while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?T:w:")) != -1) {
1640 switch (c) {
1641 case '+':
1642 if ((pgnm = atoi(eoptarg)) < 1) {
1643 (void)fputs("pr: +page number must be 1 or more\n",
1644 err);
1645 return(1);
1647 break;
1648 case '-':
1649 if ((clcnt = atoi(eoptarg)) < 1) {
1650 (void)fputs("pr: -columns must be 1 or more\n",err);
1651 return(1);
1653 if (clcnt > 1)
1654 ++cflag;
1655 break;
1656 case 'a':
1657 ++across;
1658 break;
1659 case 'd':
1660 ++dspace;
1661 break;
1662 case 'e':
1663 ++eflag;
1664 if ((eoptarg != NULL) &&
1665 !isdigit((unsigned char)*eoptarg))
1666 inchar = *eoptarg++;
1667 else
1668 inchar = INCHAR;
1669 if ((eoptarg != NULL) &&
1670 isdigit((unsigned char)*eoptarg)) {
1671 if ((ingap = atoi(eoptarg)) < 0) {
1672 (void)fputs(
1673 "pr: -e gap must be 0 or more\n", err);
1674 return(1);
1676 if (ingap == 0)
1677 ingap = INGAP;
1678 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1679 (void)fprintf(err,
1680 "pr: invalid value for -e %s\n", eoptarg);
1681 return(1);
1682 } else
1683 ingap = INGAP;
1684 break;
1685 case 'F':
1686 ++formfeed;
1687 break;
1688 case 'h':
1689 header = eoptarg;
1690 break;
1691 case 'i':
1692 ++iflag;
1693 if ((eoptarg != NULL) &&
1694 !isdigit((unsigned char)*eoptarg))
1695 ochar = *eoptarg++;
1696 else
1697 ochar = OCHAR;
1698 if ((eoptarg != NULL) &&
1699 isdigit((unsigned char)*eoptarg)) {
1700 if ((ogap = atoi(eoptarg)) < 0) {
1701 (void)fputs(
1702 "pr: -i gap must be 0 or more\n", err);
1703 return(1);
1705 if (ogap == 0)
1706 ogap = OGAP;
1707 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1708 (void)fprintf(err,
1709 "pr: invalid value for -i %s\n", eoptarg);
1710 return(1);
1711 } else
1712 ogap = OGAP;
1713 break;
1714 case 'l':
1715 if (!isdigit((unsigned char)*eoptarg) ||
1716 ((lines=atoi(eoptarg)) < 1)) {
1717 (void)fputs(
1718 "pr: Number of lines must be 1 or more\n",err);
1719 return(1);
1721 break;
1722 case 'm':
1723 ++merge;
1724 break;
1725 case 'n':
1726 if ((eoptarg != NULL) &&
1727 !isdigit((unsigned char)*eoptarg))
1728 nmchar = *eoptarg++;
1729 else
1730 nmchar = NMCHAR;
1731 if ((eoptarg != NULL) &&
1732 isdigit((unsigned char)*eoptarg)) {
1733 if ((nmwd = atoi(eoptarg)) < 1) {
1734 (void)fputs(
1735 "pr: -n width must be 1 or more\n",err);
1736 return(1);
1738 } else if ((eoptarg != NULL) && (*eoptarg != '\0')) {
1739 (void)fprintf(err,
1740 "pr: invalid value for -n %s\n", eoptarg);
1741 return(1);
1742 } else
1743 nmwd = NMWD;
1744 break;
1745 case 'o':
1746 if (!isdigit((unsigned char)*eoptarg) ||
1747 ((offst = atoi(eoptarg))< 1)){
1748 (void)fputs("pr: -o offset must be 1 or more\n",
1749 err);
1750 return(1);
1752 break;
1753 case 'r':
1754 ++nodiag;
1755 break;
1756 case 's':
1757 ++sflag;
1758 if (eoptarg == NULL)
1759 schar = SCHAR;
1760 else
1761 schar = *eoptarg++;
1762 if (eoptarg && *eoptarg != '\0') {
1763 (void)fprintf(err,
1764 "pr: invalid value for -s %s\n", eoptarg);
1765 return(1);
1767 break;
1768 case 'T':
1769 timefrmt = eoptarg;
1770 break;
1771 case 't':
1772 ++nohead;
1773 break;
1774 case 'w':
1775 ++wflag;
1776 if (!isdigit((unsigned char)*eoptarg) ||
1777 ((pgwd = atoi(eoptarg)) < 1)){
1778 (void)fputs(
1779 "pr: -w width must be 1 or more \n",err);
1780 return(1);
1782 break;
1783 case '?':
1784 default:
1785 return(1);
1790 * default and sanity checks
1792 if (!clcnt) {
1793 if (merge) {
1794 if ((clcnt = argc - eoptind) <= 1) {
1795 clcnt = CLCNT;
1796 merge = 0;
1798 } else
1799 clcnt = CLCNT;
1801 if (across) {
1802 if (clcnt == 1) {
1803 (void)fputs("pr: -a flag requires multiple columns\n",
1804 err);
1805 return(1);
1807 if (merge) {
1808 (void)fputs("pr: -m cannot be used with -a\n", err);
1809 return(1);
1812 if (!wflag) {
1813 if (sflag)
1814 pgwd = SPGWD;
1815 else
1816 pgwd = PGWD;
1818 if (cflag || merge) {
1819 if (!eflag) {
1820 inchar = INCHAR;
1821 ingap = INGAP;
1823 if (!iflag) {
1824 ochar = OCHAR;
1825 ogap = OGAP;
1828 if (cflag) {
1829 if (merge) {
1830 (void)fputs(
1831 "pr: -m cannot be used with multiple columns\n", err);
1832 return(1);
1834 if (nmwd) {
1835 colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt;
1836 pgwd = ((colwd + nmwd + 2) * clcnt) - 1;
1837 } else {
1838 colwd = (pgwd + 1 - clcnt)/clcnt;
1839 pgwd = ((colwd + 1) * clcnt) - 1;
1841 if (colwd < 1) {
1842 (void)fprintf(err,
1843 "pr: page width is too small for %d columns\n",clcnt);
1844 return(1);
1847 if (!lines)
1848 lines = LINES;
1851 * make sure long enough for headers. if not disable
1853 if (lines <= HEADLEN + TAILLEN)
1854 ++nohead;
1855 else if (!nohead)
1856 lines -= HEADLEN + TAILLEN;
1859 * adjust for double space on odd length pages
1861 if (dspace) {
1862 if (lines == 1)
1863 dspace = 0;
1864 else {
1865 if (lines & 1)
1866 ++addone;
1867 lines /= 2;
1871 return(0);