add UNLEASHED_OBJ to unleashed.mk
[unleashed/tickless.git] / usr / src / cmd / pr / pr.c
blob9d6e4f46f6873bee966d2f1664dbbcd078723bac
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
29 * All Rights Reserved
33 * University Copyright- Copyright (c) 1982, 1986, 1988
34 * The Regents of the University of California
35 * All Rights Reserved
37 * University Acknowledgment- Portions of this document are derived from
38 * software developed by the University of California, Berkeley, and its
39 * contributors.
42 #pragma ident "%Z%%M% %I% %E% SMI"
45 * PR command (print files in pages and columns, with headings)
46 * 2+head+2+page[56]+5
49 #include <stdio.h>
50 #include <signal.h>
51 #include <ctype.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <locale.h>
57 #include <string.h>
58 #include <limits.h>
59 #include <wchar.h>
60 #include <errno.h>
62 #define ESC '\033'
63 #define LENGTH 66
64 #define LINEW 72
65 #define NUMW 5
66 #define MARGIN 10
67 #define DEFTAB 8
68 #define NFILES 10
69 #define STDINNAME() nulls
70 #define PROMPT() (void) putc('\7', stderr) /* BEL */
71 #define NOFILE nulls
72 #define ETABS (Inpos % Etabn)
73 #define NSEPC '\t'
74 #define HEAD gettext("%s %s Page %d\n\n\n"), date, head, Page
75 #define cerror(S) (void) fprintf(stderr, "pr: %s", gettext(S))
76 #define done() if (Ttyout) (void) chmod(Ttyout, Mode)
77 #define ALL_NUMS(s) (strspn(s, "0123456789") == strlen(s))
78 #define REMOVE_ARG(argc, argp) \
79 { \
80 char **p = argp; \
81 while (*p != NULL) \
82 { \
83 *p = *(p + 1); \
84 p++; \
85 } \
86 argc--; \
88 #define SQUEEZE_ARG(argp, ind, n) \
89 { \
90 int i; \
91 for (i = ind; argp[i]; i++) \
92 argp[i] = argp[i + n]; \
96 * ---date time format---
97 * b -- abbreviated month name
98 * e -- day of month
99 * H -- Hour (24 hour version)
100 * M -- Minute
101 * Y -- Year in the form ccyy
103 #define FORMAT "%b %e %H:%M %Y"
105 typedef int ANY;
106 typedef unsigned int UNS;
107 typedef struct { FILE *f_f; char *f_name; wchar_t f_nextc; } FILS;
108 typedef struct {int fold; int skip; int eof; } foldinf;
109 typedef struct { wchar_t *c_ptr, *c_ptr0; long c_lno; int c_skip; } *COLP;
110 typedef struct err { struct err *e_nextp; char *e_mess; } ERR;
113 * Global data.
115 static FILS *Files;
116 static mode_t Mode;
117 static int Multi = 0;
118 static int Nfiles = 0;
119 static int Error = 0;
120 static char nulls[] = "";
121 static char *Ttyout;
122 static char obuf[BUFSIZ];
123 static char time_buf[50]; /* array to hold the time and date */
124 static long Lnumb = 0;
125 static FILE *Ttyin;
126 static int Dblspace = 1;
127 static int Fpage = 1;
128 static int Formfeed = 0;
129 static int Length = LENGTH;
130 static int Linew = 0;
131 static int Offset = 0;
132 static int Ncols = 0;
133 static int Pause = 0;
134 static wchar_t Sepc = 0;
135 static int Colw;
136 static int Plength;
137 static int Margin = MARGIN;
138 static int Numw;
139 static int Nsepc = NSEPC;
140 static int Report = 1;
141 static int Etabn = 0;
142 static wchar_t Etabc = '\t';
143 static int Itabn = 0;
144 static wchar_t Itabc = '\t';
145 static int fold = 0;
146 static int foldcol = 0;
147 static int alleof = 0;
148 static char *Head = NULL;
149 static wchar_t *Buffer = NULL, *Bufend, *Bufptr;
150 static UNS Buflen;
151 static COLP Colpts;
152 static foldinf *Fcol;
153 static int Page;
154 static wchar_t C = '\0';
155 static int Nspace;
156 static int Inpos;
157 static int Outpos;
158 static int Lcolpos;
159 static int Pcolpos;
160 static int Line;
161 static ERR *Err = NULL;
162 static ERR *Lasterr = (ERR *)&Err;
163 static int mbcurmax = 1;
166 * Function prototypes.
168 static void onintr();
169 static ANY *getspace();
170 static int findopt(int, char **);
171 static void fixtty();
172 static char *GETDATE();
173 static char *ffiler(char *);
174 static int print(char *);
175 static void putpage();
176 static void foldpage();
177 static void nexbuf();
178 static void foldbuf();
179 static void balance(int);
180 static int readbuf(wchar_t **, int, COLP);
181 static wint_t get(int);
182 static int put(wchar_t);
183 static void putspace();
184 static void unget(int);
185 static FILE *mustopen(char *, FILS *);
186 static void die(char *);
187 static void errprint();
188 static void usage(int);
189 static wint_t _fgetwc_pr(FILE *, int *);
190 static size_t freadw(wchar_t *, size_t, FILE *);
194 main(int argc, char **argv)
196 FILS fstr[NFILES];
197 int nfdone = 0;
199 Ttyin = stdin;
201 /* Get locale variables for environment */
202 (void) setlocale(LC_ALL, "");
204 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
205 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
206 #endif
207 (void) textdomain(TEXT_DOMAIN);
209 mbcurmax = MB_CUR_MAX;
210 Files = fstr;
211 for (argc = findopt(argc, argv); argc > 0; --argc, ++argv) {
212 if (Multi == 'm') {
213 if (Nfiles >= NFILES - 1) die("too many files");
214 if (mustopen(*argv, &Files[Nfiles++]) == NULL)
215 ++nfdone; /* suppress printing */
216 } else {
217 if (print(*argv))
218 (void) fclose(Files->f_f);
219 ++nfdone;
222 if (!nfdone) /* no files named, use stdin */
223 (void) print(NOFILE); /* on GCOS, use current file, if any */
225 if (Report) {
226 errprint(); /* print accumulated error reports */
227 exit(Error);
230 return (Error);
235 * findopt() returns argc modified to be the number of explicitly supplied
236 * filenames, including '-', the explicit request to use stdin.
237 * argc == 0 implies that no filenames were supplied and stdin should be used.
238 * Options are striped from argv and only file names are returned.
241 static int
242 findopt(int argc, char **argv)
244 int eargc = 0;
245 int c;
246 int mflg = 0;
247 int aflg = 0;
248 int optnum;
249 int argv_ind;
250 int end_opt;
251 int i;
252 int isarg = 0;
254 fixtty();
256 /* Handle page number option */
257 for (optnum = 1, end_opt = 0; optnum < argc && !end_opt; optnum++) {
258 switch (*argv[optnum]) {
259 case '+':
260 /* check for all digits */
261 if (strlen(&argv[optnum][1]) !=
262 strspn(&argv[optnum][1], "0123456789")) {
263 (void) fprintf(stderr, gettext(
264 "pr: Badly formed number\n"));
265 exit(1);
268 if ((Fpage = (int)strtol(&argv[optnum][1],
269 (char **)NULL, 10)) < 0) {
270 (void) fprintf(stderr, gettext(
271 "pr: Badly formed number\n"));
272 exit(1);
274 REMOVE_ARG(argc, &argv[optnum]);
275 optnum--;
276 break;
278 case '-':
279 /* Check for end of options */
280 if (argv[optnum][1] == '-') {
281 end_opt++;
282 break;
285 if (argv[optnum][1] == 'h' || argv[optnum][1] == 'l' ||
286 argv[optnum][1] == 'o' || argv[optnum][1] == 'w')
287 isarg = 1;
288 else
289 isarg = 0;
291 break;
293 default:
294 if (isarg == 0)
295 end_opt++;
296 else
297 isarg = 0;
298 break;
303 * Handle options with optional arguments.
304 * If optional arguments are present they may not be separated
305 * from the option letter.
308 for (optnum = 1; optnum < argc; optnum++) {
309 if (argv[optnum][0] == '-' && argv[optnum][1] == '-')
310 /* End of options */
311 break;
313 if (argv[optnum][0] == '-' && argv[optnum][1] == '\0')
314 /* stdin file name */
315 continue;
317 if (argv[optnum][0] != '-')
318 /* not option */
319 continue;
321 for (argv_ind = 1; argv[optnum][argv_ind] != '\0'; argv_ind++) {
322 switch (argv[optnum][argv_ind]) {
323 case 'e':
324 SQUEEZE_ARG(argv[optnum], argv_ind, 1);
325 if ((c = argv[optnum][argv_ind]) != '\0' &&
326 !isdigit(c)) {
327 int r;
328 wchar_t wc;
329 r = mbtowc(&wc, &argv[optnum][argv_ind],
330 mbcurmax);
331 if (r == -1) {
332 (void) fprintf(stderr, gettext(
333 "pr: Illegal character in -e option\n"));
334 exit(1);
336 Etabc = wc;
337 SQUEEZE_ARG(argv[optnum], argv_ind, r);
339 if (isdigit(argv[optnum][argv_ind])) {
340 Etabn = (int)strtol(&argv[optnum]
341 [argv_ind], (char **)NULL, 10);
342 while (isdigit(argv[optnum][argv_ind]))
343 SQUEEZE_ARG(argv[optnum],
344 argv_ind, 1);
346 if (Etabn <= 0)
347 Etabn = DEFTAB;
348 argv_ind--;
349 break;
351 case 'i':
352 SQUEEZE_ARG(argv[optnum], argv_ind, 1);
353 if ((c = argv[optnum][argv_ind]) != '\0' &&
354 !isdigit(c)) {
355 int r;
356 wchar_t wc;
357 r = mbtowc(&wc, &argv[optnum][argv_ind],
358 mbcurmax);
359 if (r == -1) {
360 (void) fprintf(stderr, gettext(
361 "pr: Illegal character in -i option\n"));
362 exit(1);
364 Itabc = wc;
365 SQUEEZE_ARG(argv[optnum], argv_ind, r);
367 if (isdigit(argv[optnum][argv_ind])) {
368 Itabn = (int)strtol(&argv[optnum]
369 [argv_ind], (char **)NULL, 10);
370 while (isdigit(argv[optnum][argv_ind]))
371 SQUEEZE_ARG(argv[optnum],
372 argv_ind, 1);
374 if (Itabn <= 0)
375 Itabn = DEFTAB;
376 argv_ind--;
377 break;
380 case 'n':
381 ++Lnumb;
382 SQUEEZE_ARG(argv[optnum], argv_ind, 1);
383 if ((c = argv[optnum][argv_ind]) != '\0' &&
384 !isdigit(c)) {
385 int r;
386 wchar_t wc;
387 r = mbtowc(&wc, &argv[optnum][argv_ind],
388 mbcurmax);
389 if (r == -1) {
390 (void) fprintf(stderr, gettext(
391 "pr: Illegal character in -n option\n"));
392 exit(1);
394 Nsepc = wc;
395 SQUEEZE_ARG(argv[optnum], argv_ind, r);
397 if (isdigit(argv[optnum][argv_ind])) {
398 Numw = (int)strtol(&argv[optnum]
399 [argv_ind], (char **)NULL, 10);
400 while (isdigit(argv[optnum][argv_ind]))
401 SQUEEZE_ARG(argv[optnum],
402 argv_ind, 1);
404 argv_ind--;
405 if (!Numw)
406 Numw = NUMW;
407 break;
409 case 's':
410 SQUEEZE_ARG(argv[optnum], argv_ind, 1);
411 if ((Sepc = argv[optnum][argv_ind]) == '\0')
412 Sepc = '\t';
413 else {
414 int r;
415 wchar_t wc;
416 r = mbtowc(&wc, &argv[optnum][argv_ind],
417 mbcurmax);
418 if (r == -1) {
419 (void) fprintf(stderr, gettext(
420 "pr: Illegal character in -s option\n"));
421 exit(1);
423 Sepc = wc;
424 SQUEEZE_ARG(argv[optnum], argv_ind, r);
426 argv_ind--;
427 break;
429 default:
430 break;
433 if (argv[optnum][0] == '-' && argv[optnum][1] == '\0') {
434 REMOVE_ARG(argc, &argv[optnum]);
435 optnum--;
439 /* Now get the other options */
440 while ((c = getopt(argc, argv, "0123456789adfFh:l:mo:prtw:"))
441 != EOF) {
442 switch (c) {
443 case '0':
444 case '1':
445 case '2':
446 case '3':
447 case '4':
448 case '5':
449 case '6':
450 case '7':
451 case '8':
452 case '9':
453 Ncols *= 10;
454 Ncols += c - '0';
455 break;
457 case 'a':
458 aflg++;
459 if (!Multi)
460 Multi = c;
461 break;
463 case 'd':
464 Dblspace = 2;
465 break;
467 case 'f':
468 ++Formfeed;
469 ++Pause;
470 break;
472 case 'h':
473 Head = optarg;
474 break;
476 case 'l':
477 if (strlen(optarg) != strspn(optarg, "0123456789"))
478 usage(1);
479 Length = (int)strtol(optarg, (char **)NULL, 10);
480 break;
482 case 'm':
483 mflg++;
484 Multi = c;
485 break;
487 case 'o':
488 if (strlen(optarg) != strspn(optarg, "0123456789"))
489 usage(1);
490 Offset = (int)strtol(optarg, (char **)NULL, 10);
491 break;
493 case 'p':
494 ++Pause;
495 break;
497 case 'r':
498 Report = 0;
499 break;
501 case 't':
502 Margin = 0;
503 break;
505 case 'w':
506 if (strlen(optarg) != strspn(optarg, "0123456789"))
507 usage(1);
508 Linew = (int)strtol(optarg, (char **)NULL, 10);
509 break;
511 case 'F':
512 #ifdef XPG4
513 ++Formfeed;
514 #else
515 fold++;
516 #endif
517 break;
519 case '?':
520 usage(2);
521 break;
523 default :
524 usage(2);
528 /* Count the file names and strip options */
529 for (i = 1; i < argc; i++) {
530 /* Check for explicit stdin */
531 if ((argv[i][0] == '-') && (argv[i][1] == '\0')) {
532 argv[eargc++][0] = '\0';
533 REMOVE_ARG(argc, &argv[i]);
534 if (i < optind)
535 optind--;
538 for (i = eargc; optind < argc; i++, optind++) {
539 argv[i] = argv[optind];
540 eargc++;
543 /* Check options */
544 if (Ncols == 0)
545 Ncols = 1;
547 if (mflg && (Ncols > 1)) {
548 (void) fprintf(stderr,
549 gettext("pr: only one of either -m or -column allowed\n"));
550 usage(1);
553 if (Ncols == 1 && fold)
554 Multi = 'm';
556 if (Length <= 0)
557 Length = LENGTH;
559 if (Length <= Margin)
560 Margin = 0;
562 Plength = Length - Margin/2;
564 if (Multi == 'm')
565 Ncols = eargc;
567 switch (Ncols) {
568 case 0:
569 Ncols = 1;
570 break;
572 case 1:
573 break;
575 default:
576 if (Etabn == 0) /* respect explicit tab specification */
577 Etabn = DEFTAB;
578 if (Itabn == 0)
579 Itabn = DEFTAB;
582 if ((Fcol = (foldinf *) malloc(sizeof (foldinf) * Ncols)) == NULL) {
583 (void) fprintf(stderr, gettext("pr: malloc failed\n"));
584 exit(1);
586 for (i = 0; i < Ncols; i++)
587 Fcol[i].fold = Fcol[i].skip = 0;
589 if (Linew == 0)
590 Linew = Ncols != 1 && Sepc == 0 ? LINEW : 512;
592 if (Lnumb) {
593 int numw;
595 if (Nsepc == '\t') {
596 if (Itabn == 0)
597 numw = Numw + DEFTAB - (Numw % DEFTAB);
598 else
599 numw = Numw + Itabn - (Numw % Itabn);
600 } else {
601 numw = Numw + ((iswprint(Nsepc)) ? 1 : 0);
603 Linew -= (Multi == 'm') ? numw : numw * Ncols;
606 if ((Colw = (Linew - Ncols + 1)/Ncols) < 1)
607 die("width too small");
609 if (Ncols != 1 && Multi == 0) {
610 /* Buflen should take the number of wide characters */
611 /* Not the size for Buffer */
612 Buflen = ((UNS) (Plength / Dblspace + 1)) *
613 2 * (Linew + 1);
614 /* Should allocate Buflen * sizeof (wchar_t) */
615 Buffer = (wchar_t *)getspace(Buflen * sizeof (wchar_t));
616 Bufptr = Bufend = &Buffer[Buflen];
617 Colpts = (COLP) getspace((UNS) ((Ncols + 1) *
618 sizeof (*Colpts)));
619 Colpts[0].c_lno = 0;
622 /* is stdin not a tty? */
623 if (Ttyout && (Pause || Formfeed) && !ttyname(fileno(stdin)))
624 Ttyin = fopen("/dev/tty", "r");
626 return (eargc);
630 static int
631 print(char *name)
633 static int notfirst = 0;
634 char *date = NULL;
635 char *head = NULL;
636 int c;
638 if (Multi != 'm' && mustopen(name, &Files[0]) == NULL)
639 return (0);
640 if (Multi == 'm' && Nfiles == 0 && mustopen(name, &Files[0]) == NULL)
641 die("cannot open stdin");
642 if (Buffer)
643 (void) ungetwc(Files->f_nextc, Files->f_f);
644 if (Lnumb)
645 Lnumb = 1;
646 for (Page = 0; ; putpage()) {
647 if (C == WEOF && !(fold && Buffer))
648 break;
649 if (Buffer)
650 nexbuf();
651 Inpos = 0;
652 if (get(0) == WEOF)
653 break;
654 (void) fflush(stdout);
655 if (++Page >= Fpage) {
656 /* Pause if -p and not first page */
657 if (Ttyout && Pause && !notfirst++) {
658 PROMPT(); /* prompt with bell and pause */
659 while ((c = getc(Ttyin)) != EOF && c != '\n')
662 if (Margin == 0)
663 continue;
664 if (date == NULL)
665 date = GETDATE();
666 if (head == NULL)
667 head = Head != NULL ? Head :
668 Nfiles < 2 ? Files->f_name : nulls;
669 (void) printf("\n\n");
670 Nspace = Offset;
671 putspace();
672 (void) printf(HEAD);
675 C = '\0';
676 return (1);
680 static void
681 putpage()
683 int colno;
685 if (fold) {
686 foldpage();
687 return;
689 for (Line = Margin / 2; ; (void) get(0)) {
690 for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
691 if (Lnumb && (C != WEOF) &&
692 (((colno == 0) && (Multi == 'm')) ||
693 (Multi != 'm'))) {
694 if (Page >= Fpage) {
695 putspace();
696 (void) printf("%*ld%wc", Numw, Buffer ?
697 Colpts[colno].c_lno++ :
698 Lnumb, Nsepc);
700 /* Move Outpos for number field */
701 Outpos += Numw;
702 if (Nsepc == '\t')
703 Outpos +=
704 DEFTAB - (Outpos % DEFTAB);
705 else
706 Outpos++;
708 ++Lnumb;
710 for (Lcolpos = 0, Pcolpos = 0;
711 C != '\n' && C != '\f' && C != WEOF;
712 (void) get(colno))
713 (void) put(C);
715 if ((C == WEOF) || (++colno == Ncols) ||
716 ((C == '\n') && (get(colno) == WEOF)))
717 break;
719 if (Sepc)
720 (void) put(Sepc);
721 else if ((Nspace += Colw - Lcolpos + 1) < 1)
722 Nspace = 1;
725 if (C == WEOF) {
726 if (Margin != 0)
727 break;
728 if (colno != 0)
729 (void) put('\n');
730 return;
732 if (C == '\f')
733 break;
734 (void) put('\n');
735 if (Dblspace == 2 && Line < Plength)
736 (void) put('\n');
737 if (Line >= Plength)
738 break;
740 if (Formfeed)
741 (void) put('\f');
742 else
743 while (Line < Length)
744 (void) put('\n');
748 static void
749 foldpage()
751 int colno;
752 int keep;
753 int i;
754 int pLcolpos;
755 static int sl;
757 for (Line = Margin / 2; ; (void) get(0)) {
758 for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
759 if (Lnumb && Multi == 'm' && foldcol) {
760 if (!Fcol[colno].skip) {
761 unget(colno);
762 putspace();
763 if (!colno) {
764 for (i = 0; i <= Numw; i++)
765 (void) printf(" ");
766 (void) printf("%wc", Nsepc);
768 for (i = 0; i <= Colw; i++)
769 (void) printf(" ");
770 (void) put(Sepc);
771 if (++colno == Ncols)
772 break;
773 (void) get(colno);
774 continue;
775 } else if (!colno)
776 Lnumb = sl;
779 if (Lnumb && (C != WEOF) &&
780 ((colno == 0 && Multi == 'm') || (Multi != 'm'))) {
781 if (Page >= Fpage) {
782 putspace();
783 if ((foldcol &&
784 Fcol[colno].skip && Multi != 'a') ||
785 (Fcol[0].fold && Multi == 'a') ||
786 (Buffer && Colpts[colno].c_skip)) {
787 for (i = 0; i < Numw; i++)
788 (void) printf(" ");
789 (void) printf("%wc", Nsepc);
790 if (Buffer) {
791 Colpts[colno].c_lno++;
792 Colpts[colno].c_skip =
796 else
797 (void) printf("%*ld%wc", Numw, Buffer ?
798 Colpts[colno].c_lno++ :
799 Lnumb, Nsepc);
801 sl = Lnumb++;
803 pLcolpos = 0;
804 for (Lcolpos = 0, Pcolpos = 0;
805 C != '\n' && C != '\f' && C != WEOF;
806 (void) get(colno)) {
807 if (put(C)) {
808 unget(colno);
809 Fcol[(Multi == 'a') ? 0 : colno].fold
810 = 1;
811 break;
812 } else if (Multi == 'a') {
813 Fcol[0].fold = 0;
815 pLcolpos = Lcolpos;
817 if (Buffer) {
818 alleof = 1;
819 for (i = 0; i < Ncols; i++)
820 if (!Fcol[i].eof)
821 alleof = 0;
822 if (alleof || ++colno == Ncols)
823 break;
824 } else if (C == EOF || ++colno == Ncols)
825 break;
826 keep = C;
827 (void) get(colno);
828 if (keep == '\n' && C == WEOF)
829 break;
830 if (Sepc)
831 (void) put(Sepc);
832 else if ((Nspace += Colw - pLcolpos + 1) < 1)
833 Nspace = 1;
835 foldcol = 0;
836 if (Lnumb && Multi != 'a') {
837 for (i = 0; i < Ncols; i++) {
838 Fcol[i].skip = Fcol[i].fold;
839 foldcol += Fcol[i].fold;
840 Fcol[i].fold = 0;
843 if (C == WEOF) {
844 if (Margin != 0)
845 break;
846 if (colno != 0)
847 (void) put('\n');
848 return;
850 if (C == '\f')
851 break;
852 (void) put('\n');
853 (void) fflush(stdout);
854 if (Dblspace == 2 && Line < Plength)
855 (void) put('\n');
856 if (Line >= Plength)
857 break;
859 if (Formfeed)
860 (void) put('\f');
861 else while (Line < Length)
862 (void) put('\n');
866 static void
867 nexbuf()
869 wchar_t *s = Buffer;
870 COLP p = Colpts;
871 int j;
872 int c;
873 int bline = 0;
874 wchar_t wc;
876 if (fold) {
877 foldbuf();
878 return;
880 for (; ; ) {
881 p->c_ptr0 = p->c_ptr = s;
882 if (p == &Colpts[Ncols])
883 return;
884 (p++)->c_lno = Lnumb + bline;
885 for (j = (Length - Margin)/Dblspace; --j >= 0; ++bline) {
886 for (Inpos = 0; ; ) {
887 errno = 0;
888 wc = _fgetwc_pr(Files->f_f, &c);
889 if (wc == WEOF) {
890 /* If there is an illegal character, */
891 /* handle it as a byte sequence. */
892 if (errno == EILSEQ) {
893 if (Inpos < Colw - 1) {
894 *s = c;
895 if (++s >= Bufend)
896 die("page-buffer overflow");
898 Inpos++;
899 Error++;
900 return;
901 } else {
902 /* Real EOF */
903 for (*s = WEOF; p <= &Colpts[Ncols]; ++p)
904 p->c_ptr0 = p->c_ptr = s;
905 balance(bline);
906 return;
910 if (isascii(wc)) {
911 if (isprint(wc))
912 Inpos++;
913 } else if (iswprint(wc)) {
914 Inpos += wcwidth(wc);
917 if (Inpos <= Colw || wc == '\n') {
918 *s = wc;
919 if (++s >= Bufend)
920 die("page-buffer overflow");
922 if (wc == '\n')
923 break;
924 switch (wc) {
925 case '\b':
926 if (Inpos == 0)
927 --s;
929 /*FALLTHROUGH*/
931 case ESC:
932 if (Inpos > 0)
933 --Inpos;
941 static void
942 foldbuf()
944 int num;
945 int i;
946 int colno = 0;
947 int size = Buflen;
948 wchar_t *s;
949 wchar_t *d;
950 COLP p = Colpts;
952 for (i = 0; i < Ncols; i++)
953 Fcol[i].eof = 0;
954 d = Buffer;
955 if (Bufptr != Bufend) {
956 s = Bufptr;
957 while (s < Bufend)
958 *d++ = *s++;
959 size -= (Bufend - Bufptr);
961 Bufptr = Buffer;
962 p->c_ptr0 = p->c_ptr = Buffer;
963 if (p->c_lno == 0) {
964 p->c_lno = Lnumb;
965 p->c_skip = 0;
966 } else {
967 p->c_lno = Colpts[Ncols-1].c_lno;
968 p->c_skip = Colpts[Ncols].c_skip;
969 if (p->c_skip)
970 p->c_lno--;
972 if ((num = freadw(d, size, Files->f_f)) != size) {
973 for (*(d+num) = WEOF; (++p) <= &Colpts[Ncols]; ) {
974 p->c_ptr0 = p->c_ptr = (d+num);
976 balance(0);
977 return;
979 i = (Length - Margin) / Dblspace;
980 do {
981 (void) readbuf(&Bufptr, i, p++);
982 } while (++colno < Ncols);
986 static void
987 balance(int bline) /* line balancing for last page */
989 wchar_t *s = Buffer;
990 COLP p = Colpts;
991 int colno = 0;
992 int j;
993 int c;
994 int l;
995 int lines;
997 if (!fold) {
998 c = bline % Ncols;
999 l = (bline + Ncols - 1)/Ncols;
1000 bline = 0;
1001 do {
1002 for (j = 0; j < l; ++j)
1003 while (*s++ != '\n')
1005 (++p)->c_lno = Lnumb + (bline += l);
1006 p->c_ptr0 = p->c_ptr = s;
1007 if (++colno == c)
1008 --l;
1009 } while (colno < Ncols - 1);
1010 } else {
1011 lines = readbuf(&s, 0, 0);
1012 l = (lines + Ncols - 1)/Ncols;
1013 if (l > ((Length - Margin) / Dblspace)) {
1014 l = (Length - Margin) / Dblspace;
1015 c = Ncols;
1016 } else {
1017 c = lines % Ncols;
1019 s = Buffer;
1020 do {
1021 (void) readbuf(&s, l, p++);
1022 if (++colno == c)
1023 --l;
1024 } while (colno < Ncols);
1025 Bufptr = s;
1030 static int
1031 readbuf(wchar_t **s, int lincol, COLP p)
1033 int lines = 0;
1034 int chars = 0;
1035 int width;
1036 int nls = 0;
1037 int move;
1038 int skip = 0;
1039 int decr = 0;
1041 width = (Ncols == 1) ? Linew : Colw;
1042 while (**s != WEOF) {
1043 switch (**s) {
1044 case '\n':
1045 lines++; nls++; chars = 0; skip = 0;
1046 break;
1048 case '\b':
1049 case ESC:
1050 if (chars) chars--;
1051 break;
1053 case '\t':
1054 move = Itabn - ((chars + Itabn) % Itabn);
1055 move = (move < width-chars) ? move :
1056 width-chars;
1057 chars += move;
1059 default:
1060 if (isascii(**s)) {
1061 if (isprint(**s))
1062 chars++;
1063 } else if (iswprint(**s)) {
1064 chars += wcwidth(**s);
1067 if (chars > width) {
1068 lines++;
1069 skip++;
1070 decr++;
1071 chars = 0;
1073 if (lincol && lines == lincol) {
1074 (p+1)->c_lno = p->c_lno + nls;
1075 (++p)->c_skip = skip;
1076 if (**s == '\n') (*s)++;
1077 p->c_ptr0 = p->c_ptr = (wchar_t *)*s;
1078 return (0);
1080 if (decr)
1081 decr = 0;
1082 else
1083 (*s)++;
1085 return (lines);
1089 static wint_t
1090 get(int colno)
1092 static int peekc = 0;
1093 COLP p;
1094 FILS *q;
1095 int c;
1096 wchar_t wc, w;
1098 if (peekc) {
1099 peekc = 0;
1100 wc = Etabc;
1101 } else if (Buffer) {
1102 p = &Colpts[colno];
1103 if (p->c_ptr >= (p+1)->c_ptr0)
1104 wc = WEOF;
1105 else if ((wc = *p->c_ptr) != WEOF)
1106 ++p->c_ptr;
1107 if (fold && wc == WEOF)
1108 Fcol[colno].eof = 1;
1109 } else if ((wc =
1110 (q = &Files[Multi == 'a' ? 0 : colno])->f_nextc) == WEOF) {
1111 for (q = &Files[Nfiles]; --q >= Files && q->f_nextc == WEOF; )
1113 if (q >= Files)
1114 wc = '\n';
1115 } else {
1116 errno = 0;
1117 w = _fgetwc_pr(q->f_f, &c);
1118 if (w == WEOF && errno == EILSEQ) {
1119 q->f_nextc = (wchar_t)c;
1120 } else {
1121 q->f_nextc = w;
1125 if (Etabn != 0 && wc == Etabc) {
1126 ++Inpos;
1127 peekc = ETABS;
1128 wc = ' ';
1129 return (C = wc);
1132 if (wc == WEOF)
1133 return (C = wc);
1135 if (isascii(wc)) {
1136 if (isprint(wc)) {
1137 Inpos++;
1138 return (C = wc);
1140 } else if (iswprint(wc)) {
1141 Inpos += wcwidth(wc);
1142 return (C = wc);
1145 switch (wc) {
1146 case '\b':
1147 case ESC:
1148 if (Inpos > 0)
1149 --Inpos;
1150 break;
1151 case '\f':
1152 if (Ncols == 1)
1153 break;
1154 wc = '\n';
1155 /* FALLTHROUGH */
1156 case '\n':
1157 case '\r':
1158 Inpos = 0;
1159 break;
1161 return (C = wc);
1165 static int
1166 put(wchar_t wc)
1168 int move = 0;
1169 int width = Colw;
1170 int sp = Lcolpos;
1172 if (fold && Ncols == 1)
1173 width = Linew;
1175 switch (wc) {
1176 case ' ':
1177 /* If column not full or this is separator char */
1178 if ((!fold && Ncols < 2) || (Lcolpos < width) ||
1179 ((Sepc == wc) && (Lcolpos == width))) {
1180 ++Nspace;
1181 ++Lcolpos;
1183 if (fold && sp == Lcolpos)
1184 if (Lcolpos >= width)
1185 return (1);
1187 return (0);
1189 case '\t':
1190 if (Itabn == 0)
1191 break;
1193 /* If column not full or this is separator char */
1194 if ((Lcolpos < width) ||
1195 ((Sepc == wc) && (Lcolpos == width))) {
1196 move = Itabn - ((Lcolpos + Itabn) % Itabn);
1197 move = (move < width-Lcolpos) ? move : width-Lcolpos;
1198 Nspace += move;
1199 Lcolpos += move;
1201 if (fold && sp == Lcolpos)
1202 if (Lcolpos >= width)
1203 return (1);
1204 return (0);
1206 case '\b':
1207 if (Lcolpos == 0)
1208 return (0);
1209 if (Nspace > 0) {
1210 --Nspace;
1211 --Lcolpos;
1212 return (0);
1214 if (Lcolpos > Pcolpos) {
1215 --Lcolpos;
1216 return (0);
1219 /*FALLTHROUGH*/
1221 case ESC:
1222 move = -1;
1223 break;
1225 case '\n':
1226 ++Line;
1228 /*FALLTHROUGH*/
1230 case '\r':
1231 case '\f':
1232 Pcolpos = 0;
1233 Lcolpos = 0;
1234 Nspace = 0;
1235 Outpos = 0;
1236 /* FALLTHROUGH */
1237 default:
1238 if (isascii(wc)) {
1239 if (isprint(wc))
1240 move = 1;
1241 else
1242 move = 0;
1243 } else if (iswprint(wc)) {
1244 move = wcwidth(wc);
1245 } else {
1246 move = 0;
1248 break;
1250 if (Page < Fpage)
1251 return (0);
1252 if (Lcolpos > 0 || move > 0)
1253 Lcolpos += move;
1255 putspace();
1257 /* If column not full or this is separator char */
1258 if ((!fold && Ncols < 2) || (Lcolpos <= width) ||
1259 ((Sepc == wc) && (Lcolpos > width))) {
1260 (void) fputwc(wc, stdout);
1261 Outpos += move;
1262 Pcolpos = Lcolpos;
1265 if (fold && Lcolpos > width)
1266 return (1);
1268 return (0);
1272 static void
1273 putspace(void)
1275 int nc = 0;
1277 for (; Nspace > 0; Outpos += nc, Nspace -= nc) {
1278 #ifdef XPG4
1279 /* XPG4: -i: replace multiple SPACE chars with tab chars */
1280 if ((Nspace >= 2 && Itabn > 0 &&
1281 Nspace >= (nc = Itabn - Outpos % Itabn)) && !fold) {
1282 #else
1283 /* Solaris: -i: replace white space with tab chars */
1284 if ((Itabn > 0 && Nspace >= (nc = Itabn - Outpos % Itabn)) &&
1285 !fold) {
1286 #endif
1287 (void) fputwc(Itabc, stdout);
1288 } else {
1289 nc = 1;
1290 (void) putchar(' ');
1296 static void
1297 unget(int colno)
1299 if (Buffer) {
1300 if (*(Colpts[colno].c_ptr-1) != '\t')
1301 --(Colpts[colno].c_ptr);
1302 if (Colpts[colno].c_lno)
1303 Colpts[colno].c_lno--;
1304 } else {
1305 if ((Multi == 'm' && colno == 0) || Multi != 'm')
1306 if (Lnumb && !foldcol)
1307 Lnumb--;
1308 colno = (Multi == 'a') ? 0 : colno;
1309 (void) ungetwc(Files[colno].f_nextc, Files[colno].f_f);
1310 Files[colno].f_nextc = C;
1316 * Defer message about failure to open file to prevent messing up
1317 * alignment of page with tear perforations or form markers.
1318 * Treat empty file as special case and report as diagnostic.
1321 static FILE *
1322 mustopen(char *s, FILS *f)
1324 char *empty_file_msg = gettext("%s -- empty file");
1325 int c;
1327 if (*s == '\0') {
1328 f->f_name = STDINNAME();
1329 f->f_f = stdin;
1330 } else if ((f->f_f = fopen(f->f_name = s, "r")) == NULL) {
1331 s = ffiler(f->f_name);
1332 s = strcpy((char *)getspace((UNS) strlen(s) + 1), s);
1334 if (f->f_f != NULL) {
1335 errno = 0;
1336 f->f_nextc = _fgetwc_pr(f->f_f, &c);
1337 if (f->f_nextc != WEOF) {
1338 return (f->f_f);
1339 } else { /* WEOF */
1340 if (errno == EILSEQ) {
1341 f->f_nextc = (wchar_t)c;
1342 return (f->f_f);
1344 if (Multi == 'm')
1345 return (f->f_f);
1347 (void) sprintf(s = (char *)getspace((UNS) strlen(f->f_name)
1348 + 1 + (UNS) strlen(empty_file_msg)),
1349 empty_file_msg, f->f_name);
1350 (void) fclose(f->f_f);
1352 Error = 1;
1353 if (Report)
1354 if (Ttyout) { /* accumulate error reports */
1355 Lasterr = Lasterr->e_nextp =
1356 (ERR *) getspace((UNS) sizeof (ERR));
1357 Lasterr->e_nextp = NULL;
1358 Lasterr->e_mess = s;
1359 } else { /* ok to print error report now */
1360 cerror(s);
1361 (void) putc('\n', stderr);
1363 return (NULL);
1367 static ANY *
1368 getspace(UNS n)
1370 ANY *t;
1372 if ((t = (ANY *) malloc(n)) == NULL)
1373 die("out of space");
1374 return (t);
1378 static void
1379 die(char *s)
1381 ++Error;
1382 errprint();
1383 cerror(s);
1384 (void) putc('\n', stderr);
1385 exit(1);
1387 /*NOTREACHED*/
1391 static void
1392 errprint() /* print accumulated error reports */
1394 (void) fflush(stdout);
1395 for (; Err != NULL; Err = Err->e_nextp) {
1396 cerror(Err->e_mess);
1397 (void) putc('\n', stderr);
1399 done();
1403 static void
1404 fixtty()
1406 struct stat sbuf;
1408 setbuf(stdout, obuf);
1409 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1410 (void) signal(SIGINT, onintr);
1411 if (Ttyout = ttyname(fileno(stdout))) { /* is stdout a tty? */
1412 (void) stat(Ttyout, &sbuf);
1413 Mode = sbuf.st_mode; /* save permissions */
1414 (void) chmod(Ttyout, (S_IREAD|S_IWRITE));
1419 static void
1420 onintr()
1422 ++Error;
1423 errprint();
1424 _exit(1);
1428 static char *
1429 GETDATE() /* return date file was last modified */
1431 static char *now = NULL;
1432 static struct stat sbuf;
1433 static struct stat nbuf;
1435 if (Nfiles > 1 || Files->f_name == nulls) {
1436 if (now == NULL) {
1437 (void) time(&nbuf.st_mtime);
1438 (void) cftime(time_buf,
1439 dcgettext(NULL, FORMAT, LC_TIME),
1440 &nbuf.st_mtime);
1441 now = time_buf;
1443 return (now);
1444 } else {
1445 (void) stat(Files->f_name, &sbuf);
1446 (void) cftime(time_buf, dcgettext(NULL, FORMAT, LC_TIME),
1447 &sbuf.st_mtime);
1448 return (time_buf);
1453 static char *
1454 ffiler(char *s)
1456 static char buf[100];
1458 (void) sprintf(buf, gettext("can't open %s"), s);
1459 return (buf);
1463 static void
1464 usage(int rc)
1466 (void) fprintf(stderr, gettext(
1467 "usage: pr [-# [-w #] [-a]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] \\\n"
1468 " [-o #] [-l #] [-s[char]] [-h header] [-F] [+#] [file ...]\n\n"
1469 " pr [-m [-w #]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] [-0 #] \\\n"
1470 " [-l #] [-s[char]] [-h header] [-F] [+#] file1 file2 ...\n"
1472 exit(rc);
1475 static wint_t
1476 _fgetwc_pr(FILE *f, int *ic)
1478 int i;
1479 int len;
1480 char mbuf[MB_LEN_MAX];
1481 int c;
1482 wchar_t wc;
1484 c = getc(f);
1486 if (c == EOF)
1487 return (WEOF);
1488 if (mbcurmax == 1 || isascii(c)) {
1489 return ((wint_t)c);
1491 mbuf[0] = (char)c;
1492 for (i = 1; i < mbcurmax; i++) {
1493 c = getc(f);
1494 if (c == EOF) {
1495 break;
1496 } else {
1497 mbuf[i] = (char)c;
1500 mbuf[i] = 0;
1502 len = mbtowc(&wc, mbuf, i);
1503 if (len == -1) {
1504 /* Illegal character */
1505 /* Set the first byte to *ic */
1506 *ic = mbuf[0];
1507 /* Push back remaining characters */
1508 for (i--; i > 0; i--) {
1509 (void) ungetc(mbuf[i], f);
1511 errno = EILSEQ;
1512 return (WEOF);
1513 } else {
1514 /* Push back over-read characters */
1515 for (i--; i >= len; i--) {
1516 (void) ungetc(mbuf[i], f);
1518 return ((wint_t)wc);
1522 static size_t
1523 freadw(wchar_t *ptr, size_t nitems, FILE *f)
1525 size_t i;
1526 size_t ret;
1527 int c;
1528 wchar_t *p;
1529 wint_t wc;
1531 if (feof(f)) {
1532 return (0);
1535 p = ptr;
1536 ret = 0;
1537 for (i = 0; i < nitems; i++) {
1538 errno = 0;
1539 wc = _fgetwc_pr(f, &c);
1540 if (wc == WEOF) {
1541 if (errno == EILSEQ) {
1542 *p++ = (wchar_t)c;
1543 ret++;
1544 } else {
1545 return (ret);
1547 } else {
1548 *p++ = (wchar_t)wc;
1549 ret++;
1552 return (ret);