]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing usr.bin/pr 66/1066/1
authorThomas Cort <tcort@minix3.org>
Thu, 24 Oct 2013 16:39:25 +0000 (12:39 -0400)
committerThomas Cort <tcort@minix3.org>
Fri, 25 Oct 2013 13:05:26 +0000 (09:05 -0400)
Replaces commands/pr. No Minix-specific changes needed.

Change-Id: I3c4c03b38fc8c654e7c54c3a8af0e07a0dfc9f67

13 files changed:
commands/Makefile
commands/pr/Makefile [deleted file]
commands/pr/pr.c [deleted file]
man/man1/Makefile
man/man1/pr.1 [deleted file]
releasetools/nbsd_ports
usr.bin/Makefile
usr.bin/pr/Makefile [new file with mode: 0644]
usr.bin/pr/egetopt.c [new file with mode: 0644]
usr.bin/pr/extern.h [new file with mode: 0644]
usr.bin/pr/pr.1 [new file with mode: 0644]
usr.bin/pr/pr.c [new file with mode: 0644]
usr.bin/pr/pr.h [new file with mode: 0644]

index 062cdda9ed6b9d079a1b8f22a637c170f2ee3e69..7fd1e3c14cf5d11f4d83f254ee26d39a3d721fa7 100644 (file)
@@ -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 (file)
index d362de2..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  pr
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/pr/pr.c b/commands/pr/pr.c
deleted file mode 100644 (file)
index 2ad7c05..0000000
+++ /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 <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
-       );
-}
index 94222a905e78327fae83c1a4ea041c07a0413054..bd7a170f25732494bc224cc206d4e8e58f19c150 100644 (file)
@@ -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 (file)
index 6993d4a..0000000
+++ /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).
index c10508b087fdd0dfa90b73df509a4fff76753365..c37ac0b1b3247e63583ac33fac7397e5c46b8fba 100644 (file)
 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
index 87309c805749db4dd54d6516a89ba998a7eff37c..75ca3a1eee3e415d706b513dd289296f6439e5c2 100644 (file)
@@ -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 (file)
index 0000000..e369187
--- /dev/null
@@ -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 <bsd.prog.mk>
diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c
new file mode 100644 (file)
index 0000000..5c43476
--- /dev/null
@@ -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 <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);
+}
diff --git a/usr.bin/pr/extern.h b/usr.bin/pr/extern.h
new file mode 100644 (file)
index 0000000..6aeb61c
--- /dev/null
@@ -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 (file)
index 0000000..c0997f9
--- /dev/null
@@ -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 (file)
index 0000000..91e8075
--- /dev/null
@@ -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 <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);
+}
diff --git a/usr.bin/pr/pr.h b/usr.bin/pr/pr.h
new file mode 100644 (file)
index 0000000..0afd374
--- /dev/null
@@ -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 */
+};