Replaces commands/pr. No Minix-specific changes needed.
Change-Id: I3c4c03b38fc8c654e7c54c3a8af0e07a0dfc9f67
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 \
+++ /dev/null
-PROG= pr
-MAN=
-
-.include <bsd.prog.mk>
+++ /dev/null
-/* 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 <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <time.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-
-#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<width>] [-l<length>] [-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
- );
-}
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 \
+++ /dev/null
-.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).
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
mkdep mktemp \
\
nbperf newgrp nice nl nohup nvi \
- passwd \
+ passwd pr \
printenv printf \
rev \
\
--- /dev/null
+# $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 <bsd.prog.mk>
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <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)'?'
+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);
+}
--- /dev/null
+/* $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 *);
--- /dev/null
+.\" $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.
--- /dev/null
+/* $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 <sys/cdefs.h>
+#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 <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 <time.h>
+#include <unistd.h>
+#include <util.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 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);
+}
--- /dev/null
+/* $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 */
+};