diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:33:43 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:33:43 +0000 |
commit | f9ab90d9d6d02989a075d0f0074496d5b1045e4b (patch) | |
tree | add7e996bac5289cdc55e6935750c352505560a9 /usr.bin/pr | |
parent | be22b15ae2ff8d7fe06b6e14fddf0c5b444a95da (diff) | |
download | FreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.zip FreeBSD-src-f9ab90d9d6d02989a075d0f0074496d5b1045e4b.tar.gz |
BSD 4.4 Lite Usr.bin Sources
Diffstat (limited to 'usr.bin/pr')
-rw-r--r-- | usr.bin/pr/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/pr/egetopt.c | 215 | ||||
-rw-r--r-- | usr.bin/pr/extern.h | 60 | ||||
-rw-r--r-- | usr.bin/pr/pr.1 | 347 | ||||
-rw-r--r-- | usr.bin/pr/pr.c | 1804 | ||||
-rw-r--r-- | usr.bin/pr/pr.h | 72 |
6 files changed, 2504 insertions, 0 deletions
diff --git a/usr.bin/pr/Makefile b/usr.bin/pr/Makefile new file mode 100644 index 0000000..70a921d --- /dev/null +++ b/usr.bin/pr/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= pr +SRCS= pr.c egetopt.c + +.include <bsd.prog.mk> diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c new file mode 100644 index 0000000..dd5bbbd --- /dev/null +++ b/usr.bin/pr/egetopt.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)egetopt.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +/* + * egetopt: get option letter from argument vector (an extended + * version of getopt). + * + * Non standard additions to the ostr specs are: + * 1) '?': immediate value following arg is optional (no white space + * between the arg and the value) + * 2) '#': +/- followed by a number (with an optional sign but + * no white space between the arg and the number). The - may be + * combined with other options, but the + cannot. + */ + +int eopterr = 1; /* if error message should be printed */ +int eoptind = 1; /* index into parent argv vector */ +int eoptopt; /* character checked for validity */ +char *eoptarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define EMSG "" + +int +egetopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + static int delim; /* which option delimeter */ + register char *p; + static char savec = '\0'; + + if (savec != '\0') { + *place = savec; + savec = '\0'; + } + + if (!*place) { + /* + * update scanning pointer + */ + if ((eoptind >= nargc) || + ((*(place = nargv[eoptind]) != '-') && (*place != '+'))) { + place = EMSG; + return (EOF); + } + + delim = (int)*place; + if (place[1] && *++place == '-' && !place[1]) { + /* + * found "--" + */ + ++eoptind; + place = EMSG; + return (EOF); + } + } + + /* + * check option letter + */ + if ((eoptopt = (int)*place++) == (int)':' || (eoptopt == (int)'?') || + !(oli = strchr(ostr, eoptopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means EOF when by itself. + */ + if ((eoptopt == (int)'-') && !*place) + return (EOF); + if (strchr(ostr, '#') && (isdigit(eoptopt) || + (((eoptopt == (int)'-') || (eoptopt == (int)'+')) && + isdigit(*place)))) { + /* + * # option: +/- with a number is ok + */ + for (p = place; *p != '\0'; ++p) { + if (!isdigit(*p)) + break; + } + eoptarg = place-1; + + if (*p == '\0') { + place = EMSG; + ++eoptind; + } else { + place = p; + savec = *p; + *place = '\0'; + } + return (delim); + } + + if (!*place) + ++eoptind; + if (eopterr) { + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, "%s: illegal option -- %c\n", + p, eoptopt); + } + return (BADCH); + } + if (delim == (int)'+') { + /* + * '+' is only allowed with numbers + */ + if (!*place) + ++eoptind; + if (eopterr) { + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, + "%s: illegal '+' delimiter with option -- %c\n", + p, eoptopt); + } + return (BADCH); + } + ++oli; + if ((*oli != ':') && (*oli != '?')) { + /* + * don't need argument + */ + eoptarg = NULL; + if (!*place) + ++eoptind; + return (eoptopt); + } + + if (*place) { + /* + * no white space + */ + eoptarg = place; + } else if (*oli == '?') { + /* + * no arg, but NOT required + */ + eoptarg = NULL; + } else if (nargc <= ++eoptind) { + /* + * no arg, but IS required + */ + place = EMSG; + if (eopterr) { + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", p, + eoptopt); + } + return (BADCH); + } else { + /* + * arg has white space + */ + eoptarg = nargv[eoptind]; + } + place = EMSG; + ++eoptind; + return (eoptopt); +} diff --git a/usr.bin/pr/extern.h b/usr.bin/pr/extern.h new file mode 100644 index 0000000..1456b55 --- /dev/null +++ b/usr.bin/pr/extern.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +extern int eoptind; +extern char *eoptarg; + +void addnum __P((char *, int, int)); +int egetopt __P((int, char * const *, const char *)); +void flsh_errs __P((void)); +int horzcol __P((int, char **)); +int inln __P((FILE *, char *, int, int *, int, int *)); +int inskip __P((FILE *, int, int)); +void mfail __P((void)); +int mulfile __P((int, char **)); +FILE *nxtfile __P((int, char **, char **, char *, int)); +int onecol __P((int, char **)); +int otln __P((char *, int, int *, int *, int)); +void pfail __P((void)); +int prhead __P((char *, char *, int)); +int prtail __P((int, int)); +int setup __P((int, char **)); +void terminate __P((int)); +void usage __P((void)); +int vertcol __P((int, char **)); diff --git a/usr.bin/pr/pr.1 b/usr.bin/pr/pr.1 new file mode 100644 index 0000000..d8d55be --- /dev/null +++ b/usr.bin/pr/pr.1 @@ -0,0 +1,347 @@ +.\" Copyright (c) 1991 Keith Muller. +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pr.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt PR 1 +.Os BSD 4.4 +.Sh NAME +.Nm pr +.Nd print files +.Sh SYNOPSIS +.Nm pr +.Bk -words +.Op Ar \&+page +.Ek +.Bk -words +.Op Fl Ar column +.Ek +.Op Fl adFmrt +.Bk -words +.Oo +.Op Fl e +.Op Ar char +.Op Ar gap +.Oc +.Ek +.Bk -words +.Op Fl h Ar header +.Ek +.Bk -words +.Oo +.Op Fl i +.Op Ar char +.Op Ar gap +.Oc +.Ek +.Bk -words +.Op Fl l Ar lines +.Ek +.Bk -words +.Op Fl o Ar offset +.Ek +.Bk -words +.Oo +.Op Fl s +.Op Ar char +.Oc +.Ek +.Bk -words +.Oo +.Op Fl n +.Op Ar char +.Op Ar width +.Oc +.Ek +.Bk -words +.Op Fl w Ar width +.Ek +.Op - +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm pr +utility is a printing and pagination filter for text files. +When multiple input files are specified, each is read, formatted, +and written to standard output. +By default, the input is separated into 66-line pages, each with +.sp +.in +2 +.ti -2 +\(bu A 5-line header with the page number, date, time, and +the pathname of the file. +.sp +.ti -2 +\(bu A 5-line trailer consisting of blank lines. +.in -2 +.Pp +If standard output is associated with a terminal, +diagnostic messages are suppressed until the +.Nm pr +utility has completed processing. +.Pp +When multiple column output is specified, +text columns are of equal width. +By default text columns are separated by at least one +.Em <blank>. +Input lines that do not fit into a text column are truncated. +Lines are not truncated under single column output. +.Sh OPTIONS +.Pp +In the following option descriptions, column, lines, offset, page, and +width are positive decimal integers and gap is a nonnegative decimal integer. +.Bl -tag -width 4n +.It Ar \&+page +Begin output at page number +.Ar page +of the formatted input. +.It Fl Ar column +Produce output that is +.Ar columns +wide (default is 1) that is written vertically +down each column in the order in which the text +is received from the input file. +The options +.Fl e +and +.Fl i +are assumed. +This option should not be used with +.Fl m . +When used with +.Fl t , +the minimum number of lines is used to display the output. +.It Fl a +Modify the effect of the +.Fl column +option so that the columns are filled across the page in a round-robin order +(e.g., when column is 2, the first input line heads column +1, the second heads column 2, the third is the second line +in column 1, etc.). +This option requires the use of the +.Fl column +option. +.It Fl d +Produce output that is double spaced. An extra +.Em <newline> +character is output following every <newline> found in the input. +.It Fl e Ar \&[char\&]\&[gap\&] +Expand each input <tab> to the next greater column +position specified by the formula +.Ar n*gap+1 , +where +.Em n +is an integer > 0. +If +.Ar gap +is zero or is omitted the default is 8. +All +.Em <tab> +characters in the input are expanded into the appropriate +number of +.Em <space>s . +If any nondigit character, +.Ar char , +is specified, it is used as the input tab character. +.It Fl F +Use a +.Em <form-feed> +character for new pages, +instead of the default behavior that uses a +sequence o +.Em <newline> +characters. +.It Fl h Ar header +.Ar header +Use the string +.Ar header +to replace the +.Ar file name +in the header line. +.It Fl i Ar \&[char\&]\&[gap\&] +In output, replace multiple <space>s with <tab>s whenever two or more +adjacent <space>s reach column positions +.Ar gap+1 , +.Ar 2*gap+1 , +etc. +If +.Ar gap +is zero or omitted, default +.Em <tab> +settings at every eighth column position +is used. +If any nondigit character, +.Ar char , +is specified, it is used as the output +.Em <tab> +character. +.It Fl l Ar lines +Override the 66 line default and reset the page length to +.Ar lines. +If +.Ar lines +is not greater than the sum of both the header and trailer +depths (in lines), the +.Nm pr +utility suppresses output of both the header and trailer, as if the +.Fl t +option were in effect. +.It Fl m +Merge the contents of multiple files. +One line from each file specified by a file operand is +written side by side into text columns of equal fixed widths, in +terms of the number of column positions. +The number of text columns depends on the number of +file operands successfully opened. +The maximum number of files merged depends on page width and the +per process open file limit. +The options +.Fl e +and +.Fl i +are assumed. +.It Fl n Ar \&[char\&]\&[width\&] +Provide +.Ar width +digit line numbering. +The default for +.Ar width , +if not specified, is 5. +The number occupies the first +.Ar width +column positions of each text column or each line of +.Fl m +output. +If +.Ar char +(any nondigit character) is given, it is appended to the line number to +separate it from whatever follows. The default for +.Ar char +is a +.Em <tab> . +Line numbers longer than +.Ar width +columns are truncated. +.It Fl o Ar offset +Each line of output is preceded by +.Ar offset +.Em <spaces>s . +If the +.FL o +option is not specified, the default is zero. +The space taken is in addition to the output line width. +.It Fl r +Write no diagnostic reports on failure to open a file. +.It Fl s Ar char +Separate text columns by the single character +.Ar char +instead of by the appropriate number of +.Em <space>s +(default for +.Ar char +is the +.Em <tab> +character). +.It Fl t +Print neither the five-line identifying +header nor the five-line trailer usually supplied for each page. +Quit printing after the last line of each file without spacing to the +end of the page. +.It Fl w Ar width +Set the width of the line to +.Ar width +column positions for multiple text-column output only. +If the +.Fl w +option is not specified and the +.Fl s +option is not specified, the default width is 72. +If the +.Fl w +option is not specified and the +.Fl s +option is specified, the default width is 512. +.It Ar file +A pathname of a file to be printed. +If no +.Ar file +operands are specified, or if a +.Ar file +operand is +.Sq Fl , +the standard input is used. +The standard input is used only if no +.Ar file +operands are specified, or if a +.Ar file +operand is +.Sq Fl . +.El +.Pp +The +.Fl s +option does not allow the option letter to be separated from its +argument, and the options +.Fl e , +.Fl i , +and +.Fl n +require that both arguments, if present, not be separated from the option +letter. +.Sh ERRORS +.Pp +If +.Nm pr +receives an interrupt while printing to a terminal, it +flushes all accumulated error messages to the screen before +terminating. +.Pp +The +.Nm pr +utility exits 0 on success, and 1 if an error occurs. +.Pp +Error messages are written to standard error during the printing +process (if output is redirected) or after all successful +file printing is complete (when printing to a terminal). +.Sh SEE ALSO +.Xr cat 1 , +.Xr more 1 +.Sh STANDARDS +The +.Nm pr +utility is +.St -p1003.2 +compatible. diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c new file mode 100644 index 0000000..bbc7e9b --- /dev/null +++ b/usr.bin/pr/pr.c @@ -0,0 +1,1804 @@ +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pr.c 8.2 (Berkeley) 4/16/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pr.h" +#include "extern.h" + +/* + * pr: a printing and pagination filter. If multiple input files + * are specified, each is read, formatted, and written to standard + * output. By default, input is seperated into 66-line pages, each + * with a header that includes the page number, date, time and the + * files pathname. + * + * Complies with posix P1003.2/D11 + */ + +/* + * parameter variables + */ +int pgnm; /* starting page number */ +int clcnt; /* number of columns */ +int colwd; /* column data width - multiple columns */ +int across; /* mult col flag; write across page */ +int dspace; /* double space flag */ +char inchar; /* expand input char */ +int ingap; /* expand input gap */ +int formfeed; /* use formfeed as trailer */ +char *header; /* header name instead of file name */ +char ochar; /* contract output char */ +int ogap; /* contract output gap */ +int lines; /* number of lines per page */ +int merge; /* merge multiple files in output */ +char nmchar; /* line numbering append char */ +int nmwd; /* width of line number field */ +int offst; /* number of page offset spaces */ +int nodiag; /* do not report file open errors */ +char schar; /* text column separation character */ +int sflag; /* -s option for multiple columns */ +int nohead; /* do not write head and trailer */ +int pgwd; /* page width with multiple col output */ +char *timefrmt; /* time conversion string */ + +/* + * misc globals + */ +FILE *err; /* error message file pointer */ +int addone; /* page length is odd with double space */ +int errcnt; /* error count on file processing */ +char digs[] = "0123456789"; /* page number translation map */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ret_val; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, terminate); + ret_val = setup(argc, argv); + if (!ret_val) { + /* + * select the output format based on options + */ + if (merge) + ret_val = mulfile(argc, argv); + else if (clcnt == 1) + ret_val = onecol(argc, argv); + else if (across) + ret_val = horzcol(argc, argv); + else + ret_val = vertcol(argc, argv); + } else + usage(); + flsh_errs(); + if (errcnt || ret_val) + exit(1); + return(0); +} + +/* + * onecol: print files with only one column of output. + * Line length is unlimited. + */ +int +onecol(argc, argv) + int argc; + char *argv[]; +{ + register int cnt = -1; + register int off; + register int lrgln; + register int linecnt; + register int num; + int lncnt; + int pagecnt; + int ips; + int ops; + int cps; + char *obuf; + char *lbuf; + char *nbuf; + char *hbuf; + char *ohbuf; + FILE *inf; + char *fname; + int mor; + + if (nmwd) + num = nmwd + 1; + else + num = 0; + off = num + offst; + + /* + * allocate line buffer + */ + if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + /* + * allocate header buffer + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + ohbuf = hbuf + offst; + nbuf = obuf + offst; + lbuf = nbuf + num; + if (num) + nbuf[--num] = nmchar; + if (offst) { + (void)memset(obuf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + /* + * skip to specified page + */ + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + linecnt = 0; + lrgln = 0; + ops = 0; + ips = 0; + cps = 0; + + /* + * loop by line + */ + while (linecnt < lines) { + /* + * input next line + */ + if ((cnt = inln(inf,lbuf,LBUF,&cps,0,&mor)) < 0) + break; + if (!linecnt && !nohead && + prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * start of new line. + */ + if (!lrgln) { + if (num) + addnum(nbuf, num, ++lncnt); + if (otln(obuf,cnt+off, &ips, &ops, mor)) + return(1); + } else if (otln(lbuf, cnt, &ips, &ops, mor)) + return(1); + + /* + * if line bigger than buffer, get more + */ + if (mor) { + lrgln = 1; + continue; + } + + /* + * whole line rcvd. reset tab proc. state + */ + ++linecnt; + lrgln = 0; + ops = 0; + ips = 0; + } + + /* + * fill to end of page + */ + if (linecnt && prtail(lines-linecnt-lrgln, lrgln)) + return(1); + + /* + * On EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * vertcol: print files with more than one column of output down a page + */ +int +vertcol(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register char **lstdat; + register int i; + register int j; + register int cnt = -1; + register int pln; + register int *indy; + int cvc; + int *lindy; + int lncnt; + int stp; + int pagecnt; + int col = colwd + 1; + int mxlen = pgwd + offst + 1; + int mclcnt = clcnt - 1; + struct vcol *vc; + int mvc; + int tvc; + int cw = nmwd + 1; + int fullcol; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + FILE *inf; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + /* + * allocate page buffer + */ + if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + /* + * allocate page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + if (offst) + (void)memset(hbuf, (int)' ', offst); + + /* + * col pointers when no headers + */ + mvc = lines * clcnt; + if ((vc = + (struct vcol *)malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) { + mfail(); + return(1); + } + + /* + * pointer into page where last data per line is located + */ + if ((lstdat = (char **)malloc((unsigned)lines*sizeof(char *))) == NULL){ + mfail(); + return(1); + } + + /* + * fast index lookups to locate start of lines + */ + if ((indy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) { + mfail(); + return(1); + } + if ((lindy = (int *)malloc((unsigned)lines*sizeof(int))) == NULL) { + mfail(); + return(1); + } + + if (nmwd) + fullcol = col + cw; + else + fullcol = col; + + /* + * initialize buffer lookup indexes and offset area + */ + for (j = 0; j < lines; ++j) { + lindy[j] = j * mxlen; + indy[j] = lindy[j] + offst; + if (offst) { + ptbf = buf + lindy[j]; + (void)memset(ptbf, (int)' ', offst); + ptbf += offst; + } else + ptbf = buf + indy[j]; + lstdat[j] = ptbf; + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + /* + * skip to requested page + */ + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + /* + * loop by column + */ + cvc = 0; + for (i = 0; i < clcnt; ++i) { + j = 0; + /* + * if last column, do not pad + */ + if (i == mclcnt) + stp = 1; + else + stp = 0; + /* + * loop by line + */ + for(;;) { + /* + * is this first column + */ + if (!i) { + ptbf = buf + indy[j]; + lstdat[j] = ptbf; + } else + ptbf = lstdat[j]; + vc[cvc].pt = ptbf; + + /* + * add number + */ + if (nmwd) { + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + + /* + * input next line + */ + cnt = inln(inf,ptbf,colwd,&cps,1,&mor); + vc[cvc++].cnt = cnt; + if (cnt < 0) + break; + ptbf += cnt; + + /* + * pad all but last column on page + */ + if (!stp) { + /* + * pad to end of column + */ + if (sflag) + *ptbf++ = schar; + else if ((pln = col-cnt) > 0) { + (void)memset(ptbf, + (int)' ',pln); + ptbf += pln; + } + } + /* + * remember last char in line + */ + lstdat[j] = ptbf; + if (++j >= lines) + break; + } + if (cnt < 0) + break; + } + + /* + * when -t (no header) is specified the spec requires + * the min number of lines. The last page may not have + * balanced length columns. To fix this we must reorder + * the columns. This is a very slow technique so it is + * only used under limited conditions. Without -t, the + * balancing of text columns is unspecified. To NOT + * balance the last page, add the global variable + * nohead to the if statement below e.g. + * + * if ((cnt < 0) && nohead && cvc ...... + */ + --cvc; + + /* + * check to see if last page needs to be reordered + */ + if ((cnt < 0) && cvc && ((mvc-cvc) >= clcnt)){ + pln = cvc/clcnt; + if (cvc % clcnt) + ++pln; + + /* + * print header + */ + if (!nohead && prhead(hbuf, fname, pagecnt)) + return(1); + for (i = 0; i < pln; ++i) { + ips = 0; + ops = 0; + if (offst&& otln(buf,offst,&ips,&ops,1)) + return(1); + tvc = i; + + for (j = 0; j < clcnt; ++j) { + /* + * determine column length + */ + if (j == mclcnt) { + /* + * last column + */ + cnt = vc[tvc].cnt; + if (nmwd) + cnt += cw; + } else if (sflag) { + /* + * single ch between + */ + cnt = vc[tvc].cnt + 1; + if (nmwd) + cnt += cw; + } else + cnt = fullcol; + if (otln(vc[tvc].pt, cnt, &ips, + &ops, 1)) + return(1); + tvc += pln; + if (tvc >= cvc) + break; + } + /* + * terminate line + */ + if (otln(buf, 0, &ips, &ops, 0)) + return(1); + } + /* + * pad to end of page + */ + if (prtail((lines - pln), 0)) + return(1); + /* + * done with output, go to next file + */ + break; + } + + /* + * determine how many lines to output + */ + if (i > 0) + pln = lines; + else + pln = j; + + /* + * print header + */ + if (pln && !nohead && prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * output each line + */ + for (i = 0; i < pln; ++i) { + ptbf = buf + lindy[i]; + if ((j = lstdat[i] - ptbf) <= offst) + break; + if (otln(ptbf, j, &ips, &ops, 0)) + return(1); + } + + /* + * pad to end of page + */ + if (pln && prtail((lines - pln), 0)) + return(1); + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * horzcol: print files with more than one column of output across a page + */ +int +horzcol(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register int pln; + register int cnt = -1; + register char *lstdat; + register int col = colwd + 1; + register int j; + register int i; + int lncnt; + int pagecnt; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + FILE *inf; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + if (offst) { + (void)memset(buf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + + /* + * loop by file + */ + while ((inf = nxtfile(argc, argv, &fname, ohbuf, 0)) != NULL) { + if (pgnm) { + if (inskip(inf, pgnm, lines)) + continue; + pagecnt = pgnm; + } else + pagecnt = 1; + lncnt = 0; + + /* + * loop by page + */ + for(;;) { + /* + * loop by line + */ + for (i = 0; i < lines; ++i) { + ptbf = buf + offst; + lstdat = ptbf; + j = 0; + /* + * loop by col + */ + for(;;) { + if (nmwd) { + /* + * add number to column + */ + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + /* + * input line + */ + if ((cnt = inln(inf,ptbf,colwd,&cps,1, + &mor)) < 0) + break; + ptbf += cnt; + lstdat = ptbf; + + /* + * if last line skip padding + */ + if (++j >= clcnt) + break; + + /* + * pad to end of column + */ + if (sflag) + *ptbf++ = schar; + else if ((pln = col - cnt) > 0) { + (void)memset(ptbf,(int)' ',pln); + ptbf += pln; + } + } + + /* + * determine line length + */ + if ((j = lstdat - buf) <= offst) + break; + if (!i && !nohead && + prhead(hbuf, fname, pagecnt)) + return(1); + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + return(1); + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + return(1); + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * mulfile: print files with more than one column of output and + * more than one file concurrently + */ +int +mulfile(argc, argv) + int argc; + char *argv[]; +{ + register char *ptbf; + register int j; + register int pln; + register int cnt; + register char *lstdat; + register int i; + FILE **fbuf; + int actf; + int lncnt; + int col; + int pagecnt; + int fproc; + char *buf; + char *hbuf; + char *ohbuf; + char *fname; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + + /* + * array of FILE *, one for each operand + */ + if ((fbuf = (FILE **)malloc((unsigned)clcnt*sizeof(FILE *))) == NULL) { + mfail(); + return(1); + } + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + ohbuf = hbuf + offst; + + /* + * do not know how many columns yet. The number of operands provide an + * upper bound on the number of columns. We use the number of files + * we can open successfully to set the number of columns. The operation + * of the merge operation (-m) in relation to unsuccesful file opens + * is unspecified by posix. + */ + j = 0; + while (j < clcnt) { + if ((fbuf[j] = nxtfile(argc, argv, &fname, ohbuf, 1)) == NULL) + break; + if (pgnm && (inskip(fbuf[j], pgnm, lines))) + fbuf[j] = NULL; + ++j; + } + + /* + * if no files, exit + */ + if (!j) + return(1); + + /* + * calculate page boundries based on open file count + */ + clcnt = j; + if (nmwd) { + colwd = (pgwd - clcnt - nmwd)/clcnt; + pgwd = ((colwd + 1) * clcnt) - nmwd - 2; + } else { + colwd = (pgwd + 1 - clcnt)/clcnt; + pgwd = ((colwd + 1) * clcnt) - 1; + } + if (colwd < 1) { + (void)fprintf(err, + "pr: page width too small for %d columns\n", clcnt); + return(1); + } + actf = clcnt; + col = colwd + 1; + + /* + * line buffer + */ + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) { + mfail(); + return(1); + } + if (offst) { + (void)memset(buf, (int)' ', offst); + (void)memset(hbuf, (int)' ', offst); + } + if (pgnm) + pagecnt = pgnm; + else + pagecnt = 1; + lncnt = 0; + + /* + * continue to loop while any file still has data + */ + while (actf > 0) { + /* + * loop by line + */ + for (i = 0; i < lines; ++i) { + ptbf = buf + offst; + lstdat = ptbf; + if (nmwd) { + /* + * add line number to line + */ + addnum(ptbf, nmwd, ++lncnt); + ptbf += nmwd; + *ptbf++ = nmchar; + } + j = 0; + fproc = 0; + + /* + * loop by column + */ + for (j = 0; j < clcnt; ++j) { + if (fbuf[j] == NULL) { + /* + * empty column; EOF + */ + cnt = 0; + } else if ((cnt = inln(fbuf[j], ptbf, colwd, + &cps, 1, &mor)) < 0) { + /* + * EOF hit; no data + */ + if (fbuf[j] != stdin) + (void)fclose(fbuf[j]); + fbuf[j] = NULL; + --actf; + cnt = 0; + } else { + /* + * process file data + */ + ptbf += cnt; + lstdat = ptbf; + fproc++; + } + + /* + * if last ACTIVE column, done with line + */ + if (fproc >= actf) + break; + + /* + * pad to end of column + */ + if (sflag) { + *ptbf++ = schar; + } else if ((pln = col - cnt) > 0) { + (void)memset(ptbf, (int)' ', pln); + ptbf += pln; + } + } + + /* + * calculate data in line + */ + if ((j = lstdat - buf) <= offst) + break; + + if (!i && !nohead && prhead(hbuf, fname, pagecnt)) + return(1); + + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + return(1); + + /* + * if no more active files, done + */ + if (actf <= 0) { + ++i; + break; + } + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + return(1); + ++pagecnt; + } + if (eoptind < argc) + return(1); + return(0); +} + +/* + * inln(): input a line of data (unlimited length lines supported) + * Input is optionally expanded to spaces + * + * inf: file + * buf: buffer + * lim: buffer length + * cps: column positon 1st char in buffer (large line support) + * trnc: throw away data more than lim up to \n + * mor: set if more data in line (not truncated) + */ +int +inln(inf, buf, lim, cps, trnc, mor) + FILE *inf; + char *buf; + register int lim; + int *cps; + int trnc; + int *mor; +{ + register int col; + register int gap = ingap; + register int ch = EOF; + register char *ptbuf; + register int chk = (int)inchar; + + ptbuf = buf; + + if (gap) { + /* + * expanding input option + */ + while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { + /* + * is this the input "tab" char + */ + if (ch == chk) { + /* + * expand to number of spaces + */ + col = (ptbuf - buf) + *cps; + col = gap - (col % gap); + + /* + * if more than this line, push back + */ + if ((col > lim) && (ungetc(ch, inf) == EOF)) + return(1); + + /* + * expand to spaces + */ + while ((--col >= 0) && (--lim >= 0)) + *ptbuf++ = ' '; + continue; + } + if (ch == '\n') + break; + *ptbuf++ = ch; + } + } else { + /* + * no expansion + */ + while ((--lim >= 0) && ((ch = getc(inf)) != EOF)) { + if (ch == '\n') + break; + *ptbuf++ = ch; + } + } + col = ptbuf - buf; + if (ch == EOF) { + *mor = 0; + *cps = 0; + if (!col) + return(-1); + return(col); + } + if (ch == '\n') { + /* + * entire line processed + */ + *mor = 0; + *cps = 0; + return(col); + } + + /* + * line was larger than limit + */ + if (trnc) { + /* + * throw away rest of line + */ + while ((ch = getc(inf)) != EOF) { + if (ch == '\n') + break; + } + *cps = 0; + *mor = 0; + } else { + /* + * save column offset if not truncated + */ + *cps += col; + *mor = 1; + } + + return(col); +} + +/* + * otln(): output a line of data. (Supports unlimited length lines) + * output is optionally contracted to tabs + * + * buf: output buffer with data + * cnt: number of chars of valid data in buf + * svips: buffer input column position (for large lines) + * svops: buffer output column position (for large lines) + * mor: output line not complete in this buf; more data to come. + * 1 is more, 0 is complete, -1 is no \n's + */ +int +otln(buf, cnt, svips, svops, mor) + register char *buf; + int cnt; + int *svops; + int *svips; + int mor; +{ + register int ops; /* last col output */ + register int ips; /* last col in buf examined */ + register int gap = ogap; + register int tbps; + register char *endbuf; + + if (ogap) { + /* + * contracting on output + */ + endbuf = buf + cnt; + ops = *svops; + ips = *svips; + while (buf < endbuf) { + /* + * count number of spaces and ochar in buffer + */ + if (*buf == ' ') { + ++ips; + ++buf; + continue; + } + + /* + * simulate ochar processing + */ + if (*buf == ochar) { + ips += gap - (ips % gap); + ++buf; + continue; + } + + /* + * got a non space char; contract out spaces + */ + while (ops < ips) { + /* + * use as many ochar as will fit + */ + if ((tbps = ops + gap - (ops % gap)) > ips) + break; + if (putchar(ochar) == EOF) { + pfail(); + return(1); + } + ops = tbps; + } + + while (ops < ips) { + /* + * finish off with spaces + */ + if (putchar(' ') == EOF) { + pfail(); + return(1); + } + ++ops; + } + + /* + * output non space char + */ + if (putchar(*buf++) == EOF) { + pfail(); + return(1); + } + ++ips; + ++ops; + } + + if (mor > 0) { + /* + * if incomplete line, save position counts + */ + *svops = ops; + *svips = ips; + return(0); + } + + if (mor < 0) { + while (ops < ips) { + /* + * use as many ochar as will fit + */ + if ((tbps = ops + gap - (ops % gap)) > ips) + break; + if (putchar(ochar) == EOF) { + pfail(); + return(1); + } + ops = tbps; + } + while (ops < ips) { + /* + * finish off with spaces + */ + if (putchar(' ') == EOF) { + pfail(); + return(1); + } + ++ops; + } + return(0); + } + } else { + /* + * output is not contracted + */ + if (cnt && (fwrite(buf, sizeof(char), cnt, stdout) <= 0)) { + pfail(); + return(1); + } + if (mor != 0) + return(0); + } + + /* + * process line end and double space as required + */ + if ((putchar('\n') == EOF) || (dspace && (putchar('\n') == EOF))) { + pfail(); + return(1); + } + return(0); +} + +/* + * inskip(): skip over pgcnt pages with lncnt lines per page + * file is closed at EOF (if not stdin). + * + * inf FILE * to read from + * pgcnt number of pages to skip + * lncnt number of lines per page + */ +int +inskip(inf, pgcnt, lncnt) + FILE *inf; + register int pgcnt; + register int lncnt; +{ + register int c; + register int cnt; + + while(--pgcnt > 0) { + cnt = lncnt; + while ((c = getc(inf)) != EOF) { + if ((c == '\n') && (--cnt == 0)) + break; + } + if (c == EOF) { + if (inf != stdin) + (void)fclose(inf); + return(1); + } + } + return(0); +} + +/* + * nxtfile: returns a FILE * to next file in arg list and sets the + * time field for this file (or current date). + * + * buf array to store proper date for the header. + * dt if set skips the date processing (used with -m) + */ +FILE * +nxtfile(argc, argv, fname, buf, dt) + int argc; + char **argv; + char **fname; + char *buf; + int dt; +{ + FILE *inf = NULL; + struct timeval tv; + struct timezone tz; + struct tm *timeptr = NULL; + struct stat statbuf; + static int twice = -1; + + ++twice; + if (eoptind >= argc) { + /* + * no file listed; default, use standard input + */ + if (twice) + return(NULL); + clearerr(stdin); + inf = stdin; + if (header != NULL) + *fname = header; + else + *fname = FNAME; + if (nohead) + return(inf); + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(err, "pr: cannot get time of day, %s\n", + strerror(errno)); + eoptind = argc - 1; + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } + for (; eoptind < argc; ++eoptind) { + if (strcmp(argv[eoptind], "-") == 0) { + /* + * process a "-" for filename + */ + clearerr(stdin); + inf = stdin; + if (header != NULL) + *fname = header; + else + *fname = FNAME; + ++eoptind; + if (nohead || (dt && twice)) + return(inf); + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(err, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } else { + /* + * normal file processing + */ + if ((inf = fopen(argv[eoptind], "r")) == NULL) { + ++errcnt; + if (nodiag) + continue; + (void)fprintf(err, "pr: Cannot open %s, %s\n", + argv[eoptind], strerror(errno)); + continue; + } + if (header != NULL) + *fname = header; + else if (dt) + *fname = FNAME; + else + *fname = argv[eoptind]; + ++eoptind; + if (nohead || (dt && twice)) + return(inf); + + if (dt) { + if (gettimeofday(&tv, &tz) < 0) { + ++errcnt; + (void)fprintf(err, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + timeptr = localtime(&(tv.tv_sec)); + } else { + if (fstat(fileno(inf), &statbuf) < 0) { + ++errcnt; + (void)fclose(inf); + (void)fprintf(err, + "pr: Cannot stat %s, %s\n", + argv[eoptind], strerror(errno)); + return(NULL); + } + timeptr = localtime(&(statbuf.st_mtime)); + } + } + break; + } + if (inf == NULL) + return(NULL); + + /* + * set up time field used in header + */ + if (strftime(buf, HDBUF, timefrmt, timeptr) <= 0) { + ++errcnt; + if (inf != stdin) + (void)fclose(inf); + (void)fputs("pr: time conversion failed\n", err); + return(NULL); + } + return(inf); +} + +/* + * addnum(): adds the line number to the column + * Truncates from the front or pads with spaces as required. + * Numbers are right justified. + * + * buf buffer to store the number + * wdth width of buffer to fill + * line line number + * + * NOTE: numbers occupy part of the column. The posix + * spec does not specify if -i processing should or should not + * occur on number padding. The spec does say it occupies + * part of the column. The usage of addnum currently treats + * numbers as part of the column so spaces may be replaced. + */ +void +addnum(buf, wdth, line) + register char *buf; + register int wdth; + register int line; +{ + register char *pt = buf + wdth; + + do { + *--pt = digs[line % 10]; + line /= 10; + } while (line && (pt > buf)); + + /* + * pad with space as required + */ + while (pt > buf) + *--pt = ' '; +} + +/* + * prhead(): prints the top of page header + * + * buf buffer with time field (and offset) + * cnt number of chars in buf + * fname fname field for header + * pagcnt page number + */ +int +prhead(buf, fname, pagcnt) + char *buf; + char *fname; + int pagcnt; +{ + int ips = 0; + int ops = 0; + + if ((putchar('\n') == EOF) || (putchar('\n') == EOF)) { + pfail(); + return(1); + } + /* + * posix is not clear if the header is subject to line length + * restrictions. The specification for header line format + * in the spec clearly does not limit length. No pr currently + * restricts header length. However if we need to truncate in + * an reasonable way, adjust the length of the printf by + * changing HDFMT to allow a length max as an arguement printf. + * buf (which contains the offset spaces and time field could + * also be trimmed + * + * note only the offset (if any) is processed for tab expansion + */ + if (offst && otln(buf, offst, &ips, &ops, -1)) + return(1); + (void)printf(HDFMT,buf+offst, fname, pagcnt); + return(0); +} + +/* + * prtail(): pad page with empty lines (if required) and print page trailer + * if requested + * + * cnt number of lines of padding needed + * incomp was a '\n' missing from last line output + */ +int +prtail(cnt, incomp) + register int cnt; + int incomp; +{ + if (nohead) { + /* + * only pad with no headers when incomplete last line + */ + if (!incomp) + return(0); + if ((dspace && (putchar('\n') == EOF)) || + (putchar('\n') == EOF)) { + pfail(); + return(1); + } + return(0); + } + + /* + * if double space output two \n + */ + if (dspace) + cnt *= 2; + + /* + * if an odd number of lines per page, add an extra \n + */ + if (addone) + ++cnt; + + /* + * pad page + */ + if (formfeed) { + if ((incomp && (putchar('\n') == EOF)) || + (putchar('\f') == EOF)) { + pfail(); + return(1); + } + return(0); + } + cnt += TAILLEN; + while (--cnt >= 0) { + if (putchar('\n') == EOF) { + pfail(); + return(1); + } + } + return(0); +} + +/* + * terminate(): when a SIGINT is recvd + */ +void +terminate(which_sig) + int which_sig; +{ + flsh_errs(); + exit(1); +} + + +/* + * flsh_errs(): output saved up diagnostic messages after all normal + * processing has completed + */ +void +flsh_errs() +{ + char buf[BUFSIZ]; + + (void)fflush(stdout); + (void)fflush(err); + if (err == stderr) + return; + rewind(err); + while (fgets(buf, BUFSIZ, err) != NULL) + (void)fputs(buf, stderr); +} + +void +mfail() +{ + (void)fputs("pr: memory allocation failed\n", err); +} + +void +pfail() +{ + (void)fprintf(err, "pr: write failure, %s\n", strerror(errno)); +} + +void +usage() +{ + (void)fputs( + "usage: pr [+page] [-col] [-adFmrt] [-e[ch][gap]] [-h header]\n",err); + (void)fputs( + " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n",err); + (void)fputs( + " [-s[ch]] [-w width] [-] [file ...]\n", err); +} + +/* + * setup: Validate command args, initialize and perform sanity + * checks on options + */ +int +setup(argc, argv) + register int argc; + register char **argv; +{ + register int c; + int eflag = 0; + int iflag = 0; + int wflag = 0; + int cflag = 0; + + if (isatty(fileno(stdout))) { + /* + * defer diagnostics until processing is done + */ + if ((err = tmpfile()) == NULL) { + (void)fputs("Cannot defer diagnostic messages\n",stderr); + return(1); + } + } else + err = stderr; + while ((c = egetopt(argc, argv, "#adFmrte?h:i?l:n?o:s?w:")) != EOF) { + switch (c) { + case '+': + if ((pgnm = atoi(eoptarg)) < 1) { + (void)fputs("pr: +page number must be 1 or more\n", + err); + return(1); + } + break; + case '-': + if ((clcnt = atoi(eoptarg)) < 1) { + (void)fputs("pr: -columns must be 1 or more\n",err); + return(1); + } + if (clcnt > 1) + ++cflag; + break; + case 'a': + ++across; + break; + case 'd': + ++dspace; + break; + case 'e': + ++eflag; + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + inchar = *eoptarg++; + else + inchar = INCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((ingap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -e gap must be 0 or more\n", err); + return(1); + } + if (ingap == 0) + ingap = INGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(err, + "pr: invalid value for -e %s\n", eoptarg); + return(1); + } else + ingap = INGAP; + break; + case 'F': + ++formfeed; + break; + case 'h': + header = eoptarg; + break; + case 'i': + ++iflag; + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + ochar = *eoptarg++; + else + ochar = OCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((ogap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -i gap must be 0 or more\n", err); + return(1); + } + if (ogap == 0) + ogap = OGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(err, + "pr: invalid value for -i %s\n", eoptarg); + return(1); + } else + ogap = OGAP; + break; + case 'l': + if (!isdigit(*eoptarg) || ((lines=atoi(eoptarg)) < 1)) { + (void)fputs( + "pr: Number of lines must be 1 or more\n",err); + return(1); + } + break; + case 'm': + ++merge; + break; + case 'n': + if ((eoptarg != NULL) && !isdigit(*eoptarg)) + nmchar = *eoptarg++; + else + nmchar = NMCHAR; + if ((eoptarg != NULL) && isdigit(*eoptarg)) { + if ((nmwd = atoi(eoptarg)) < 1) { + (void)fputs( + "pr: -n width must be 1 or more\n",err); + return(1); + } + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(err, + "pr: invalid value for -n %s\n", eoptarg); + return(1); + } else + nmwd = NMWD; + break; + case 'o': + if (!isdigit(*eoptarg) || ((offst = atoi(eoptarg))< 1)){ + (void)fputs("pr: -o offset must be 1 or more\n", + err); + return(1); + } + break; + case 'r': + ++nodiag; + break; + case 's': + ++sflag; + if (eoptarg == NULL) + schar = SCHAR; + else + schar = *eoptarg++; + if (*eoptarg != '\0') { + (void)fprintf(err, + "pr: invalid value for -s %s\n", eoptarg); + return(1); + } + break; + case 't': + ++nohead; + break; + case 'w': + ++wflag; + if (!isdigit(*eoptarg) || ((pgwd = atoi(eoptarg)) < 1)){ + (void)fputs( + "pr: -w width must be 1 or more \n",err); + return(1); + } + break; + case '?': + default: + return(1); + } + } + + /* + * default and sanity checks + */ + if (!clcnt) { + if (merge) { + if ((clcnt = argc - eoptind) <= 1) { + clcnt = CLCNT; + merge = 0; + } + } else + clcnt = CLCNT; + } + if (across) { + if (clcnt == 1) { + (void)fputs("pr: -a flag requires multiple columns\n", + err); + return(1); + } + if (merge) { + (void)fputs("pr: -m cannot be used with -a\n", err); + return(1); + } + } + if (!wflag) { + if (sflag) + pgwd = SPGWD; + else + pgwd = PGWD; + } + if (cflag || merge) { + if (!eflag) { + inchar = INCHAR; + ingap = INGAP; + } + if (!iflag) { + ochar = OCHAR; + ogap = OGAP; + } + } + if (cflag) { + if (merge) { + (void)fputs( + "pr: -m cannot be used with multiple columns\n", err); + return(1); + } + if (nmwd) { + colwd = (pgwd + 1 - (clcnt * (nmwd + 2)))/clcnt; + pgwd = ((colwd + nmwd + 2) * clcnt) - 1; + } else { + colwd = (pgwd + 1 - clcnt)/clcnt; + pgwd = ((colwd + 1) * clcnt) - 1; + } + if (colwd < 1) { + (void)fprintf(err, + "pr: page width is too small for %d columns\n",clcnt); + return(1); + } + } + if (!lines) + lines = LINES; + + /* + * make sure long enough for headers. if not disable + */ + if (lines <= HEADLEN + TAILLEN) + ++nohead; + else if (!nohead) + lines -= HEADLEN + TAILLEN; + + /* + * adjust for double space on odd length pages + */ + if (dspace) { + if (lines == 1) + dspace = 0; + else { + if (lines & 1) + ++addone; + lines /= 2; + } + } + + if ((timefrmt = getenv("LC_TIME")) == NULL) + timefrmt = TIMEFMT; + return(0); +} diff --git a/usr.bin/pr/pr.h b/usr.bin/pr/pr.h new file mode 100644 index 0000000..d020e9f --- /dev/null +++ b/usr.bin/pr/pr.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pr.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * parameter defaults + */ +#define CLCNT 1 +#define INCHAR '\t' +#define INGAP 8 +#define OCHAR '\t' +#define OGAP 8 +#define LINES 66 +#define NMWD 5 +#define NMCHAR '\t' +#define SCHAR '\t' +#define PGWD 72 +#define SPGWD 512 + +/* + * misc default values + */ +#define HDFMT "%s %s Page %d\n\n\n" +#define HEADLEN 5 +#define TAILLEN 5 +#define TIMEFMT "%b %e %H:%M %Y" +#define FNAME "" +#define LBUF 8192 +#define HDBUF 512 + +/* + * structure for vertical columns. Used to balance cols on last page + */ +struct vcol { + char *pt; /* ptr to col */ + int cnt; /* char count */ +}; |