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