req@snow.UUCP (Russell Quin) (05/22/85)
pf is a C program to print files in multiple columns without truncating its input on long lines. To do this, it prints stuff using as many columns as will fit on each page -- this means that pages of the same file are printed in varying numbers of columns, which saves paper & usually reduces printing time. Fight Back For Trees!!! A manual page & a rudimentary makefile are included. Mail bug fixes to me at ukc!ubu!snow!req please; I will repost -- if there are any! :-) :-) :-) The software has been in use for over a year now, with no major known bugs remaining. A rewrite would, however, possibly improve efficiency no end. The original author was Crispin Goswell, who is possibly rlvd!caag, but I'm not sure about that. I have fixed a couple of minor bugs, but left the original style/layout. Enjoy! - Russell PS: snow users will find this in ~req/bin/pf, so don't make more copies! # # This is a shell archive. Cut below the CUT line, save in a file, make foo # executable (chmod +x foo) (if you called it foo), then run foo # #---------------- CUT HERE, DO NOT INCLUDE THIS LINE -------------------# #!/bin/sh echo 'Start of pack.out, part 01 of 01:' echo 'x - makefile' sed 's/^X//' > makefile << '/' X# Makefile for pf.c X# pf.c was written by Crispin Goswell; X# minor bug fixes and makefile by Russell Quin 1984/5 X# XCCFLAGS=-s -O Xpf: pf.c X $(CC) $(CCFLAGS) pf.c -o pf / echo 'x - pf.1' sed 's/^X//' > pf.1 << '/' X X X XPF(1) UNIX Programmer's Manual PF(1) X X X XNAME X pf - print file with automatic multi-columning X XSYNOPSIS X pf [ option ] ... [ file ] ... X XDESCRIPTION X _P_f produces a printed listing of one or more _f_i_l_e_s. If there X are no file arguments, or if a file name is '-' _p_f prints X its standard input. X X Options apply to all following files: X X -_F Specify footer information - see note which follows X options. X X -_H Specify header information - see note which follows X options. X X -_d Don't break a page for each file - continue in next X column. X X -_e Ensure all the columns on a page are of equal width. X X -_f Produce true action for formfeeds, i.e. move to next X page. The default action is to move to the top of the X next column. X X -_h Produces a list of options. X X -_j Causes the text to be justified to the right hand X column (old behavior). X X -_ln Set the length of the page to 'n' (the default is 66 X lines). X X -_mn Set the maximum number of columns to use to 'n' X (default is the page width). X X -_sn Set the minimum width to separate columns by to 'n' X (the default is 1). X X -_tn Set the width to which tabs are expanded to 'n' (the X default is 8). X X -_wn Set the width of the page to 'n' (the default is 132 X positions). X X The header and footer options H and F, respectively allow X specification as follows: X X X X X XPrinted 6/22/84 1 X X X X X X XPF(1) UNIX Programmer's Manual PF(1) X X X X Mere inclusion indicates that headers and footers are X required. X X Either can be followed immediately by one of l, c or r which X mean that the next argument will be the _l_e_f_t, _c_e_n_t_r_e or X _r_i_g_h_t header or footer respectively. The s specification, X immediately followed by a number will give the vertical size X of the header or footer, X e.g. Fs3 indicates that footers are to be placed in three X lines at the bottom of the page. X X The three headers or footers are placed on a single line in X the middle of the vertical space. X XNOTE X _P_f knows about control characters and escape sequences of X length two: it assumes these to print in zero width and will X print them even if the line has been truncated because it is X longer than a page. These means that bold will get turned X off properly when the line is too long. To prevent this X behavior, put the input through _c_o_l_r_m first. X XAUTHOR X Crispin Goswell X XSEE ALSO X cat(1), colrm(1), pr(1) X X X X X X X X X X X X X X X X X X X X X X X X X X X X XPrinted 6/22/84 2 X X X / echo 'x - pf.c' sed 's/^X//' > pf.c << '/' X#include <stdio.h> X X#define EOL '\0' X Xint width = 132, length = 66, sep = 1, justify = 0, bufsiz, headers = 0, X equal = 0, dont = 0, ncols = 0, form = 0, tabsize = 8, maxcols = 0; X Xint widest, eof, this_col, feed, *col_width; X Xchar ***cols, *buffer, *malloc (), *getline (); X Xchar *left_top, *centre_top, *right_top; Xchar *left_bot, *centre_bot, *right_bot; Xint top_space = -1, bot_space = -1; X Xmain (argc, argv) char **argv; X { X char *arg; X X for (++argv; argc > 1; argc--, argv++) X if (*(arg = *argv) != '-' || *++arg == EOL) X break; X else switch (*arg) X { X case 'm': maxcols = atoi (++arg); break; X case 'j': justify = 1; break; /* removes spaces to right */ X case 't': tabsize = atoi (++arg); break; X case 'f': form = 1; break; /* request true form-feed */ X case 'w': width = atoi (++arg); break; X case 'l': length = atoi (++arg); break; X case 's': sep = atoi (++arg); break; X /* minimum column separation */ X case 'e': equal = 1; break; /* request equal width columns */ X case 'd': dont = 1; break; /* don't break a page on \f */ X case 'H': X headers = 1; X if (header (++arg, argv[1])) X argv++, argc--; X break; X case 'F': X headers = 1; X if (footer (++arg, argv[1])) X argv++, argc--; X break; X X default: X if ((*argv)[1] != 'h') X fprintf (stderr, "pf: unknown option '%s'\n", *argv ); X fprintf (stderr, "\ XValid options (with defaults if appropriate) are:-\n\ X\n\ X -F produce footers\n\ X -H produce headers\n\ X -d don't break a page between files - break column instead\n\ X -e equal width columns\n\ X -f true form feeds - cause a new page instead of new column\n\ X -h produces this listing and exits\n\ X -m maximum number of columns (defaults to paper width)\n\ X -j remove spaces to the right (old behavior)\n\ X -l66 length of paper\n\ X -s1 minimum separation of columns\n\ X -t8 tab size\n\ X -w132 width of paper\n\ X\n\ X-H and -F are normally turned off and can be appended by the following args\n\ X\n\ X l following argument is left header\n\ X c following argument is centre header\n\ X r following argument is right header\n\ X s3 number of lines used for headers\n\ X\n\ X"); X exit (1); X } X if (maxcols == 0) maxcols = width; X if (top_space < 0) top_space = 3; X if (bot_space < 0) bot_space = 3; X if (tabsize <= 0 || length <= 0 || width <= 0) X fprintf (stderr, X "pf: non-positive or non-numeric flag (-t, -l or -w)\n"), X exit (1); X cols = (char ***) malloc (width * sizeof (char ***)); X col_width = (int *) malloc (width * sizeof (int *)); X buffer = malloc (bufsiz = width + 1); X if (argc == 1) X *argv = "-", argc = 2; X if (headers) X if (length <= top_space + bot_space) X fprintf (stderr, "pf: page too short for headers\n"), X exit (1); X else X length -= top_space + bot_space; X else X ; X for (; argc > 1; argc--, argv++) X { X int fd; X FILE *in; X X if (**argv == '-' && (*argv)[1] == EOL) X in = stdin; X else if ((fd = open (*argv, 0)) == -1) X fprintf (stderr, "pf: cannot open '%s'\n", *argv), exit (1); X else X close (fd), in = fopen (*argv, "r"); X readfrom (in); X if (in != stdin) X fclose (in); X if (!dont) output_page (); X } X if (ncols > 0) output_page (); X } X Xheader (ch, s) char *ch, *s; X { X switch (*ch) X { X case 'l': left_top = s; break; X case 'c': centre_top = s; break; X case 'r': right_top = s; break; X case 's': top_space = atoi (++ch); return 0; X case '\0': return 0; X X default: fprintf (stderr, "pf: -H not followed by l, c, r or s\n"); X exit (1); X } X return 1; X } X Xfooter (ch, s) char *ch, *s; X { X switch (*ch) X { X case 'l': left_bot = s; break; X case 'c': centre_bot = s; break; X case 'r': right_bot = s; break; X case 's': bot_space = atoi (++ch); return 0; X case '\0': return 0; X X default: fprintf (stderr, "pf: -F not followed by l, c, r or s\n"); X exit (1); X } X return 1; X } X Xreadfrom (f) FILE *f; X { X eof = 0; X while (!eof) X { X read_col (f, ncols++); X if (width_so_far () > width || form && feed || ncols >= maxcols) X output_page (); X } X } X Xread_col (f, n) FILE *f; int n; X { X int line; X char **this = cols[n] = (char **) malloc (length * sizeof (char *)); X X this_col = n; feed = 0; col_width [n] = 0; X for (line = 0; line < length; line++) X this [line] = getline (f); X } X Xaddch (b, c) char **b; int c; X { X char *realloc (); X X if (&buffer [bufsiz] == *b) X { X buffer = realloc (buffer, bufsiz + width); X *b = &buffer [bufsiz]; X bufsiz += width; X } X *(*b)++ = c; X } X Xchar * Xgetline (f) FILE *f; X { X int w = 0, *cw = &col_width [this_col], m = 0, c; X char *result, *b = buffer; X X X if (feof (f)) X { X eof = 1; X return NULL; X } X if (feed) X return NULL; X else X { X while ((c = getc (f)) != '\n' && c != '\f' && c != EOF) X switch (c) X { X case '\r': if (m < w) m = w; w = 0; addch (&b, '\r'); break; X case '\b': if (m < w) m = w; w--; addch (&b, '\b'); break; X case '\t': do addch (&b, ' '), ++w; while (w % tabsize != 0); break; X case 27: addch (&b, c); if (m < w) m = w; --w; break; X default: addch (&b, c); if (c >= ' ' && c < 127) ++w; break; X } X w = m > w ? m : w; X if (w > width) w = width; X addch (&b, EOL); X feed |= (c == '\f'); eof = c == EOF; X } X *cw = *cw < w ? w : *cw; X result = malloc (b - buffer); X strcpy (result, buffer); X return result; X } X Xwidth_so_far () X { X int i, sum = 0; X X widest = 0; X for (i = 0; i < ncols; i++) X { X int cwi = col_width [i]; X X sum += cwi, widest = widest < cwi ? cwi : widest; X } X sum = (equal ? widest * ncols : sum) + (ncols - justify -1 /*REQ*/ ) * sep; X return (sum); X } X Xoutput_page () X { X int cum = 0, n = 0, new, real_sep, extra, line, col; X X if (ncols <= 0) return; /*REQ*/ X if (headers) X { X space (top_space / 2 - 1 + top_space % 2, '\n'); X if (top_space) X { X printf ("%s", left_top); X space (width / 2 - strlen (centre_top) / 2 - strlen (left_top), X ' '); X printf (centre_top); X space (width / 2 - strlen (right_top) - strlen (centre_top) / 2, X ' '); X printf ("%s\n", right_top); X } X space (top_space / 2, '\n'); X } X X --ncols; X if (width_so_far () > width) /* executed for side-effect of */ X fprintf (stderr, "pf: internal error"), /* recomputing widest */ X exit (1); X ++ncols; X X X for (n = 0; n < ncols; n++) /* n is number of columns to print */ X if ((new = cum + (equal ? widest : col_width [n]) + sep) X > width + sep || X equal && col_width [n] > widest) X break; X else X cum = new; X X if (n > 1) X real_sep = sep + (width + sep - cum) / (n - justify), X extra = (width + sep - cum) % (n - justify); X X for (line = 0; line < length; line++) X for (col = 0; col < n; col++) X { X char *text = cols[col][line]; X putline ((text != NULL ? text : ""), X (col == n ? 0 : equal ? widest : col_width [col]), X col + 1 != n); X if (text != NULL) X free (text); X if (col + 1 < n) X space (real_sep + (col < extra ? 1 : 0), ' '); X else X putchar ('\n'); X } X for (col = 0; col < n; col++) X free (cols[col]); X for (col = n; col < ncols; col++) X cols[col-n] = cols[col], X col_width[col-n] = col_width[col]; X ncols -= n; X X if (headers) X { X space (bot_space / 2, '\n'); X if (bot_space) X { X printf ("%s", left_bot); X space (width / 2 - strlen (centre_bot) / 2 - strlen (left_bot), X ' '); X printf (centre_bot); X space (width / 2 - strlen (right_bot) - strlen (centre_bot) / 2, X ' '); X printf ("%s\n", right_bot); X } X space (bot_space / 2 - 1 + bot_space % 2, '\n'); X } X } X Xspace (n, c) X { X int i; X X for (i = 0; i < n; i++) X putchar (c); X } X Xputline (text, width, pad) char *text; int width, pad; X { X int cols = 0; X X for (; *text != EOL; text++) X switch (*text) X { X case 27: X putchar (*text++); X if (*text == EOL) X --text; X else X putchar (*text); X break; X X case '\b': X if (cols > 0 && cols <= width) X putchar (*text); X --cols; X break; X X case '\r': X cols = cols > width ? width : cols; X while (cols > 0) X --cols, putchar ('\b'); X break; X X default: X if (cols >= 0 && cols < width || *text < ' ' || *text >= 127) X putchar (*text); X if (*text >= ' ' && *text < 127) X ++cols; X break; X } X if (pad) X while (cols++ < width) X putchar (' '); X } / echo 'x - pf.n' sed 's/^X//' > pf.n << '/' X.TH PF 1 X.UC 4 X.SH NAME Xpf \- print file with automatic multi-columning X.SH SYNOPSIS X.B pf X[ option ] ... X[ file ] ... X.SH DESCRIPTION X.I Pf Xproduces a printed listing of one or more X.I files. XIf there are no file arguments, or if a file name is '\-' X.I pf Xprints its standard input. X.PP XOptions apply to all following files: X.TP X.BI \- F XSpecify footer information \- see note which follows options. X.TP X.BI \- H XSpecify header information \- see note which follows options. X.TP X.BI \- d XDon't break a page for each file \- continue in next column. X.TP X.BI \- e XEnsure all the columns on a page are of equal width. X.TP X.BI \- f XProduce true action for formfeeds, i.e. move to next page. The default Xaction is to move to the top of the next column. X.TP X.BI \- h XProduces a list of options. X.TP X.BI \- j XCauses the text to be justified to the right hand column (old behavior). X.TP X.BI \- l\fPn XSet the length of the page to '\fin\fP' (the default is 66 lines). X.TP X.BI \- m\fPn XSet the maximum number of columns to use to '\fin\fP' (default is the page Xwidth). X.TP X.BI \- s\fPn XSet the minimum width to separate columns by to '\fin\fP' (the default is 1). X.TP X.BI \- t\fPn XSet the width to which tabs are expanded to '\fin\fP' (the default is 8). X.TP X.BI \- w\fPn XSet the width of the page to '\fin\fP' (the default is 132 positions). X.PP XThe header and footer options \fBH\fP and \fBF\fP, respectively allow Xspecification as follows: X.LP XMere inclusion indicates that headers and footers are required. X.LP XEither can be followed immediately by one of \fBl\fP, \fBc\fP or \fBr\fP Xwhich mean that the next argument will be the \fIleft\fP, \fIcentre\fP or X\fIright\fP header or footer respectively. The \fBs\fP specification, Ximmediately followed by a number will give the vertical size of the header Xor footer, X.br Xe.g. X\fBFs3\fP indicates that footers are to be placed in three lines at the Xbottom of the page. X.LP XThe three headers or footers are placed on a single line in the middle of Xthe vertical space. X.SH NOTE X.I Pf Xknows about control characters and escape sequences of length two: Xit assumes these to print in zero width and will print them even if the line Xhas been truncated because it is longer than a page. XThese means that bold will get turned off properly when the line is too long. XTo prevent this behavior, Xput the input through X.I colrm Xfirst. X.SH AUTHOR XCrispin Goswell X.SH "SEE ALSO" Xcat(1), colrm(1), pr(1) / echo 'Part 01 of pack.out complete.' exit -- # ... mcvax!ukc!qtlon!flame!ubu!snow!req # Striving to promote the interproduction of epimorphistic conformability ....