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