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