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