From 6ea7f916dbb2c8c6d3efdfc36049c8dccbbb70f0 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Thu, 24 Oct 2013 12:39:25 -0400 Subject: [PATCH] Importing usr.bin/pr Replaces commands/pr. No Minix-specific changes needed. Change-Id: I3c4c03b38fc8c654e7c54c3a8af0e07a0dfc9f67 --- commands/Makefile | 2 +- commands/pr/Makefile | 4 - commands/pr/pr.c | 504 ----------- man/man1/Makefile | 2 +- man/man1/pr.1 | 57 -- releasetools/nbsd_ports | 1 + usr.bin/Makefile | 2 +- usr.bin/pr/Makefile | 12 + usr.bin/pr/egetopt.c | 215 +++++ usr.bin/pr/extern.h | 42 + usr.bin/pr/pr.1 | 361 ++++++++ usr.bin/pr/pr.c | 1901 +++++++++++++++++++++++++++++++++++++++ usr.bin/pr/pr.h | 79 ++ 13 files changed, 2614 insertions(+), 568 deletions(-) delete mode 100644 commands/pr/Makefile delete mode 100644 commands/pr/pr.c delete mode 100644 man/man1/pr.1 create mode 100644 usr.bin/pr/Makefile create mode 100644 usr.bin/pr/egetopt.c create mode 100644 usr.bin/pr/extern.h create mode 100644 usr.bin/pr/pr.1 create mode 100644 usr.bin/pr/pr.c create mode 100644 usr.bin/pr/pr.h diff --git a/commands/Makefile b/commands/Makefile index 062cdda9e..7fd1e3c14 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -18,7 +18,7 @@ SUBDIR= add_route arp ash at backup btrace \ mesg mined mkfifo \ mount mt netconf \ nonamed od paste patch \ - ping postinstall poweroff pr prep printroot \ + ping postinstall poweroff prep printroot \ profile progressbar pr_routes ps pwdauth \ ramdisk rarpd rawspeed rcp readclock \ reboot remsync rget rlogin \ diff --git a/commands/pr/Makefile b/commands/pr/Makefile deleted file mode 100644 index d362de261..000000000 --- a/commands/pr/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -PROG= pr -MAN= - -.include diff --git a/commands/pr/pr.c b/commands/pr/pr.c deleted file mode 100644 index 2ad7c0574..000000000 --- a/commands/pr/pr.c +++ /dev/null @@ -1,504 +0,0 @@ -/* pr - print files Author: Michiel Huisjes */ - -/* Pr - print files - * - * Author: Michiel Huisjes. - * Modified: Jacob P. Bunschoten. (30 nov 87) - * When "columns" is not given and numbering is on: - * line numbers are correlated with input lines. - * (try pr [-1] -n file ) - * tabs are accounted for. - * When numbering is turned on, width know this. - * automatic line-folding. -f to get the original program. - * backspaces are accounted for. -b to disable this. - * multi-column mode changed. - * header can be given and used. - * format changed may occur between printing of several files: - * pr -l30 file1 -w75 file2 - * - * Modified: Rick Thomas. (Sept 12, 1988) - * added "-M" option to cover functionality of old "-n" option, - * and made "-n" option behavior compatible with system V. - * - * Usage: pr [+page] [-columns] [-h header] [-wwidth] [-llength] [-ntm] [files] - * -t : Do not print the 5 line header and trailer at the page. - * -n : Turn on line numbering. - * -M : Use "Minix" style line numbering -- Each page begins at - * a line number that is an even multiple of the page length. - * Like the listings in Appendix E of the book. - * +page : Start printing at page n. - * -columns : Print files in n-columns. - * -l length: Take the length of the page to be n instead of 66 - * -h header: Take next argument as page header. - * -w width : Take the width of the page to be n instead of default 79 - * -f : do not fold lines. - * - * Modified: Lars Fredriksen (Jan 19, 1990) - * fixed the program so that - * pr -n *.c - * would work. The clobal variable 'width' was decremented - * by NUM_WIDTH, for each file, resulting in width finally - * being so small that nothing was printed. Used the local - * variable 'w' for the width adjustment (in print()) - * - * Modified: Kenneth J. Hendrickson (10 April 1991) - * date in header should be last modification date for files, - * and the current time for stdin. - * - * Modified: Kees J. Bot (5 October 1992) - * Use localtime(3) to get the date, it knows TZ. - */ - -#include -#include -#include -#include -#include -#include - - -#define DEF_LENGTH 66 -#define DEF_WIDTH 79 -#define NUM_WIDTH 8 -#define TAB_WIDTH 8 /* fixed tab_width */ - -/* Used to compute next (fixed) tabstop */ -#define TO_TAB(x) (( (x) + TAB_WIDTH ) & ~07 ) - -typedef char BOOL; - -#define FALSE 0 -#define TRUE 1 - -/* EAT: eat rest of input line */ -#define EAT(fp) while((c=getc(fp))!='\n' && c!=EOF) - -/* L_BUF: calculate address of pointer to char (string) used in format */ -#define L_BUF(i,j) * (char **) (line_buf + (i + j*length)*sizeof(char *)) - -char *header; -BOOL no_header; -BOOL number = FALSE; -BOOL minix_number = FALSE; -BOOL ext_header_set = FALSE; /* external header given */ -BOOL back_space = TRUE; /* back space correction in line width */ -BOOL dont_fold = FALSE; /* original. If the line does not fit eat it. */ -short columns; -short cwidth; -short start_page = 1; -short width = DEF_WIDTH; -short length = DEF_LENGTH; -short linenr; -char *line_buf; /* used in format for multi-column output */ - -char output[1024]; - -int main(int argc, char **argv); -static char *myalloc(size_t size); -char skip_page(int lines, int width, FILE * filep); -void format(FILE * filep); -void print_page(int pagenr, int maxcol); -void print(FILE * filep); -void out_header(int page); -void print_time(time_t t); - -int main(argc, argv) -int argc; -char *argv[]; -{ - FILE *file; - char *ptr; - int index = 1; /* index is one ahead of argc */ - int line, col; - - setbuf(stdout, output); - do { - if (argc == index) /* No arguments (left) */ - goto pr_files; - - ptr = argv[index++]; - if (*ptr == '+') { - start_page = atoi(++ptr); - continue; - } - if (*ptr != '-') { /* no flags */ - index--; - goto pr_files; - } - if (*++ptr >= '0' && *ptr <= '9') { - columns = atoi(ptr); - if (columns <= 0) columns = 1; - continue; /* Fetch next flag */ - } - while (*ptr) switch (*ptr++) { - case 't': no_header = TRUE; break; - case 'n': - number = TRUE; - minix_number = FALSE; - break; - case 'M': - number = TRUE; - minix_number = TRUE; - break; - case 'h': - header = argv[index++]; - ext_header_set = TRUE; - break; - case 'w': - if ((width = atoi(ptr)) <= 0) width = DEF_WIDTH; - *ptr = '\0'; - break; - case 'l': - if ((length = atoi(ptr)) <= 0) length = DEF_LENGTH; - *ptr = '\0'; - break; - case 'b': /* back_space correction off */ - back_space = FALSE; - break; - case 'f': /* do not fold lines */ - dont_fold = TRUE; - break; - default: - fprintf(stderr, "Usage: %s [+page] [-columns] [-h header] [-w] [-l] [-nMt] [files]\n", argv[0]); - exit(1); - } - continue; /* Scan for next flags */ - - - /* ============== flags are read. Print the file(s) ========= */ - -pr_files: - - if (!no_header) length -= 10; - - if (columns) { - cwidth = width / columns + 1; - if (columns > width) { - fprintf(stderr, "Too many columns for page width.\n"); - exit(1); - } - - /* Allocate piece of mem to hold some pointers */ - line_buf = myalloc(length * columns * sizeof(char *)); - } - for (line = 0; line < length; line++) - for (col = 0; col < columns; col++) - L_BUF(line, col) = NULL; - - if (length <= 0) { - fprintf(stderr, "Minimal length should be %d\n", no_header ? - 1 : 11); - exit(1); - } - while (index <= argc) { /* print all files, including stdin */ - if (index < argc && (*argv[index] == '-' || *argv[index] == '+')) - break; /* Format change */ - - if (argc == index) { /* no file specified, so stdin */ - if (!ext_header_set) header = ""; - file = stdin; - } else { - if ((file = fopen(argv[index], "r")) == (FILE *) 0) { - fprintf(stderr, "Cannot open %s\n", argv[index++]); - continue; - } - if (!ext_header_set) header = argv[index]; - } - if (columns) - format(file); - else - print(file); - fclose(file); - if (++index >= argc) - break; /* all files (including stdin) done */ - } - if (index >= argc) break; - /* When control comes here. format changes are to be done. - * reinitialize some variables */ - if (!no_header) length += 10; - - start_page = 1; - ext_header_set = FALSE; - if (columns) free(line_buf); - } while (index <= argc); /* "pr -l60" should work too */ - - (void) fflush(stdout); - return(0); -} - -char skip_page(lines, width, filep) -int lines, width; -FILE *filep; -{ - short c; - int char_cnt; - int w; - - do { - w = width; - if (number) /* first lines are shorter */ - if (!columns || /* called from print(file) */ - !(lines % columns)) /* called from format(file) */ - w -= NUM_WIDTH; - - char_cnt = 0; - while ((c = getc(filep)) != '\n' && c != EOF && char_cnt < w) { - /* Calculate if this line is longer than "width (w)" - * characters */ - if (c == '\b' && back_space) { - if (--char_cnt < 0) char_cnt = 0; - } else if (c == '\t') - char_cnt = TO_TAB(char_cnt); - else - char_cnt++; - } - if (dont_fold && c != '\n' && c != EOF) EAT(filep); - lines--; - if (c == '\n') linenr++; - } while (lines > 0 && c != EOF); - - return c; /* last char read */ -} - -void format(filep) -FILE *filep; -{ - char buf[512]; - short c = '\0'; - short index, lines, i; - short page_number = 0; - short maxcol = columns; - short wdth; - short line, col; - - do { - /* Check printing of page */ - page_number++; - - if (page_number < start_page && c != EOF) { - c = (char) skip_page(columns * length, cwidth, filep); - continue; - } - if (c == EOF) return; - - lines = columns * length; - for (line = 0; line < length; line++) - for (col = 0; col < columns; col++) { - if (L_BUF(line, col) != NULL) - free(L_BUF(line, col)); - L_BUF(line, col) = (char *) NULL; - } - line = 0; - col = 0; - do { - index = 0; - wdth = cwidth - 1; - if (number && !col) /* need room for numbers */ - wdth -= NUM_WIDTH; - - /* Intermidiate colums are shortened by 1 char */ - /* Last column not */ - if (col + 1 == columns) wdth++; - for (i = 0; i < wdth - 1; i++) { - c = getc(filep); - if (c == '\n' || c == EOF) break; - - if (c == '\b' && back_space) { - buf[index++] = '\b'; - if (--i < 0) { /* just in case ... */ - i = 0; - index = 0; - } - } else if (c == '\t') { - int cnt, max; - - max = TO_TAB(i); - for (cnt = i; cnt < max; cnt++) - buf[index++] = ' '; - i = max - 1; - } else - buf[index++] = (char) c; - } - buf[index++] = '\0'; - /* Collected enough chars (or eoln, or EOF) */ - - /* First char is EOF */ - if (i == 0 && lines == columns * length && c == EOF) return; - - /* Alloc mem to hold this (sub) string */ - L_BUF(line, col) = myalloc(index * sizeof(char)); - strcpy(L_BUF(line, col), buf); - - line++; - line %= length; - if (line == 0) { - col++; - col %= columns; - } - if (dont_fold && c != '\n' && c != EOF) EAT(filep); - lines--; /* line ready for output */ - if (c == EOF) { - maxcol = columns - lines / length; - } - } while (c != EOF && lines); - print_page(page_number, maxcol); - } while (c != EOF); -} - -void print_page(pagenr, maxcol) -short pagenr, maxcol; -{ - short pad, i, j; - short width; - char *p; - - if (minix_number) - linenr = (pagenr - 1) * length + 1; - else - linenr = 1; - - if (!no_header) out_header(pagenr); - - for (i = 0; i < length; i++) { - for (j = 0; j < maxcol; j++) { - width = cwidth; - if (number && j == 0) { /* first columns */ - printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */ - width -= NUM_WIDTH; - } - pad = 0; - if (p = (char *) L_BUF(i, j)) - for (; pad < width - 1 && *p; pad++) putchar(*p++); - if (j < maxcol - 1) while (pad++ < width - 1) - putchar(' '); - } - putchar('\n'); - } - if (!no_header) printf("\n\n\n\n\n"); -} - -void print(filep) -FILE *filep; -{ - short c = '\0'; - short page_number = 0; - short lines; - short cnt; - short w = width; - BOOL pr_number = TRUE; /* only real lines are numbered, not folded - * parts */ - - linenr = 1; - if (number) w -= NUM_WIDTH; - - do { - /* Check printing of page */ - page_number++; - - if (page_number < start_page && c != EOF) { - pr_number = FALSE; - c = skip_page(length, w, filep); - if (c == '\n') pr_number = TRUE; - continue; - } - if (c == EOF) return; - - if (minix_number) linenr = (page_number - 1) * length + 1; - - if (page_number == start_page) c = getc(filep); - - /* Print the page */ - lines = length; - while (lines && c != EOF) { - if (lines == length && !no_header) out_header(page_number); - if (number) - if (pr_number) - printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */ - else - printf("%7c ", ' '); /* 7 == NUM_WIDTH-1 */ - pr_number = FALSE; - cnt = 0; - while (c != '\n' && c != EOF && cnt < w) { - if (c == '\t') { - int i, max; - max = TO_TAB(cnt); - for (i = cnt; i < max; i++) putchar(' '); - cnt = max - 1; - } else if (c == '\b' && back_space) { - putchar('\b'); - cnt--; - } else - putchar(c); - c = getc(filep); - cnt++; - } - putchar('\n'); - if (dont_fold && c != '\n' && c != EOF) EAT(filep); - lines--; - if (c == '\n') { - c = getc(filep); - pr_number = TRUE; - } - } - if (lines == length) /* We never printed anything on this - * page -- */ - return; /* even the header, so dont try to fill it up */ - if (!no_header) /* print the trailer -- 5 blank lines */ - printf("\n\n\n\n\n"); - } while (c != EOF); - - /* Fill last page */ - if (page_number >= start_page) { - while (lines--) putchar('\n'); - } -} - -static char *myalloc(size) -size_t size; /* How many bytes */ -{ - void *ptr; - - ptr = malloc(size); - if (ptr == NULL) { - fprintf(stderr, "malloc returned NULL\n"); - exit(1); - } - return(char *) ptr; -} - -void out_header(page) -short page; -{ - time_t t; - struct stat buf; - - if (strlen(header)) { - stat(header, &buf); /* use last modify time for file */ - t = buf.st_mtime; - } else - (void) time(&t); /* use current time for stdin */ - print_time(t); - printf(" %s Page %d\n\n\n", header, page); -} - -char *moname[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -/* Print the date. */ -void print_time(t) -time_t t; -{ - struct tm *tm; - - tm = localtime(&t); - - printf("\n\n%s %2d %2d:%02d %d", - moname[tm->tm_mon], - tm->tm_mday, - tm->tm_hour, - tm->tm_min, - 1900 + tm->tm_year - ); -} diff --git a/man/man1/Makefile b/man/man1/Makefile index 94222a905..bd7a170f2 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -12,7 +12,7 @@ MAN= ash.1 at.1 \ look.1 lp.1 lspci.1 mail.1 \ mesg.1 mixer.1 \ mkproto.1 mount.1 mt.1 od.1 \ - paste.1 ping.1 playwave.1 pr.1 prep.1 \ + paste.1 ping.1 playwave.1 prep.1 \ profile.1 ps.1 rcp.1 recwave.1 \ remsync.1 rget.1 rlogin.1 rsh.1 rz.1 \ shar.1 sleep.1 spell.1 \ diff --git a/man/man1/pr.1 b/man/man1/pr.1 deleted file mode 100644 index 6993d4ab6..000000000 --- a/man/man1/pr.1 +++ /dev/null @@ -1,57 +0,0 @@ -.TH PR 1 -.SH NAME -pr \- print a file -.SH SYNOPSIS -\fBpr\fR [\fB\-Mfnt\fR]\fR [\fB\-h \fIn\fR] [\fB\-l \fIn\fR] [\fB\-w \fIn\fR] [\fB\-\fRcolumns\fR] [\fB+\fIpage\fR] [\fIfile\fR] ...\fR -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH OPTIONS -.TP 5 -.B \-M -# Use MINIX style line number -.TP 5 -.B \-f -# Do not fold long lines -.TP 5 -.B \-h -# Take next argument as page header -.TP 5 -.B \-l -# Sets page length in lines -.TP 5 -.B \-n -# Number the output lines -.TP 5 -.B \-t -# Do not print page header or trailer -.TP 5 -.B \-w -# Sets line length in characters -.SH EXAMPLES -.TP 20 -.B pr \-w85 \-l60 file -# Use 85 character line, 60 line page -.TP 20 -.B pr \-3 file -# List \fIfile\fP three columns to a page -.TP 20 -.B pr +4 file -# Start printing with page 4 -.SH DESCRIPTION -.PP -.I Pr -formats one or more files for printing. -If no files are specified, \fIstdin\fR is printed. -Options are provided for setting the width and height of the page, the -number of columns to use (default 1), and the page to start with, among others. -.SH "SEE ALSO" -.BR lp (1). diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index c10508b08..c37ac0b1b 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -189,6 +189,7 @@ 2012/10/17 12:00:00,usr.bin/nvi 2010/05/14 17:28:23,usr.bin/newgrp 2012/10/17 12:00:00,usr.bin/passwd +2013/10/24 12:00:00,usr.bin/pr 2013/10/17 12:00:00,usr.bin/printenv 2012/10/17 12:00:00,usr.bin/printf 2013/09/28 12:00:00,usr.bin/rev diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 87309c805..75ca3a1ee 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -19,7 +19,7 @@ SUBDIR= \ mkdep mktemp \ \ nbperf newgrp nice nl nohup nvi \ - passwd \ + passwd pr \ printenv printf \ rev \ \ diff --git a/usr.bin/pr/Makefile b/usr.bin/pr/Makefile new file mode 100644 index 000000000..e36918750 --- /dev/null +++ b/usr.bin/pr/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.4 2011/08/16 12:05:52 christos Exp $ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= pr +SRCS= pr.c egetopt.c + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +COPTS.pr.c += -Wno-format-nonliteral + +.include diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c new file mode 100644 index 000000000..5c43476da --- /dev/null +++ b/usr.bin/pr/egetopt.c @@ -0,0 +1,215 @@ +/* $NetBSD: egetopt.c,v 1.9 2011/09/06 18:26:06 joerg Exp $ */ + +/*- + * 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. 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. + */ + +#include +#ifndef lint +#if 0 +from: static char sccsid[] = "@(#)egetopt.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: egetopt.c,v 1.9 2011/09/06 18:26:06 joerg Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#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)'?' +static char EMSG[1] = { '\0' }; + +int +egetopt(int nargc, char * const *nargv, const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + static int delim; /* which option delimiter */ + 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 (-1); + } + + delim = (int)*place; + if (place[1] && *++place == '-' && !place[1]) { + /* + * found "--" + */ + ++eoptind; + place = EMSG; + return (-1); + } + } + + /* + * 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 -1 when by itself. + */ + if ((eoptopt == (int)'-') && !*place) + return (-1); + if (strchr(ostr, '#') && (isdigit(eoptopt) || + (((eoptopt == (int)'-') || (eoptopt == (int)'+')) && + isdigit((unsigned char)*place)))) { + /* + * # option: +/- with a number is ok + */ + for (p = place; *p != '\0'; ++p) { + if (!isdigit((unsigned char)*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 000000000..6aeb61c38 --- /dev/null +++ b/usr.bin/pr/extern.h @@ -0,0 +1,42 @@ +/* $NetBSD: extern.h,v 1.6 2011/09/06 18:26:06 joerg Exp $ */ + +/*- + * 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. 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. + * + * from: @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: extern.h,v 1.6 2011/09/06 18:26:06 joerg Exp $ + */ + +extern int eoptind; +extern char *eoptarg; + +int egetopt(int, char * const *, const char *); diff --git a/usr.bin/pr/pr.1 b/usr.bin/pr/pr.1 new file mode 100644 index 000000000..c0997f906 --- /dev/null +++ b/usr.bin/pr/pr.1 @@ -0,0 +1,361 @@ +.\" $NetBSD: pr.1,v 1.22 2012/08/01 02:36:17 ginsbach Exp $ +.\" +.\" Copyright (c) 1991 Keith Muller. +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" Copyright (c) 1994-1995, 1997, 1999-2003, 2009, 2012 +.\" The NetBSD Foundation, Inc. +.\" +.\" 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. 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. +.\" +.\" from: @(#)pr.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd July 31, 2012 +.Dt PR 1 +.Os +.Sh NAME +.Nm pr +.Nd print files +.Sh SYNOPSIS +.Nm +.Op Ar \&+page +.Op Fl Ar column +.Op Fl adFfmprt +.Oo +.Fl e Ns Oo Ar char Oc Ns Op Ar gap +.Oc +.Op Fl h Ar header +.Oo +.Fl i Ns Oo Ar char Oc Ns Op Ar gap +.Oc +.Op Fl l Ar lines +.Oo +.Fl n Ns Oo Ar char Oc Ns Op Ar width +.Oc +.Op Fl o Ar offset +.Oo +.Fl s Ns Op Ar char +.Oc +.Op Fl T Ar timefmt +.Op Fl w Ar width +.Op - +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm +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 +.Bl -bullet +.It +A 5-line header with the page number, date, time, and +the pathname of the file. +.It +A 5-line trailer consisting of blank lines. +.El +.Pp +If standard output is associated with a terminal, +diagnostic messages are suppressed until the +.Nm +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 +.Aq Em blank . +Input lines that do not fit into a text column are truncated. +Lines are not truncated under single column output. +.Sh OPTIONS +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 +.Aq Em newline +character is output following every +.Aq newline +found in the input. +.It Fl e Ns Oo Ar char Oc Ns Op Ar gap +Expand each input +.Aq tab +to the next greater column +position specified by the formula +.Ar n*gap+1 , +where +.Em n +is an integer \*[Gt] 0. +If +.Ar gap +is zero or is omitted the default is 8. +All +.Aq Em tab +characters in the input are expanded into the appropriate +number of +.Ao Em space Ac Ns s . +If any nondigit character, +.Ar char , +is specified, it is used as the input tab character. +If the first character of +.Ar char +is a digit then +.Ar char +is treated as +.Ar gap . +.It Fl F +Use a +.Aq Em form-feed +character for new pages, +instead of the default behavior that uses a +sequence of +.Aq Em newline +characters. +.It Fl f +Same as +.Fl F . +Additionally pause before beginning the first page +if the standard output is associated with a terminal. +.It Fl h Ar header +Use the string +.Ar header +to replace the +.Ar file name +in the header line. +.It Fl i Ns Oo Ar char Oc Ns Op Ar gap +In output, replace multiple +.Ao space Ac Ns s +with +.Ao tab Ac Ns s +whenever two or more +adjacent +.Ao space Ac Ns s +reach column positions +.Ar gap+1 , +.Ar 2*gap+1 , +etc. +If +.Ar gap +is zero or omitted, default +.Aq Em tab +settings at every eighth column position +is used. +If any nondigit character, +.Ar char , +is specified, it is used as the output +.Aq Em tab +character. +If the first character of +.Ar char +is a digit then +.Ar char +is treated as +.Ar gap . +.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 +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 Ns Oo Ar char Oc Ns Op Ar 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 +.Aq 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 +.Ao Em space Ac Ns 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 p +Pause before beginning each page if the +standard output is associated with a terminal. +.Nm +will write an +.Aq Em alert +to standard error and wait for a +.Aq Em carriage-return +to be read on +.Pa /dev/tty . +.It Fl r +Write no diagnostic reports on failure to open a file. +.It Fl s Ns Op Ar char +Separate text columns by the single character +.Ar char +instead of by the appropriate number of +.Ao Em space Ac Ns s +(default for +.Ar char +is the +.Aq Em tab +character). +.It Fl T +Specify an +.Xr strftime 3 +format string to be used to format the date and time information in the page +header. +.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 +If +.Nm +receives an interrupt while printing to a terminal, it +flushes all accumulated error messages to the screen before +terminating. +.Pp +The +.Nm +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 , +.Xr strftime 3 +.Sh STANDARDS +The +.Nm +utility is +.St -p1003.1-2008 +compatible. diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c new file mode 100644 index 000000000..91e807527 --- /dev/null +++ b/usr.bin/pr/pr.c @@ -0,0 +1,1901 @@ +/* $NetBSD: pr.c,v 1.24 2012/08/01 02:27:48 ginsbach Exp $ */ + +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2012 + * The NetBSD Foundation, Inc. + * + * 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. 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. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +from: static char sccsid[] = "@(#)pr.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: pr.c,v 1.24 2012/08/01 02:27:48 ginsbach Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 separated 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 + */ +static int pgnm; /* starting page number */ +static int clcnt; /* number of columns */ +static int colwd; /* column data width - multiple columns */ +static int across; /* mult col flag; write across page */ +static int dspace; /* double space flag */ +static char inchar; /* expand input char */ +static int ingap; /* expand input gap */ +static int formfeed; /* use formfeed as trailer */ +static char *header; /* header name instead of file name */ +static char ochar; /* contract output char */ +static int ogap; /* contract output gap */ +static int lines; /* number of lines per page */ +static int merge; /* merge multiple files in output */ +static char nmchar; /* line numbering append char */ +static int nmwd; /* width of line number field */ +static int offst; /* number of page offset spaces */ +static int nodiag; /* do not report file open errors */ +static char schar; /* text column separation character */ +static int sflag; /* -s option for multiple columns */ +static int ttyout; /* output is a tty */ +static int nohead; /* do not write head and trailer */ +static int pgpause; /* pause before each page */ +static int pgwd; /* page width with multiple col output */ +static const char *timefrmt = TIMEFMT; /* time conversion string */ +static FILE *ttyinf; /* input terminal for page pauses */ + +/* + * misc globals + */ +static FILE *errf; /* error message file pointer */ +static int addone; /* page length is odd with double space */ +static int errcnt; /* error count on file processing */ +static const char digs[] = "0123456789"; /* page number translation map */ + +static void addnum(char *, int, int); +static void flsh_errs(void); +static int horzcol(int, char **); +static int inln(FILE *, char *, int, int *, int, int *); +static int inskip(FILE *, int, int); +static void mfail(void); +static int mulfile(int, char **); +static FILE *nxtfile(int, char **, const char **, char *, int); +static int onecol(int, char **); +static int otln(char *, int, int *, int *, int); +static void pfail(void); +static int prhead(char *, const char *, int); +static void prpause(int); +static int prtail(int, int); +static int setup(int, char **); +__dead static void terminate(int); +static void usage(void); +static int vertcol(int, char **); + +int +main(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. + */ +static int +onecol(int argc, char *argv[]) +{ + int cnt = -1; + int off; + int lrgln; + int linecnt; + int num; + int lncnt; + int pagecnt; + int ips; + int ops; + int cps; + char *obuf = NULL; + char *lbuf; + char *nbuf; + char *hbuf = NULL; + char *ohbuf; + FILE *inf = NULL; + const char *fname; + int mor; + int error = 1; + + if (nmwd) + num = nmwd + 1; + else + num = 0; + off = num + offst; + + /* + * allocate line buffer + */ + if ((obuf = malloc((unsigned)(LBUF + off)*sizeof(char))) == NULL) + goto oomem; + /* + * allocate header buffer + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) + goto oomem; + + 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) { + if (pgpause) + prpause(pagecnt); + + if (!nohead && + prhead(hbuf, fname, pagecnt)) + goto out; + } + + /* + * start of new line. + */ + if (!lrgln) { + if (num) + addnum(nbuf, num, ++lncnt); + if (otln(obuf,cnt+off, &ips, &ops, mor)) + goto out; + } else if (otln(lbuf, cnt, &ips, &ops, mor)) + goto out; + + /* + * 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)) + goto out; + + /* + * On EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + goto out; + error = 0; + goto out; +oomem: + mfail(); +out: + free(obuf); + free(hbuf); + if (inf != NULL && inf != stdin) + (void)fclose(inf); + return error; +} + +/* + * vertcol: print files with more than one column of output down a page + */ +static int +vertcol(int argc, char *argv[]) +{ + char *ptbf; + char **lstdat = NULL; + int i; + int j; + int cnt = -1; + int pln; + int *indy = NULL; + int cvc; + int *lindy = NULL; + int lncnt; + int stp; + int pagecnt; + int col = colwd + 1; + int mxlen = pgwd + offst + 1; + int mclcnt = clcnt - 1; + struct vcol *vc = NULL; + int mvc; + int tvc; + int cw = nmwd + 1; + int fullcol; + char *buf = NULL; + char *hbuf = NULL; + char *ohbuf; + const char *fname; + FILE *inf = NULL; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + int error = 1; + + /* + * allocate page buffer + */ + if ((buf = malloc((unsigned)lines*mxlen*sizeof(char))) == NULL) + goto oomem; + + /* + * allocate page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) + goto oomem; + ohbuf = hbuf + offst; + if (offst) + (void)memset(hbuf, (int)' ', offst); + + /* + * col pointers when no headers + */ + mvc = lines * clcnt; + if ((vc = malloc((unsigned)mvc*sizeof(struct vcol))) == NULL) + goto oomem; + + /* + * pointer into page where last data per line is located + */ + if ((lstdat = malloc((unsigned)lines*sizeof(char *))) == NULL) + goto oomem; + + /* + * fast index lookups to locate start of lines + */ + if ((indy = malloc((unsigned)lines*sizeof(int))) == NULL) + goto oomem; + if ((lindy = malloc((unsigned)lines*sizeof(int))) == NULL) + goto oomem; + + 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; + + if (pgpause) + prpause(pagecnt); + + /* + * print header + */ + if (!nohead && prhead(hbuf, fname, pagecnt)) + goto out; + for (i = 0; i < pln; ++i) { + ips = 0; + ops = 0; + if (offst&& otln(buf,offst,&ips,&ops,1)) { + error = 1; + goto out; + } + 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)) + goto out; + tvc += pln; + if (tvc >= cvc) + break; + } + /* + * terminate line + */ + if (otln(buf, 0, &ips, &ops, 0)) + goto out; + } + /* + * pad to end of page + */ + if (prtail((lines - pln), 0)) + goto out; + /* + * 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) { + if (pgpause) + prpause(pagecnt); + + if (!nohead && prhead(hbuf, fname, pagecnt)) + goto out; + } + + /* + * 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)) + goto out; + } + + /* + * pad to end of page + */ + if (pln && prtail((lines - pln), 0)) + goto out; + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + goto out; + error = 0; + goto out; +oomem: + mfail(); +out: + free(buf); + free(hbuf); + free(vc); + free(lstdat); + free(lindy); + if (inf != NULL && inf != stdin) + (void)fclose(inf); + return error; +} + +/* + * horzcol: print files with more than one column of output across a page + */ +static int +horzcol(int argc, char *argv[]) +{ + char *ptbf; + int pln; + int cnt = -1; + char *lstdat; + int col = colwd + 1; + int j; + int i; + int lncnt; + int pagecnt; + char *buf = NULL; + char *hbuf = NULL; + char *ohbuf; + const char *fname; + FILE *inf = NULL; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + int error = 1; + + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) + goto oomem; + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) + goto oomem; + 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) { + if (pgpause) + prpause(pagecnt); + + if (!nohead && + prhead(hbuf, fname, pagecnt)) + goto out; + } + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + goto out; + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + goto out; + + /* + * if EOF go to next file + */ + if (cnt < 0) + break; + ++pagecnt; + } + if (inf != stdin) + (void)fclose(inf); + } + if (eoptind < argc) + goto out; + error = 0; + goto out; +oomem: + mfail(); +out: + free(buf); + free(hbuf); + if (inf != NULL && inf != stdin) + (void)fclose(inf); + return error; +} + +/* + * mulfile: print files with more than one column of output and + * more than one file concurrently + */ +static int +mulfile(int argc, char *argv[]) +{ + char *ptbf; + int j; + int pln; + int cnt; + char *lstdat; + int i; + FILE **fbuf = NULL; + int actf; + int lncnt; + int col; + int pagecnt; + int fproc; + char *buf = NULL; + char *hbuf = NULL; + char *ohbuf; + const char *fname; + int ips = 0; + int cps = 0; + int ops = 0; + int mor = 0; + int error = 1; + + /* + * array of FILE *, one for each operand + */ + if ((fbuf = calloc(clcnt, sizeof(FILE *))) == NULL) + goto oomem; + + /* + * page header + */ + if ((hbuf = malloc((unsigned)(HDBUF + offst)*sizeof(char))) == NULL) + goto oomem; + 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) + goto out; + + /* + * 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(errf, + "pr: page width too small for %d columns\n", clcnt); + goto out; + } + actf = clcnt; + col = colwd + 1; + + /* + * line buffer + */ + if ((buf = malloc((unsigned)(pgwd+offst+1)*sizeof(char))) == NULL) + goto out; + 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) { + if (pgpause) + prpause(pagecnt); + + if (!nohead && prhead(hbuf, fname, pagecnt)) + goto out; + } + + /* + * output line + */ + if (otln(buf, j, &ips, &ops, 0)) + goto out; + + /* + * if no more active files, done + */ + if (actf <= 0) { + ++i; + break; + } + } + + /* + * pad to end of page + */ + if (i && prtail(lines-i, 0)) + goto out; + ++pagecnt; + } + if (eoptind < argc) + goto out; + error = 0; + goto out; +oomem: + mfail(); +out: + if (fbuf) { + for (j = 0; j < clcnt; j++) + if (fbuf[j] && fbuf[j] != stdin) + (void)fclose(fbuf[j]); + free(fbuf); + } + free(hbuf); + free(buf); + return error; +} + +/* + * 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) + */ +static int +inln(FILE *inf, char *buf, int lim, int *cps, int trnc, int *mor) +{ + int col; + int gap = ingap; + int ch = EOF; + char *ptbuf; + 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 + */ +static int +otln(char *buf, int cnt, int *svips, int *svops, int mor) +{ + int ops; /* last col output */ + int ips; /* last col in buf examined */ + int gap = ogap; + int tbps; + 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 (ips - ops > 1) { + /* + * 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 (ips - ops > 1) { + /* + * 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 + */ +static int +inskip(FILE *inf, int pgcnt, int lncnt) +{ + int c; + 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) + */ +static FILE * +nxtfile(int argc, char **argv, const char **fname, char *buf, int dt) +{ + FILE *inf = NULL; + struct timeval tv; + struct timezone tz; + struct tm *timeptr = NULL; + struct stat statbuf; + time_t curtime; + 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(errf, "pr: cannot get time of day, %s\n", + strerror(errno)); + eoptind = argc - 1; + return(NULL); + } + curtime = tv.tv_sec; + timeptr = localtime(&curtime); + } + 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(errf, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + curtime = tv.tv_sec; + timeptr = localtime(&curtime); + } else { + /* + * normal file processing + */ + if ((inf = fopen(argv[eoptind], "r")) == NULL) { + ++errcnt; + if (nodiag) + continue; + (void)fprintf(errf, "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(errf, + "pr: cannot get time of day, %s\n", + strerror(errno)); + return(NULL); + } + curtime = tv.tv_sec; + timeptr = localtime(&curtime); + } else { + if (fstat(fileno(inf), &statbuf) < 0) { + ++errcnt; + (void)fclose(inf); + (void)fprintf(errf, + "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", errf); + 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(char *buf, int wdth, int line) +{ + char *pt = buf + wdth; + + do { + *--pt = digs[line % 10]; + line /= 10; + } while (line && (pt > buf)); + + /* + * pad with space as required + */ + while (pt > buf) + *--pt = ' '; +} + +/* + * prpause(): pause before printing each page + * + * pagcnt page number + */ +static void +prpause(int pagcnt) +{ + + if (ttyout) { + int c; + + (void)putc('\a', stderr); + (void)fflush(stderr); + + while ((c = getc(ttyinf)) != '\n' && c != EOF) + ; + + /* + * pause ONLY before first page of first file + */ + if (pgpause == FIRSTPAGE && pagcnt == 1) + pgpause = NO_PAUSE; + } +} + +/* + * 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 + */ +static int +prhead(char *buf, const 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 argument 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 + */ +static int +prtail(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 + */ +static void +terminate(int which_sig) +{ + flsh_errs(); + (void)raise_default_signal(which_sig); + exit(1); +} + + +/* + * flsh_errs(): output saved up diagnostic messages after all normal + * processing has completed + */ +static void +flsh_errs(void) +{ + char buf[BUFSIZ]; + + (void)fflush(stdout); + (void)fflush(errf); + if (errf == stderr) + return; + rewind(errf); + while (fgets(buf, BUFSIZ, errf) != NULL) + (void)fputs(buf, stderr); +} + +static void +mfail(void) +{ + (void)fputs("pr: memory allocation failed\n", errf); +} + +static void +pfail(void) +{ + (void)fprintf(errf, "pr: write failure, %s\n", strerror(errno)); +} + +static void +usage(void) +{ + (void)fputs( + "usage: pr [+page] [-col] [-adFfmprt] [-e[ch][gap]] [-h header]\n", + errf); + (void)fputs( + " [-i[ch][gap]] [-l line] [-n[ch][width]] [-o offset]\n", + errf); + (void)fputs( + " [-s[ch]] [-T timefmt] [-w width] [-] [file ...]\n", + errf); +} + +/* + * setup: Validate command args, initialize and perform sanity + * checks on options + */ +static int +setup(int argc, char **argv) +{ + int c; + int eflag = 0; + int iflag = 0; + int wflag = 0; + int cflag = 0; + + ttyinf = stdin; + + if (isatty(fileno(stdout))) { + /* + * defer diagnostics until processing is done + */ + if ((errf = tmpfile()) == NULL) { + (void)fputs("Cannot defer diagnostic messages\n",stderr); + return(1); + } + ttyout = 1; + } else + errf = stderr; + while ((c = egetopt(argc, argv, "#adFfmrte?h:i?l:n?o:ps?T:w:")) != -1) { + switch (c) { + case '+': + if ((pgnm = atoi(eoptarg)) < 1) { + (void)fputs("pr: +page number must be 1 or more\n", + errf); + return(1); + } + break; + case '-': + if ((clcnt = atoi(eoptarg)) < 1) { + (void)fputs("pr: -columns must be 1 or more\n",errf); + return(1); + } + if (clcnt > 1) + ++cflag; + break; + case 'a': + ++across; + break; + case 'd': + ++dspace; + break; + case 'e': + ++eflag; + if ((eoptarg != NULL) && + !isdigit((unsigned char)*eoptarg)) + inchar = *eoptarg++; + else + inchar = INCHAR; + if ((eoptarg != NULL) && + isdigit((unsigned char)*eoptarg)) { + if ((ingap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -e gap must be 0 or more\n", errf); + return(1); + } + if (ingap == 0) + ingap = INGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -e %s\n", eoptarg); + return(1); + } else + ingap = INGAP; + break; + case 'f': + pgpause |= FIRSTPAGE; + /*FALLTHROUGH*/ + case 'F': + ++formfeed; + break; + case 'h': + header = eoptarg; + break; + case 'i': + ++iflag; + if ((eoptarg != NULL) && + !isdigit((unsigned char)*eoptarg)) + ochar = *eoptarg++; + else + ochar = OCHAR; + if ((eoptarg != NULL) && + isdigit((unsigned char)*eoptarg)) { + if ((ogap = atoi(eoptarg)) < 0) { + (void)fputs( + "pr: -i gap must be 0 or more\n", errf); + return(1); + } + if (ogap == 0) + ogap = OGAP; + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -i %s\n", eoptarg); + return(1); + } else + ogap = OGAP; + break; + case 'l': + if (!isdigit((unsigned char)*eoptarg) || + ((lines=atoi(eoptarg)) < 1)) { + (void)fputs( + "pr: Number of lines must be 1 or more\n",errf); + return(1); + } + break; + case 'm': + ++merge; + break; + case 'n': + if ((eoptarg != NULL) && + !isdigit((unsigned char)*eoptarg)) + nmchar = *eoptarg++; + else + nmchar = NMCHAR; + if ((eoptarg != NULL) && + isdigit((unsigned char)*eoptarg)) { + if ((nmwd = atoi(eoptarg)) < 1) { + (void)fputs( + "pr: -n width must be 1 or more\n",errf); + return(1); + } + } else if ((eoptarg != NULL) && (*eoptarg != '\0')) { + (void)fprintf(errf, + "pr: invalid value for -n %s\n", eoptarg); + return(1); + } else + nmwd = NMWD; + break; + case 'o': + if (!isdigit((unsigned char)*eoptarg) || + ((offst = atoi(eoptarg))< 1)){ + (void)fputs("pr: -o offset must be 1 or more\n", + errf); + return(1); + } + break; + case 'p': + pgpause |= EACHPAGE; + break; + case 'r': + ++nodiag; + break; + case 's': + ++sflag; + if (eoptarg == NULL) + schar = SCHAR; + else + schar = *eoptarg++; + if (eoptarg && *eoptarg != '\0') { + (void)fprintf(errf, + "pr: invalid value for -s %s\n", eoptarg); + return(1); + } + break; + case 'T': + timefrmt = eoptarg; + break; + case 't': + ++nohead; + break; + case 'w': + ++wflag; + if (!isdigit((unsigned char)*eoptarg) || + ((pgwd = atoi(eoptarg)) < 1)){ + (void)fputs( + "pr: -w width must be 1 or more \n",errf); + 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", + errf); + return(1); + } + if (merge) { + (void)fputs("pr: -m cannot be used with -a\n", errf); + 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", errf); + 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(errf, + "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; + } + } + + /* + * open /dev/tty if we are to pause before each page + * but only if stdout is a terminal and stdin is not a terminal + */ + if (ttyout && pgpause && !isatty(fileno(stdin))) { + if ((ttyinf = fopen("/dev/tty", "r")) == NULL) { + (void)fprintf(errf, "pr: cannot open terminal\n"); + return(1); + } + } + + return(0); +} diff --git a/usr.bin/pr/pr.h b/usr.bin/pr/pr.h new file mode 100644 index 000000000..0afd3746a --- /dev/null +++ b/usr.bin/pr/pr.h @@ -0,0 +1,79 @@ +/* $NetBSD: pr.h,v 1.5 2012/07/24 02:13:04 ginsbach Exp $ */ + +/*- + * Copyright (c) 1991 Keith Muller. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2012 + * The NetBSD Foundation, Inc. + * + * 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. 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. + * + * from: @(#)pr.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: pr.h,v 1.5 2012/07/24 02:13:04 ginsbach Exp $ + */ + +/* + * 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 + +/* when to pause before (for -f and -p options) */ +#define NO_PAUSE 0 +#define FIRSTPAGE 1 +#define ENSUINGPAGES 2 +#define EACHPAGE (FIRSTPAGE | ENSUINGPAGES) + +/* + * structure for vertical columns. Used to balance cols on last page + */ +struct vcol { + char *pt; /* ptr to col */ + int cnt; /* char count */ +};