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