pr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
* All Rights Reserved
*/
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PR command (print files in pages and columns, with headings)
* 2+head+2+page[56]+5
*/
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <limits.h>
#include <wchar.h>
#include <errno.h>
#define ESC '\033'
#define LENGTH 66
#define LINEW 72
#define NUMW 5
#define MARGIN 10
#define DEFTAB 8
#define NFILES 10
#define STDINNAME() nulls
#define PROMPT() (void) putc('\7', stderr) /* BEL */
#define NOFILE nulls
#define ETABS (Inpos % Etabn)
#define NSEPC '\t'
#define HEAD gettext("%s %s Page %d\n\n\n"), date, head, Page
#define cerror(S) (void) fprintf(stderr, "pr: %s", gettext(S))
#define done() if (Ttyout) (void) chmod(Ttyout, Mode)
#define ALL_NUMS(s) (strspn(s, "0123456789") == strlen(s))
#define REMOVE_ARG(argc, argp) \
{ \
char **p = argp; \
while (*p != NULL) \
{ \
*p = *(p + 1); \
p++; \
} \
argc--; \
}
#define SQUEEZE_ARG(argp, ind, n) \
{ \
int i; \
for (i = ind; argp[i]; i++) \
argp[i] = argp[i + n]; \
}
/*
* ---date time format---
* b -- abbreviated month name
* e -- day of month
* H -- Hour (24 hour version)
* M -- Minute
* Y -- Year in the form ccyy
*/
#define FORMAT "%b %e %H:%M %Y"
typedef int ANY;
typedef unsigned int UNS;
typedef struct { FILE *f_f; char *f_name; wchar_t f_nextc; } FILS;
typedef struct {int fold; int skip; int eof; } foldinf;
typedef struct { wchar_t *c_ptr, *c_ptr0; long c_lno; int c_skip; } *COLP;
typedef struct err { struct err *e_nextp; char *e_mess; } ERR;
/*
* Global data.
*/
static FILS *Files;
static mode_t Mode;
static int Multi = 0;
static int Nfiles = 0;
static int Error = 0;
static char nulls[] = "";
static char *Ttyout;
static char obuf[BUFSIZ];
static char time_buf[50]; /* array to hold the time and date */
static long Lnumb = 0;
static FILE *Ttyin = stdin;
static int Dblspace = 1;
static int Fpage = 1;
static int Formfeed = 0;
static int Length = LENGTH;
static int Linew = 0;
static int Offset = 0;
static int Ncols = 0;
static int Pause = 0;
static wchar_t Sepc = 0;
static int Colw;
static int Plength;
static int Margin = MARGIN;
static int Numw;
static int Nsepc = NSEPC;
static int Report = 1;
static int Etabn = 0;
static wchar_t Etabc = '\t';
static int Itabn = 0;
static wchar_t Itabc = '\t';
static int fold = 0;
static int foldcol = 0;
static int alleof = 0;
static char *Head = NULL;
static wchar_t *Buffer = NULL, *Bufend, *Bufptr;
static UNS Buflen;
static COLP Colpts;
static foldinf *Fcol;
static int Page;
static wchar_t C = '\0';
static int Nspace;
static int Inpos;
static int Outpos;
static int Lcolpos;
static int Pcolpos;
static int Line;
static ERR *Err = NULL;
static ERR *Lasterr = (ERR *)&Err;
static int mbcurmax = 1;
/*
* Function prototypes.
*/
static void onintr();
static ANY *getspace();
static int findopt(int, char **);
static void fixtty();
static char *GETDATE();
static char *ffiler(char *);
static int print(char *);
static void putpage();
static void foldpage();
static void nexbuf();
static void foldbuf();
static void balance(int);
static int readbuf(wchar_t **, int, COLP);
static wint_t get(int);
static int put(wchar_t);
static void putspace();
static void unget(int);
static FILE *mustopen(char *, FILS *);
static void die(char *);
static void errprint();
static void usage(int);
static wint_t _fgetwc_pr(FILE *, int *);
static size_t freadw(wchar_t *, size_t, FILE *);
main(int argc, char **argv)
{
FILS fstr[NFILES];
int nfdone = 0;
/* Get locale variables for environment */
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
mbcurmax = MB_CUR_MAX;
Files = fstr;
for (argc = findopt(argc, argv); argc > 0; --argc, ++argv) {
if (Multi == 'm') {
if (Nfiles >= NFILES - 1) die("too many files");
if (mustopen(*argv, &Files[Nfiles++]) == NULL)
++nfdone; /* suppress printing */
} else {
if (print(*argv))
(void) fclose(Files->f_f);
++nfdone;
}
}
if (!nfdone) /* no files named, use stdin */
(void) print(NOFILE); /* on GCOS, use current file, if any */
if (Report) {
errprint(); /* print accumulated error reports */
exit(Error);
}
return (Error);
}
/*
* findopt() returns argc modified to be the number of explicitly supplied
* filenames, including '-', the explicit request to use stdin.
* argc == 0 implies that no filenames were supplied and stdin should be used.
* Options are striped from argv and only file names are returned.
*/
static int
findopt(int argc, char **argv)
{
int eargc = 0;
int c;
int mflg = 0;
int aflg = 0;
int optnum;
int argv_ind;
int end_opt;
int i;
fixtty();
/* Handle page number option */
for (optnum = 1, end_opt = 0; optnum < argc && !end_opt; optnum++) {
switch (*argv[optnum]) {
case '+':
/* check for all digits */
if (strlen(&argv[optnum][1]) !=
strspn(&argv[optnum][1], "0123456789")) {
(void) fprintf(stderr, gettext(
"pr: Badly formed number\n"));
exit(1);
}
if ((Fpage = (int)strtol(&argv[optnum][1],
(char **)NULL, 10)) < 0) {
(void) fprintf(stderr, gettext(
"pr: Badly formed number\n"));
exit(1);
}
REMOVE_ARG(argc, &argv[optnum]);
optnum--;
break;
case '-':
/* Check for end of options */
if (argv[optnum][1] == '-') {
end_opt++;
break;
}
break;
default:
end_opt++;
break;
}
}
/*
* Handle options with optional arguments.
* If optional arguments are present they may not be separated
* from the option letter.
*/
for (optnum = 1; optnum < argc; optnum++) {
if (argv[optnum][0] == '-' && argv[optnum][1] == '-')
/* End of options */
break;
if (argv[optnum][0] == '-' && argv[optnum][1] == '\0')
/* stdin file name */
continue;
if (argv[optnum][0] != '-')
/* not option */
continue;
for (argv_ind = 1; argv[optnum][argv_ind] != '\0'; argv_ind++) {
switch (argv[optnum][argv_ind]) {
case 'e':
SQUEEZE_ARG(argv[optnum], argv_ind, 1);
if ((c = argv[optnum][argv_ind]) != '\0' &&
!isdigit(c)) {
int r;
wchar_t wc;
r = mbtowc(&wc, &argv[optnum][argv_ind],
mbcurmax);
if (r == -1) {
(void) fprintf(stderr, gettext(
"pr: Illegal character in -e option\n"));
exit(1);
}
Etabc = wc;
SQUEEZE_ARG(argv[optnum], argv_ind, r);
}
if (isdigit(argv[optnum][argv_ind])) {
Etabn = (int)strtol(&argv[optnum]
[argv_ind], (char **)NULL, 10);
while (isdigit(argv[optnum][argv_ind]))
SQUEEZE_ARG(argv[optnum],
argv_ind, 1);
}
if (Etabn <= 0)
Etabn = DEFTAB;
argv_ind--;
break;
case 'i':
SQUEEZE_ARG(argv[optnum], argv_ind, 1);
if ((c = argv[optnum][argv_ind]) != '\0' &&
!isdigit(c)) {
int r;
wchar_t wc;
r = mbtowc(&wc, &argv[optnum][argv_ind],
mbcurmax);
if (r == -1) {
(void) fprintf(stderr, gettext(
"pr: Illegal character in -i option\n"));
exit(1);
}
Itabc = wc;
SQUEEZE_ARG(argv[optnum], argv_ind, r);
}
if (isdigit(argv[optnum][argv_ind])) {
Itabn = (int)strtol(&argv[optnum]
[argv_ind], (char **)NULL, 10);
while (isdigit(argv[optnum][argv_ind]))
SQUEEZE_ARG(argv[optnum],
argv_ind, 1);
}
if (Itabn <= 0)
Itabn = DEFTAB;
argv_ind--;
break;
case 'n':
++Lnumb;
SQUEEZE_ARG(argv[optnum], argv_ind, 1);
if ((c = argv[optnum][argv_ind]) != '\0' &&
!isdigit(c)) {
int r;
wchar_t wc;
r = mbtowc(&wc, &argv[optnum][argv_ind],
mbcurmax);
if (r == -1) {
(void) fprintf(stderr, gettext(
"pr: Illegal character in -n option\n"));
exit(1);
}
Nsepc = wc;
SQUEEZE_ARG(argv[optnum], argv_ind, r);
}
if (isdigit(argv[optnum][argv_ind])) {
Numw = (int)strtol(&argv[optnum]
[argv_ind], (char **)NULL, 10);
while (isdigit(argv[optnum][argv_ind]))
SQUEEZE_ARG(argv[optnum],
argv_ind, 1);
}
argv_ind--;
if (!Numw)
Numw = NUMW;
break;
case 's':
SQUEEZE_ARG(argv[optnum], argv_ind, 1);
if ((Sepc = argv[optnum][argv_ind]) == '\0')
Sepc = '\t';
else {
int r;
wchar_t wc;
r = mbtowc(&wc, &argv[optnum][argv_ind],
mbcurmax);
if (r == -1) {
(void) fprintf(stderr, gettext(
"pr: Illegal character in -s option\n"));
exit(1);
}
Sepc = wc;
SQUEEZE_ARG(argv[optnum], argv_ind, r);
}
argv_ind--;
break;
default:
break;
}
}
if (argv[optnum][0] == '-' && argv[optnum][1] == '\0') {
REMOVE_ARG(argc, &argv[optnum]);
optnum--;
}
}
/* Now get the other options */
while ((c = getopt(argc, argv, "0123456789adfFh:l:mo:prtw:"))
!= EOF) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
Ncols *= 10;
Ncols += c - '0';
break;
case 'a':
aflg++;
if (!Multi)
Multi = c;
break;
case 'd':
Dblspace = 2;
break;
case 'f':
++Formfeed;
++Pause;
break;
case 'h':
Head = optarg;
break;
case 'l':
if (strlen(optarg) != strspn(optarg, "0123456789"))
usage(1);
Length = (int)strtol(optarg, (char **)NULL, 10);
break;
case 'm':
mflg++;
Multi = c;
break;
case 'o':
if (strlen(optarg) != strspn(optarg, "0123456789"))
usage(1);
Offset = (int)strtol(optarg, (char **)NULL, 10);
break;
case 'p':
++Pause;
break;
case 'r':
Report = 0;
break;
case 't':
Margin = 0;
break;
case 'w':
if (strlen(optarg) != strspn(optarg, "0123456789"))
usage(1);
Linew = (int)strtol(optarg, (char **)NULL, 10);
break;
case 'F':
#ifdef XPG4
++Formfeed;
#else
fold++;
#endif
break;
case '?':
usage(2);
break;
default :
usage(2);
}
}
/* Count the file names and strip options */
for (i = 1; i < argc; i++) {
/* Check for explicit stdin */
if ((argv[i][0] == '-') && (argv[i][1] == '\0')) {
argv[eargc++][0] = '\0';
REMOVE_ARG(argc, &argv[i]);
if (i < optind)
optind--;
}
}
for (i = eargc; optind < argc; i++, optind++) {
argv[i] = argv[optind];
eargc++;
}
/* Check options */
if (Ncols == 0)
Ncols = 1;
if (mflg && (Ncols > 1)) {
(void) fprintf(stderr,
gettext("pr: only one of either -m or -column allowed\n"));
usage(1);
}
if (Ncols == 1 && fold)
Multi = 'm';
if (Length <= 0)
Length = LENGTH;
if (Length <= Margin)
Margin = 0;
Plength = Length - Margin/2;
if (Multi == 'm')
Ncols = eargc;
switch (Ncols) {
case 0:
Ncols = 1;
break;
case 1:
break;
default:
if (Etabn == 0) /* respect explicit tab specification */
Etabn = DEFTAB;
if (Itabn == 0)
Itabn = DEFTAB;
}
if ((Fcol = (foldinf *) malloc(sizeof (foldinf) * Ncols)) == NULL) {
(void) fprintf(stderr, gettext("pr: malloc failed\n"));
exit(1);
}
for (i = 0; i < Ncols; i++)
Fcol[i].fold = Fcol[i].skip = 0;
if (Linew == 0)
Linew = Ncols != 1 && Sepc == 0 ? LINEW : 512;
if (Lnumb) {
int numw;
if (Nsepc == '\t') {
if (Itabn == 0)
numw = Numw + DEFTAB - (Numw % DEFTAB);
else
numw = Numw + Itabn - (Numw % Itabn);
} else {
numw = Numw + ((iswprint(Nsepc)) ? 1 : 0);
}
Linew -= (Multi == 'm') ? numw : numw * Ncols;
}
if ((Colw = (Linew - Ncols + 1)/Ncols) < 1)
die("width too small");
if (Ncols != 1 && Multi == 0) {
/* Buflen should take the number of wide characters */
/* Not the size for Buffer */
Buflen = ((UNS) (Plength / Dblspace + 1)) *
2 * (Linew + 1);
/* Should allocate Buflen * sizeof (wchar_t) */
Buffer = (wchar_t *)getspace(Buflen * sizeof (wchar_t));
Bufptr = Bufend = &Buffer[Buflen];
Colpts = (COLP) getspace((UNS) ((Ncols + 1) *
sizeof (*Colpts)));
Colpts[0].c_lno = 0;
}
/* is stdin not a tty? */
if (Ttyout && (Pause || Formfeed) && !ttyname(fileno(stdin)))
Ttyin = fopen("/dev/tty", "r");
return (eargc);
}
static int
print(char *name)
{
static int notfirst = 0;
char *date = NULL;
char *head = NULL;
int c;
if (Multi != 'm' && mustopen(name, &Files[0]) == NULL)
return (0);
if (Multi == 'm' && Nfiles == 0 && mustopen(name, &Files[0]) == NULL)
die("cannot open stdin");
if (Buffer)
(void) ungetwc(Files->f_nextc, Files->f_f);
if (Lnumb)
Lnumb = 1;
for (Page = 0; ; putpage()) {
if (C == WEOF && !(fold && Buffer))
break;
if (Buffer)
nexbuf();
Inpos = 0;
if (get(0) == WEOF)
break;
(void) fflush(stdout);
if (++Page >= Fpage) {
/* Pause if -p and not first page */
if (Ttyout && Pause && !notfirst++) {
PROMPT(); /* prompt with bell and pause */
while ((c = getc(Ttyin)) != EOF && c != '\n')
;
}
if (Margin == 0)
continue;
if (date == NULL)
date = GETDATE();
if (head == NULL)
head = Head != NULL ? Head :
Nfiles < 2 ? Files->f_name : nulls;
(void) printf("\n\n");
Nspace = Offset;
putspace();
(void) printf(HEAD);
}
}
C = '\0';
return (1);
}
static void
putpage()
{
int colno;
if (fold) {
foldpage();
return;
}
for (Line = Margin / 2; ; (void) get(0)) {
for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
if (Lnumb && (C != WEOF) &&
(((colno == 0) && (Multi == 'm')) ||
(Multi != 'm'))) {
if (Page >= Fpage) {
putspace();
(void) printf("%*ld%wc", Numw, Buffer ?
Colpts[colno].c_lno++ :
Lnumb, Nsepc);
/* Move Outpos for number field */
Outpos += Numw;
if (Nsepc == '\t')
Outpos +=
DEFTAB - (Outpos % DEFTAB);
else
Outpos++;
}
++Lnumb;
}
for (Lcolpos = 0, Pcolpos = 0;
C != '\n' && C != '\f' && C != WEOF;
(void) get(colno))
(void) put(C);
if ((C == WEOF) || (++colno == Ncols) ||
((C == '\n') && (get(colno) == WEOF)))
break;
if (Sepc)
(void) put(Sepc);
else if ((Nspace += Colw - Lcolpos + 1) < 1)
Nspace = 1;
}
if (C == WEOF) {
if (Margin != 0)
break;
if (colno != 0)
(void) put('\n');
return;
}
if (C == '\f')
break;
(void) put('\n');
if (Dblspace == 2 && Line < Plength)
(void) put('\n');
if (Line >= Plength)
break;
}
if (Formfeed)
(void) put('\f');
else
while (Line < Length)
(void) put('\n');
}
static void
foldpage()
{
int colno;
int keep;
int i;
int pLcolpos;
static int sl;
for (Line = Margin / 2; ; (void) get(0)) {
for (Nspace = Offset, colno = 0, Outpos = 0; C != '\f'; ) {
if (Lnumb && Multi == 'm' && foldcol) {
if (!Fcol[colno].skip) {
unget(colno);
putspace();
if (!colno) {
for (i = 0; i <= Numw; i++)
(void) printf(" ");
(void) printf("%wc", Nsepc);
}
for (i = 0; i <= Colw; i++)
(void) printf(" ");
(void) put(Sepc);
if (++colno == Ncols)
break;
(void) get(colno);
continue;
} else if (!colno)
Lnumb = sl;
}
if (Lnumb && (C != WEOF) &&
((colno == 0 && Multi == 'm') || (Multi != 'm'))) {
if (Page >= Fpage) {
putspace();
if ((foldcol &&
Fcol[colno].skip && Multi != 'a') ||
(Fcol[0].fold && Multi == 'a') ||
(Buffer && Colpts[colno].c_skip)) {
for (i = 0; i < Numw; i++)
(void) printf(" ");
(void) printf("%wc", Nsepc);
if (Buffer) {
Colpts[colno].c_lno++;
Colpts[colno].c_skip =
0;
}
}
else
(void) printf("%*ld%wc", Numw, Buffer ?
Colpts[colno].c_lno++ :
Lnumb, Nsepc);
}
sl = Lnumb++;
}
pLcolpos = 0;
for (Lcolpos = 0, Pcolpos = 0;
C != '\n' && C != '\f' && C != WEOF;
(void) get(colno)) {
if (put(C)) {
unget(colno);
Fcol[(Multi == 'a') ? 0 : colno].fold
= 1;
break;
} else if (Multi == 'a') {
Fcol[0].fold = 0;
}
pLcolpos = Lcolpos;
}
if (Buffer) {
alleof = 1;
for (i = 0; i < Ncols; i++)
if (!Fcol[i].eof)
alleof = 0;
if (alleof || ++colno == Ncols)
break;
} else if (C == EOF || ++colno == Ncols)
break;
keep = C;
(void) get(colno);
if (keep == '\n' && C == WEOF)
break;
if (Sepc)
(void) put(Sepc);
else if ((Nspace += Colw - pLcolpos + 1) < 1)
Nspace = 1;
}
foldcol = 0;
if (Lnumb && Multi != 'a') {
for (i = 0; i < Ncols; i++) {
Fcol[i].skip = Fcol[i].fold;
foldcol += Fcol[i].fold;
Fcol[i].fold = 0;
}
}
if (C == WEOF) {
if (Margin != 0)
break;
if (colno != 0)
(void) put('\n');
return;
}
if (C == '\f')
break;
(void) put('\n');
(void) fflush(stdout);
if (Dblspace == 2 && Line < Plength)
(void) put('\n');
if (Line >= Plength)
break;
}
if (Formfeed)
(void) put('\f');
else while (Line < Length)
(void) put('\n');
}
static void
nexbuf()
{
wchar_t *s = Buffer;
COLP p = Colpts;
int j;
int c;
int bline = 0;
wchar_t wc;
if (fold) {
foldbuf();
return;
}
for (; ; ) {
p->c_ptr0 = p->c_ptr = s;
if (p == &Colpts[Ncols])
return;
(p++)->c_lno = Lnumb + bline;
for (j = (Length - Margin)/Dblspace; --j >= 0; ++bline) {
for (Inpos = 0; ; ) {
errno = 0;
wc = _fgetwc_pr(Files->f_f, &c);
if (wc == WEOF) {
/* If there is an illegal character, */
/* handle it as a byte sequence. */
if (errno == EILSEQ) {
if (Inpos < Colw - 1) {
*s = c;
if (++s >= Bufend)
die("page-buffer overflow");
}
Inpos++;
Error++;
return;
} else {
/* Real EOF */
for (*s = WEOF; p <= &Colpts[Ncols]; ++p)
p->c_ptr0 = p->c_ptr = s;
balance(bline);
return;
}
}
if (isascii(wc)) {
if (isprint(wc))
Inpos++;
} else if (iswprint(wc)) {
Inpos += wcwidth(wc);
}
if (Inpos <= Colw || wc == '\n') {
*s = wc;
if (++s >= Bufend)
die("page-buffer overflow");
}
if (wc == '\n')
break;
switch (wc) {
case '\b':
if (Inpos == 0)
--s;
/*FALLTHROUGH*/
case ESC:
if (Inpos > 0)
--Inpos;
}
}
}
}
}
static void
foldbuf()
{
int num;
int i;
int colno = 0;
int size = Buflen;
wchar_t *s;
wchar_t *d;
COLP p = Colpts;
for (i = 0; i < Ncols; i++)
Fcol[i].eof = 0;
d = Buffer;
if (Bufptr != Bufend) {
s = Bufptr;
while (s < Bufend)
*d++ = *s++;
size -= (Bufend - Bufptr);
}
Bufptr = Buffer;
p->c_ptr0 = p->c_ptr = Buffer;
if (p->c_lno == 0) {
p->c_lno = Lnumb;
p->c_skip = 0;
} else {
p->c_lno = Colpts[Ncols-1].c_lno;
p->c_skip = Colpts[Ncols].c_skip;
if (p->c_skip)
p->c_lno--;
}
if ((num = freadw(d, size, Files->f_f)) != size) {
for (*(d+num) = WEOF; (++p) <= &Colpts[Ncols]; ) {
p->c_ptr0 = p->c_ptr = (d+num);
}
balance(0);
return;
}
i = (Length - Margin) / Dblspace;
do {
(void) readbuf(&Bufptr, i, p++);
} while (++colno < Ncols);
}
static void
balance(int bline) /* line balancing for last page */
{
wchar_t *s = Buffer;
COLP p = Colpts;
int colno = 0;
int j;
int c;
int l;
int lines;
if (!fold) {
c = bline % Ncols;
l = (bline + Ncols - 1)/Ncols;
bline = 0;
do {
for (j = 0; j < l; ++j)
while (*s++ != '\n')
;
(++p)->c_lno = Lnumb + (bline += l);
p->c_ptr0 = p->c_ptr = s;
if (++colno == c)
--l;
} while (colno < Ncols - 1);
} else {
lines = readbuf(&s, 0, 0);
l = (lines + Ncols - 1)/Ncols;
if (l > ((Length - Margin) / Dblspace)) {
l = (Length - Margin) / Dblspace;
c = Ncols;
} else {
c = lines % Ncols;
}
s = Buffer;
do {
(void) readbuf(&s, l, p++);
if (++colno == c)
--l;
} while (colno < Ncols);
Bufptr = s;
}
}
static int
readbuf(wchar_t **s, int lincol, COLP p)
{
int lines = 0;
int chars = 0;
int width;
int nls = 0;
int move;
int skip = 0;
int decr = 0;
width = (Ncols == 1) ? Linew : Colw;
while (**s != WEOF) {
switch (**s) {
case '\n':
lines++; nls++; chars = 0; skip = 0;
break;
case '\b':
case ESC:
if (chars) chars--;
break;
case '\t':
move = Itabn - ((chars + Itabn) % Itabn);
move = (move < width-chars) ? move :
width-chars;
chars += move;
default:
if (isascii(**s)) {
if (isprint(**s))
chars++;
} else if (iswprint(**s)) {
chars += wcwidth(**s);
}
}
if (chars > width) {
lines++;
skip++;
decr++;
chars = 0;
}
if (lincol && lines == lincol) {
(p+1)->c_lno = p->c_lno + nls;
(++p)->c_skip = skip;
if (**s == '\n') (*s)++;
p->c_ptr0 = p->c_ptr = (wchar_t *)*s;
return (0);
}
if (decr)
decr = 0;
else
(*s)++;
}
return (lines);
}
static wint_t
get(int colno)
{
static int peekc = 0;
COLP p;
FILS *q;
int c;
wchar_t wc, w;
if (peekc) {
peekc = 0;
wc = Etabc;
} else if (Buffer) {
p = &Colpts[colno];
if (p->c_ptr >= (p+1)->c_ptr0)
wc = WEOF;
else if ((wc = *p->c_ptr) != WEOF)
++p->c_ptr;
if (fold && wc == WEOF)
Fcol[colno].eof = 1;
} else if ((wc =
(q = &Files[Multi == 'a' ? 0 : colno])->f_nextc) == WEOF) {
for (q = &Files[Nfiles]; --q >= Files && q->f_nextc == WEOF; )
;
if (q >= Files)
wc = '\n';
} else {
errno = 0;
w = _fgetwc_pr(q->f_f, &c);
if (w == WEOF && errno == EILSEQ) {
q->f_nextc = (wchar_t)c;
} else {
q->f_nextc = w;
}
}
if (Etabn != 0 && wc == Etabc) {
++Inpos;
peekc = ETABS;
wc = ' ';
return (C = wc);
}
if (wc == WEOF)
return (C = wc);
if (isascii(wc)) {
if (isprint(wc)) {
Inpos++;
return (C = wc);
}
} else if (iswprint(wc)) {
Inpos += wcwidth(wc);
return (C = wc);
}
switch (wc) {
case '\b':
case ESC:
if (Inpos > 0)
--Inpos;
break;
case '\f':
if (Ncols == 1)
break;
wc = '\n';
/* FALLTHROUGH */
case '\n':
case '\r':
Inpos = 0;
break;
}
return (C = wc);
}
static int
put(wchar_t wc)
{
int move = 0;
int width = Colw;
int sp = Lcolpos;
if (fold && Ncols == 1)
width = Linew;
switch (wc) {
case ' ':
/* If column not full or this is separator char */
if ((!fold && Ncols < 2) || (Lcolpos < width) ||
((Sepc == wc) && (Lcolpos == width))) {
++Nspace;
++Lcolpos;
}
if (fold && sp == Lcolpos)
if (Lcolpos >= width)
return (1);
return (0);
case '\t':
if (Itabn == 0)
break;
/* If column not full or this is separator char */
if ((Lcolpos < width) ||
((Sepc == wc) && (Lcolpos == width))) {
move = Itabn - ((Lcolpos + Itabn) % Itabn);
move = (move < width-Lcolpos) ? move : width-Lcolpos;
Nspace += move;
Lcolpos += move;
}
if (fold && sp == Lcolpos)
if (Lcolpos >= width)
return (1);
return (0);
case '\b':
if (Lcolpos == 0)
return (0);
if (Nspace > 0) {
--Nspace;
--Lcolpos;
return (0);
}
if (Lcolpos > Pcolpos) {
--Lcolpos;
return (0);
}
/*FALLTHROUGH*/
case ESC:
move = -1;
break;
case '\n':
++Line;
/*FALLTHROUGH*/
case '\r':
case '\f':
Pcolpos = 0;
Lcolpos = 0;
Nspace = 0;
Outpos = 0;
/* FALLTHROUGH */
default:
if (isascii(wc)) {
if (isprint(wc))
move = 1;
else
move = 0;
} else if (iswprint(wc)) {
move = wcwidth(wc);
} else {
move = 0;
}
break;
}
if (Page < Fpage)
return (0);
if (Lcolpos > 0 || move > 0)
Lcolpos += move;
putspace();
/* If column not full or this is separator char */
if ((!fold && Ncols < 2) || (Lcolpos <= width) ||
((Sepc == wc) && (Lcolpos > width))) {
(void) fputwc(wc, stdout);
Outpos += move;
Pcolpos = Lcolpos;
}
if (fold && Lcolpos > width)
return (1);
return (0);
}
static void
putspace(void)
{
int nc = 0;
for (; Nspace > 0; Outpos += nc, Nspace -= nc) {
#ifdef XPG4
/* XPG4: -i: replace multiple SPACE chars with tab chars */
if ((Nspace >= 2 && Itabn > 0 &&
Nspace >= (nc = Itabn - Outpos % Itabn)) && !fold) {
#else
/* Solaris: -i: replace white space with tab chars */
if ((Itabn > 0 && Nspace >= (nc = Itabn - Outpos % Itabn)) &&
!fold) {
#endif
(void) fputwc(Itabc, stdout);
} else {
nc = 1;
(void) putchar(' ');
}
}
}
static void
unget(int colno)
{
if (Buffer) {
if (*(Colpts[colno].c_ptr-1) != '\t')
--(Colpts[colno].c_ptr);
if (Colpts[colno].c_lno)
Colpts[colno].c_lno--;
} else {
if ((Multi == 'm' && colno == 0) || Multi != 'm')
if (Lnumb && !foldcol)
Lnumb--;
colno = (Multi == 'a') ? 0 : colno;
(void) ungetwc(Files[colno].f_nextc, Files[colno].f_f);
Files[colno].f_nextc = C;
}
}
/*
* Defer message about failure to open file to prevent messing up
* alignment of page with tear perforations or form markers.
* Treat empty file as special case and report as diagnostic.
*/
static FILE *
mustopen(char *s, FILS *f)
{
char *empty_file_msg = gettext("%s -- empty file");
int c;
if (*s == '\0') {
f->f_name = STDINNAME();
f->f_f = stdin;
} else if ((f->f_f = fopen(f->f_name = s, "r")) == NULL) {
s = ffiler(f->f_name);
s = strcpy((char *)getspace((UNS) strlen(s) + 1), s);
}
if (f->f_f != NULL) {
errno = 0;
f->f_nextc = _fgetwc_pr(f->f_f, &c);
if (f->f_nextc != WEOF) {
return (f->f_f);
} else { /* WEOF */
if (errno == EILSEQ) {
f->f_nextc = (wchar_t)c;
return (f->f_f);
}
if (Multi == 'm')
return (f->f_f);
}
(void) sprintf(s = (char *)getspace((UNS) strlen(f->f_name)
+ 1 + (UNS) strlen(empty_file_msg)),
empty_file_msg, f->f_name);
(void) fclose(f->f_f);
}
Error = 1;
if (Report)
if (Ttyout) { /* accumulate error reports */
Lasterr = Lasterr->e_nextp =
(ERR *) getspace((UNS) sizeof (ERR));
Lasterr->e_nextp = NULL;
Lasterr->e_mess = s;
} else { /* ok to print error report now */
cerror(s);
(void) putc('\n', stderr);
}
return ((FILE *)NULL);
}
static ANY *
getspace(UNS n)
{
ANY *t;
if ((t = (ANY *) malloc(n)) == NULL)
die("out of space");
return (t);
}
static void
die(char *s)
{
++Error;
errprint();
cerror(s);
(void) putc('\n', stderr);
exit(1);
/*NOTREACHED*/
}
static void
errprint() /* print accumulated error reports */
{
(void) fflush(stdout);
for (; Err != NULL; Err = Err->e_nextp) {
cerror(Err->e_mess);
(void) putc('\n', stderr);
}
done();
}
static void
fixtty()
{
struct stat sbuf;
setbuf(stdout, obuf);
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
(void) signal(SIGINT, onintr);
if (Ttyout = ttyname(fileno(stdout))) { /* is stdout a tty? */
(void) stat(Ttyout, &sbuf);
Mode = sbuf.st_mode; /* save permissions */
(void) chmod(Ttyout, (S_IREAD|S_IWRITE));
}
}
static void
onintr()
{
++Error;
errprint();
_exit(1);
}
static char *
GETDATE() /* return date file was last modified */
{
static char *now = NULL;
static struct stat sbuf;
static struct stat nbuf;
if (Nfiles > 1 || Files->f_name == nulls) {
if (now == NULL) {
(void) time(&nbuf.st_mtime);
(void) cftime(time_buf,
dcgettext(NULL, FORMAT, LC_TIME),
&nbuf.st_mtime);
now = time_buf;
}
return (now);
} else {
(void) stat(Files->f_name, &sbuf);
(void) cftime(time_buf, dcgettext(NULL, FORMAT, LC_TIME),
&sbuf.st_mtime);
return (time_buf);
}
}
static char *
ffiler(char *s)
{
static char buf[100];
(void) sprintf(buf, gettext("can't open %s"), s);
return (buf);
}
static void
usage(int rc)
{
(void) fprintf(stderr, gettext(
"usage: pr [-# [-w #] [-a]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] \\\n"
" [-o #] [-l #] [-s[char]] [-h header] [-F] [+#] [file ...]\n\n"
" pr [-m [-w #]] [-e[c][#]] [-i[c][#]] [-drtfp] [-n[c][#]] [-0 #] \\\n"
" [-l #] [-s[char]] [-h header] [-F] [+#] file1 file2 ...\n"
));
exit(rc);
}
static wint_t
_fgetwc_pr(FILE *f, int *ic)
{
int i;
int len;
char mbuf[MB_LEN_MAX];
int c;
wchar_t wc;
c = getc(f);
if (c == EOF)
return (WEOF);
if (mbcurmax == 1 || isascii(c)) {
return ((wint_t)c);
}
mbuf[0] = (char)c;
for (i = 1; i < mbcurmax; i++) {
c = getc(f);
if (c == EOF) {
break;
} else {
mbuf[i] = (char)c;
}
}
mbuf[i] = 0;
len = mbtowc(&wc, mbuf, i);
if (len == -1) {
/* Illegal character */
/* Set the first byte to *ic */
*ic = mbuf[0];
/* Push back remaining characters */
for (i--; i > 0; i--) {
(void) ungetc(mbuf[i], f);
}
errno = EILSEQ;
return (WEOF);
} else {
/* Push back over-read characters */
for (i--; i >= len; i--) {
(void) ungetc(mbuf[i], f);
}
return ((wint_t)wc);
}
}
static size_t
freadw(wchar_t *ptr, size_t nitems, FILE *f)
{
size_t i;
size_t ret;
int c;
wchar_t *p;
wint_t wc;
if (feof(f)) {
return (0);
}
p = ptr;
ret = 0;
for (i = 0; i < nitems; i++) {
errno = 0;
wc = _fgetwc_pr(f, &c);
if (wc == WEOF) {
if (errno == EILSEQ) {
*p++ = (wchar_t)c;
ret++;
} else {
return (ret);
}
} else {
*p++ = (wchar_t)wc;
ret++;
}
}
return (ret);
}