pf - print files economically

Russell Quin req at snow.UUCP
Wed May 22 08:08:03 AEST 1985


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 ....



More information about the Comp.sources.unix mailing list