lee@sq.sq.com (Liam R. E. Quin) (12/15/89)
I tried twice to mail this to ++Brandon for comp.sources.misc, and got a week's silence plus a bounce, so here it is... [Brandon, if you see this, it might be nice to archive it...] pfm is rather like pr, except that * it atomatically works out how many columns of text will fit on each page, on a page-by-page basis, without truncating. * it can do some fancy stuff with headers/footers, and you can tell it to make the input a pipe (for example calling "vis", "cat -v" or whatever for each input file). You will need this if you want to try the ascii to postscript filter I am posting in the next article... It used to be called pf, but I typed "pf" too often by mistake, so I renamed it to pfm. If you are using the version that was posted to net.sources in 1985 or so, you will find this one has some minor bugs removed and is noticeably faster on slow systems [suns, mips etc :-) :-)]. Lee --- Liam Russell Quin --- lee@sq.com #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: Makefile pfm.1 pfm.c # Wrapped by lee@sq on Thu Dec 14 16:10:00 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(2595 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# $Header: /u/lee/src/pfm/RCS/Makefile,v 1.5 89/12/11 14:05:58 lee Exp $ X# X# Makefile for pf.c X# pf.c was written by Crispin Goswell; X# minor bug fixes and makefile by Russell Quin, X# who subsequently changed his name to Liam Quin. X# X# make, or make all -- makes the program X# make tidy -- remove all generated intermediate files (including pfm.c) X# make clean -- remove all generated files X# X# CFLAGS=-s -O CFLAGS=-O PROGS=pfm DOCS=pfm.1 LINTFLAGS=-a -b -c -h -x X BIN=/usr/local/bin MANDIR=/usr/local/man X RM=/bin/rm -f # for "make clean" and "make tidy" CO=co # rcs check out program, if you have it X CLEAN=clean MAKE=make SHELL=/bin/sh X default: X @echo make $(PROGS) -- make the program X @echo make lint -- run lint on the source for $(PROGS) X @echo make install -- make $(PROGS), install them, $(MAKE) clean X @echo make tidy -- clean up without deleting: $(PROGS) X @echo make clean -- remove all generated files X @echo make all -- make: $(PROGS) tidy X @echo make rcsclean -- $(RM) non-writable files also in RCS X @false # force an error X X# you might want to remove the "tidy" for debugging... all: $(PROGS) $(DOCS) tidy X install: $(PROGS) $(DOCS) X for i in $(PROGS); do \ X mv $$i $(BIN) ; \ X done ; \ X for i in $(DOCS); do \ X mv $$i $(MANDIR) ; \ X done X $(MAKE) $(CLEAN) X lint: pfm.lint X pfm.lint: pfm.c X lint $(LINTFLAGS) pfm.c > pfm.lint 2>&1 X X# making pfm depend on Makefile forces a recompile when CFLAGS change. pfm: pfm.c Makefile X $(CC) $(CFLAGS) pfm.c -o pfm X X# make tidy removes all generated files, including pfm.c if there is X# an RCS directory and pfm.c is not writeable. X# you have been warned! tidy: rcsclean X $(RM) *.o core tags X clean: tidy X $(RM) $(PROGS) X X# remove files that are also in the RCS directory rcsclean: X -if test -d RCS; then \ X for i in `ls RCS` ; \ X do \ X f=`basename $$i ,v` ; \ X if test ! -w $$f ; \ X then $(RM) $$f ; \ X echo Removed $$f; \ X fi ; \ X done ; \ X fi X X# if there are more programs, use a .SUFFIX rule instead here: pfm.1: RCS/pfm.c,v X $(CO) pfm.1 X chmod -w pfm.1 # to ensure that it gets removed X pfm.c: RCS/pfm.c,v X $(CO) pfm.c X chmod -w pfm.c # to ensure that it gets removed X ctags pfm.c X X# X# $Log: Makefile,v $ X# Revision 1.5 89/12/11 14:05:58 lee X# CFLAGS change X# X# Revision 1.4 89/12/03 20:58:43 lee X# added tidy to default make. X# X# Revision 1.3 89/12/03 20:55:07 lee X# added make install entry, plus improved portability of rcsclean by X# adding a chmod. X# X# Revision 1.2 89/12/03 20:50:59 lee X# Added code to remove rcs files. X# X# Revision 1.1 89/12/03 20:36:34 lee X# Initial revision X# X# END_OF_FILE if test 2595 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'pfm.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pfm.1'\" else echo shar: Extracting \"'pfm.1'\" \(5220 characters\) sed "s/^X//" >'pfm.1' <<'END_OF_FILE' X.\" $Header: /u/lee/src/pfm/RCS/pfm.1,v 1.4 89/12/14 16:05:03 lee Exp Locker: lee $ X.TH PFM 1 X.UC 4 X.SH NAME pfm \- print file with automatic multi-columning X.SH SYNOPSIS X.B pfm X[ option ] ... X[ file ] ... X.SH DESCRIPTION X.I Pfm produces a printed listing of one or more X.I files. If there are no file arguments, or if a file name is \- X.I pfm prints its standard input. X.PP Options apply to all following files: X.TP X.B \-d Don't break a page for each file \- continue in next column. X.TP X.B \-e XEnsure all the columns on a page are of equal width. X.TP X.B \-f Produce true action for formfeeds, i.e. move to next page. The default action is to move to the top of the next column. X.TP X.B \-F Specify footer information \- see note which follows options. X.TP X.B \-H Specify header information \- see note which follows options. X.TP X.B \-h Produces a list of options. X.TP X.B \-j Causes the text to be justified to the right hand column (old behavior). X.TP X.BI \-l n Set the length of the page to X.I n X(the default is 66 lines). X.TP X.BI \-m n Set the maximum number of columns to use to X.I n X(default is the page width). X.TP X.BI \-N n Number each output line, allowing space for at least X.I n digits in the line number (larger numbers will print correctly, but will intrude into the text area). The default for X.I N is 4 digits. X.TP X.BI \-n n Number each output line, but reset the numbers to one at the start of each new file. The optional argument X.I n is treated in the same way as that for the X.B \-N option. X.TP X.BI \-p cmd XFilter input for each file through the given command. Any number of X.B %f sequences may occur in X.IR cmd , and will be replaced by the file name each time the command is used. This option has no effect when printing from standard input, although in that case a pipe can be used directly from the shell. X.TP X.BI \-s n Set the minimum width to separate columns by to X.I n X(the default is 1). X.TP X.BI \-t n Set the width to which tabs are expanded to X.I n X(the default is 8). X.TP X.BI \-w n Set the width of the page to X.I n X(the default is 132 positions). X.PP The header and footer options X.B \-BH and X.B \-BF respectively, allow specification as follows: X.LP Mere inclusion indicates that headers and footers are required. X.LP XEither can be followed immediately by one of X.BR l , X.BR c , or X.B r which mean that the next argument will be the X.IR left , X.I centre or X.I right header or footer respectively. The X.B s specification, X.I immediately followed by a number, will give the vertical size of the header or footer, X.br e.g. X.B \-Fs3 indicates that footers are to be placed in three lines at the bottom of the page. X.PP Within any header or footer, the following sequences are undertood: X.TP X.B %% is replaced by a literal X.B % sign. X.TP X.B %l X(lower-case ell) is replaced by the line number of the first line of text on the page (counting the first line in that file as line one). X.TP X.B %L is replaced by the line number of the first line of text on the page. This number is not reset at the start of each new input file. X.TP X.B %n is replaced by the line number of the X.I last line of text on the page, i.e., the bottom line in the right-most column, numbered within the file. X.TP X.B %N is replaced by the line number of the last line on the page. This number is not reset at the start of each new input file. X.TP X.B %p is preplaced by the current page number within the current file. X.TP X.B %P is replaced by the current output page number. This number is not reset at the start of each new input file. X.TP X.B %s is replaced by the name of the file that was being printed at the X.I start of this page. X.TP X.B %e is replaced by the name of the file that was being printed at the X.I end of the current page. X.P Any other character after a X.BR % -sign in a header or footer prints as itself, and a warning is generated. X.LP The three headers or footers are placed on a single line in the middle of the vertical space. X.SH NOTE X.I Pfm knows about control characters and escape sequences of length two: it assumes these to print in zero width and will print them even if the line has been truncated because it is longer than a page. These means that bold will get turned off properly when the line is too long. To prevent this behavior, put the input through X.I colrm first. X.SH AUTHOR Crispin Goswell X.br Modified slightly and distrinbuted (with permission) by Liam Quin. X.SH "SEE ALSO" cat(1), colrm(1), pr(1) X.SH BUGS Too many options. X.br A more general syntax for headers and line numbers would help, but the program would to begin to turn into a text formatter. X.br No way to print a list of all the filenames on the page in the header. This would affect the number of columns prnted on the page if the filenames were long, however. X.\" X.\" $Log: pfm.1,v $ X.\" Revision 1.4 89/12/14 16:05:03 lee X.\" added new options. X.\" Fixed Log comment string. X.\" X.\" Revision 1.3 89/12/14 16:04:25 lee X.\" fixed font changes X.\" X.\" Revision 1.2 89/12/14 12:05:34 lee X.\" tidied up a little for distribution. X.\" Fixed a bug in -p option (thanks to Mark Brader for commenting on and X.\" improving my solution) X.\" X.\" Revision 1.1 89/12/03 20:36:50 lee X.\" Initial revision X.\" X.\" END_OF_FILE if test 5220 -ne `wc -c <'pfm.1'`; then echo shar: \"'pfm.1'\" unpacked with wrong size! fi # end of 'pfm.1' fi if test -f 'pfm.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pfm.c'\" else echo shar: Extracting \"'pfm.c'\" \(20126 characters\) sed "s/^X//" >'pfm.c' <<'END_OF_FILE' X/* pfm.c -- print files in multiple columns X * originally written by Crispin Goswell, from an idea we had at X * Warwick University, England, in 1983 X * X * Modified by me (Liam Quin) to use the new "getopt()" routine X * and to be a little more portable now that the world's no longer X * all a vax :-) :-) In the process, I did massive re-indenting. X * X * I have also renamed it from pf to pfm, as I type "pf" too often by X * mistake for pr or ps... X * X * Liam Quin, liam@sq.com, December 1989 X * X * $Header: /u/lee/src/pfm/RCS/pfm.c,v 1.6 89/12/14 16:05:25 lee Exp $ X * X * (see the end of this file for the RCS log) X */ X X#ifdef BSD X# define strrchr rindex X#endif X X#include <stdio.h> X#include <ctype.h> /* for isprint() */ X#include <malloc.h> X X#define Malloc(n) malloc((unsigned)(n)) X#define Realloc(s, n) realloc(s, (unsigned)(n)) X X#define EOL '\0' X#define TW(s) (s ? Title(s, (FILE *) 0) : 0) X X#ifndef STREQ X# define STREQ(henry,utzoo) ((*(henry)== *(utzoo)) && !strcmp(henry, utzoo)) X#endif X int 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 int widest, eof, this_col, feed, *col_width; typedef struct { X long File; X long Overall; X} t_Lines; X t_Lines *first_lines, *last_lines; X extern char *malloc(), *strcpy(), *strcat(); X char *getline(); char ***cols = 0; X char *left_top = 0, *centre_top = 0, *right_top = 0; char *left_bot = 0, *centre_bot = 0, *right_bot = 0; int top_space = -1, bot_space = -1; int NumberLines = 0; /* an evil bletch I put there */ int NumberWidth = 6; char *PipeCmd = 0; X char *progname = "pfm"; X X/* Things that can be included in titles: */ char *FilePage = 0; char *DocPage = 0; char *ThisName; int NameLen; long DocLine = 0L; long FileLine = 0L; char **names; X main(argc, argv) X int argc; X char **argv; X{ X extern char *strrchr(); X char *arg; X X /* determine program name for error reporting */ X arg = strrchr(argv[0], '/'); X if (arg != (char *) 0) { X arg++; /* step over the final / */ X } X if (!arg || !*arg) arg = argv[0]; /* best we can do (we're not on Unix?) */ X progname = arg; X X for (++argv; argc > 1; argc--, argv++) { X if (*(arg = *argv) != '-' || *++arg == EOL) { X break; X } else switch (*arg) { 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 'N': /* number throughout input */ X NumberLines = 1; X /* fall through */ X case 'n': /* reset on file boundary */ X NumberLines++; /* i.e., 1 or 2 */ X if (isdigit(arg[1])) { X NumberWidth = atoi(++arg); X } X break; X case 'p': X PipeCmd = argv[1]; --argc; ++argv; X 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 } X break; X case 'F': X headers = 1; X if (footer(++arg, argv[1])) { X argv++; argc--; X } X break; X X default: X if ((*argv)[1] != 'h') X fprintf(stderr, "%s: unknown option `%s'\n", X progname, *argv ); X fprintf(stderr, "\ Valid options (with defaults if appropriate) are:-\n\ X\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 exit\n\ X -F produce footers\n\ X -H produce headers\n\ X -j remove spaces to the right (original behavior)\n\ X -l66 length of paper\n\ X -m maximum number of columns (defaults to paper width)\n\ X -n4 number lines in each file, allow 4 digits (default)\n\ X -N4 number lines in the output, allowing 4 digits (default)\n\ X -p cmd pipe the input for each file through cmd\n\ X -s1 minimum separation of columns\n\ X -t8 tab size\n\ X -w132 width of paper\n\ X -x produces this explanation and exit\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 } X if (maxcols == 0) maxcols = width; X if (sep) { X if (maxcols > width / (sep + 1)) maxcols = width / (sep + 1); X } X if (maxcols < 1) { X fprintf(stderr, X "%s: No colums (each %d apart) fit on a %d-wide page\n", X progname, sep, width); X exit(1); X } 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 "%s: non-positive or non-numeric flag (-t, -l or -w)\n", progname), X exit(1); X /*NOSTRICT*/ cols = (char ***) Malloc((maxcols + 1) * sizeof(char **)); X /*NOSTRICT*/ col_width = (int *) Malloc((maxcols+1) * sizeof(int)); X /*NOSTRICT*/ first_lines = (t_Lines *) Malloc((maxcols+1)*sizeof(t_Lines)); X /*NOSTRICT*/ last_lines = (t_Lines *) Malloc((maxcols+1)*sizeof(t_Lines)); X /*NOSTRICT*/ names = (char **) Malloc((maxcols + 1) * sizeof(char *)); X X if (!cols || !col_width || !first_lines || !last_lines || !names) { X fprintf(stderr, "%s: not enough memory to allocate buffers\n", X progname); X exit(0); X } X X if (argc == 1) { X#ifdef unix X /*NONPORT*/ X *argv = "-", argc = 2; X#else X this needs fixing, sorry -- Liam. ;;;; X#endif X } X X if (headers) { X if (length <= top_space + bot_space) { X fprintf(stderr, "%s: page too short for headers\n", progname); X exit(1); X } else { X length -= top_space + bot_space; X } X } X X for (; argc > 1; argc--, argv++) { X FILE *in; X X ThisName = (*argv); X DocLine = 0; X X if (**argv == '-' && (*argv)[1] == EOL) { X in = stdin; X ThisName = "-"; X NameLen = 1; /* strlen("-"); */ X } else if (PipeCmd) { X /* this might be a misfeature, I'm not sure */ X int len = strlen(PipeCmd + strlen(ThisName) + 1); X char *buf = Malloc(len); X char *p, *q; X int NameIncluded = 0; X X NameLen = strlen(ThisName); X if (!buf) { X fprintf(stderr, "%s: %s: -p: no memory for pipe \"%s\"\n", X progname, ThisName, PipeCmd); X exit(1); X } X for (p = PipeCmd, q = buf; *p; q++, p++) { X if (*p == '%' && p[1] == 'f') { X ++NameIncluded; X if (q - buf + NameLen >= len - 3) { X int where = q - buf; X X if (!(buf = Realloc(buf, len += NameLen + 3))) { X fprintf(stderr, "%s: RAM shortag\n", progname); X exit(1); X } X q = &buf[where]; X } X (void) strcpy(q, ThisName); X q += strlen(q); X ++p; /* skip over the f in "%f" */ X } else { X if (q - buf >= len - 2) { X int where = q - buf; X X if (!(buf = Realloc(buf, len += 10 + NameLen))) { X fprintf(stderr, "%s: RAM shorta\n", progname); X exit(1); X } X q = &buf[where]; /* in case buf changed */ X } X *q = *p; X } X } X *q = '\0'; X if (!NameIncluded) { X if (q - buf >= len - NameLen - 3) { X if (!(buf = Realloc(buf, len += 10 + NameLen))) { X fprintf(stderr, "%s: %s: no RAM for pipe\n", X progname, ThisName); X exit(1); X } X } X (void) strcpy(q, " < "); X (void) strcat(q, ThisName); X } X X if ((in = popen(buf, "r")) == (FILE *) 0) { X fprintf(stderr, "%s: %s: -p: can't open input pipe \"%s\"\n", X progname, ThisName, buf); X exit(1); X } X /** fprintf(stderr, "[[%s]]\n", buf); **/ X (void) free(buf); X } else { X NameLen = strlen(ThisName); X if ((in = fopen(ThisName, "r")) == (FILE *) 0) { X fprintf (stderr, "%s: cannot open '%s'\n", progname, ThisName); X exit(1); X } X } X DocPage = 0; X readfrom(in); X if (in != stdin) { X if (PipeCmd) (void) pclose(in); X else (void) fclose(in); X } X if (!dont) { X output_page(); X } X } X if (ncols > 0) { X output_page(); X } X} X header(ch, s) X char *ch, *s; X{ X switch (*ch) { X case 'l': left_top = s; break; X case 'c': centre_top = s; break; X case 'r': right_top = s; break; X case 's': X if (*++ch) { X top_space = atoi(ch); X return 0; X } else { X top_space = atoi(s); X return 1; X } X case '\0': return 0; X X default: X fprintf(stderr, "%s: -H not followed by %c, not l, c, r or s\n", X progname, *ch); X exit(1); X } X return 1; X} X footer(ch, s) X char *ch, *s; X{ X switch (*ch) { X case 'l': left_bot = s; break; X case 'c': centre_bot = s; break; X case 'r': right_bot = s; break; X case 's': X if (*++ch) { X bot_space = atoi(ch); X return 0; X } else { X bot_space = atoi(s); X return 1; X } X case '\0': return 0; X X default: X fprintf(stderr, "%s: -F not followed by %c, not l, c, r or s\n", X progname, *ch); X exit(1); X } X return 1; X} X readfrom(f) X FILE *f; X{ X eof = 0; X X /* TODO: read_col should return EOF on EOF, & eliminate "eof" */ X while(!eof) { X read_col(f, ncols++); X if (width_so_far() > width || form && feed || ncols >= maxcols) { X output_page(); X } X } X} X read_col(f, n) X FILE *f; X int n; X{ X int line; X char **this = cols[n] = (char **) Malloc(length * sizeof(char *)); X X if (cols[n] == (char **) 0) { X fprintf(stderr, "%s: no memory for colmuns in \"%s\"\n", X progname, ThisName); X exit(1); X } X X this_col = n; X feed = 0; X col_width[n] = 0; X X first_lines[n].File = 0L; X first_lines[n].Overall = 0L; X X for (line = 0; line < length; line++) { X if ((this[line] = getline(f)) != (char *) 0) { X if (!first_lines[n].File) { /* read the first line... */ X first_lines[n].File = DocLine; X first_lines[n].Overall = FileLine; X if ((names[n] = Malloc(NameLen + 1)) == (char *) 0) { X fprintf(stderr, "%s: running out of memory\n", progname); X /* but try to struggle on */ X } else { X (void) strcpy(names[n], ThisName); X } X } X } X } X X last_lines[n].File = DocLine; X last_lines[n].Overall = FileLine; X} X X#define addch(buf, b, c) \ X ((&(*buf)[bufsiz] == *b) ? AddCh(buf, b, c) : (*(*(b))++ = (c))) X AddCh(buf, b, c) X char **buf; X char **b; X int c; X{ X extern char *realloc(); X X if (&(*buf)[bufsiz] == *b) { X (*buf) = Realloc((*buf), bufsiz + width); X *b = &(*buf)[bufsiz]; X bufsiz += width; X } X *(*b)++ = c; X} X char * getline(f) X FILE *f; X{ X int w = 0, m = 0; X int *cw = &col_width[this_col]; X int c; X char *r, *b; X char *RealStart; X X if (eof || feof(f)) { X eof = 1; X return NULL; X } X if (feed) { X return NULL; X } else { X if ((RealStart = b = r = Malloc(width)) == (char *) 0) { X fprintf(stderr, "%s: out of memory reading \"%s\"\n", X progname, ThisName); X exit(1); X } X X if (NumberLines) { X (void) sprintf(r, "%*d", NumberWidth, X (NumberLines == 1) ? DocLine + 1 : FileLine + 1); X while (*b) { X b++; X } X addch(&r, &b, ' '); X RealStart = b; X } X while ((c = getc(f)) != '\n' && c != '\f' && c != EOF) { X switch (c) { X case '\r': X if (m < w) m = w; X w = 0; X addch(&r, &b, c); X break; X case '\b': X if (m < w) m = w; X w--; X addch(&r, &b, c); X break; X case '\t': X do { X ++w; X addch(&r, &b, ' '); X } while (w % tabsize); X break; X X /* special case: don't count ESCc as a character, in case X * there are printer escape sequences like START BOLD. X */ X X case '\033': X if (m < w) m = w; X addch(&r, &b, c); X --w; X break; X default: X addch(&r, &b, c); X if (isprint(c)) { X ++w; X } X break; X } X } X X addch(&r, &b, EOL); X X if (c == EOF) { X eof = 1; X } else { X feed |= (c == '\f'); X } X X w = esclength(r); X if (w > width) w = width; X } X X if (*cw < w) *cw = w; X X if (eof && r - 1 <= RealStart) { X (void) free(r); X return (char *) 0; X } else { X DocLine++; X FileLine++; X return r; X } X} X width_so_far() X{ X register int i, sum = 0; X X widest = 0; X for (i = 0; i < ncols; i++) { X int cwi = col_width[i]; X X sum += cwi; X if (cwi > widest) widest = cwi; X } X sum = (equal ? widest * ncols : sum) + (ncols - justify - 1) * sep; X return(sum); X} X int LastColOnPage; X output_page() X{ X int used = 0, n = 0, new, real_sep, extra, line, col; X X ++DocPage; X ++FilePage; X X if (ncols <= 0) { X return; X } X X --ncols; X if (width_so_far() > width) { /* NOTE: recomputes "widest" */ X fprintf(stderr, "%s: internal error calculating width", progname); X exit(1); X } X ++ncols; X X /* work out the number of columns to print, and also the X * umber of used columns (so as to be able to deduce the separation) X */ X for (n = 0; n < ncols; n++) { /* n is number of columns to print */ X if ((new = used + (equal ? widest : col_width[n]) + sep) > X width + sep || equal && col_width[n] > widest) { X break; X } else { X used = new; X } X } X X if (n > 1) { X real_sep = sep + (width + sep - used) / (n - justify), X extra = (width + sep - used) % (n - justify); X } X X LastColOnPage = n; /* hack hack */ X X if (headers) { X int ltw = TW(left_top); X int ctw = TW(centre_top); X int rtw = TW(right_top); X X space((top_space - 1)/ 2, '\n'); X if (top_space) { X if (left_top && *left_top) (void) Title(left_top, stdout); X space((width - ctw) / 2 - ltw, ' '); X if (centre_top && *centre_top) (void) Title(centre_top, stdout); X space((width - ctw) / 2 - rtw, ' '); X if (right_top && *right_top) (void) Title(right_top, stdout); X } X space(top_space / 2 + 1, '\n'); X } X X for (line = 0; line < length; line++) { X for (col = 0; col < n; col++) { 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 } X if (col + 1 < n) { X space(real_sep + (col < extra ? 1 : 0), ' '); X } else { X putchar('\n'); X } X } X } X X for (col = 0; col < n; col++) { X if (cols[col]) != (char **) 0) { X /*NOSTRICT*/ (void) free((char *) cols[col]); X cols[col] = (char **) 0; X } X if (names[col]) { X (void) free(names[col]); X names[col] = (char *) 0; X } X } X for(col = n; col < ncols; col++) { X cols[col-n] = cols[col]; X col_width[col-n] = col_width[col]; X first_lines[col-n].File = first_lines[col].File; X first_lines[col-n].Overall = first_lines[col].Overall; X last_lines[col-n].File = last_lines[col].File; X last_lines[col-n].Overall = last_lines[col].Overall; X if (names[col-n] != (char *) 0) { X /* "not reached", because already freed above... */ X (void) free(names[col-n]); X } X names[col-n] = names[n]; X } X ncols -= n; X X /* feet */ X if (headers) { /* should be "if footers"? */ X int ctw = TW(centre_bot); X int ltw = TW(left_bot); X int rtw = TW(right_bot); X X space(bot_space / 2, '\n'); X if (bot_space) { X if (left_bot && *left_bot) (void) Title(left_bot, stdout); X space((width - ctw) / 2 - ltw, ' '); X if (centre_bot && *centre_bot) (void) Title(centre_bot, stdout); X space((width - ctw) / 2 - rtw, ' '); X if (right_bot && *right_bot) (void) Title(right_bot, stdout); X } X space((bot_space + 1) / 2, '\n'); X } X} X X/* Maybe should be able to use tabs? */ space(n, c) X{ X register int i; X X for (i = 0; i < n; i++) X putchar(c); X} X putline(text, Width, pad) X char *text; X int Width, pad; X{ X register char *p; X int col = 0; X X for (p = text; *p; p++) { X switch (*p) { X case '\033': X putchar(*p); X if (*++p) putchar(*p); X else --p; X break; X X case '\b': X if (col > 0) { X if (col <= Width) { X putchar(*p); X } X --col; X } X break; X case '\r': X col = col > Width ? Width : col; X /* why not putchar (\r) here? -- Lee */ X while (col > 0) { X --col; X putchar('\b'); X } X break; X default: X /* only allow control characters beyond the margin */ X if (col >= 0 && col < Width || !isprint(*p)) { X putchar(*p); X } X if (isprint(*p)) ++col; X break; X } X } X if (pad) { X while (col++ < Width) { X putchar(' '); X } X } X} X int Title(string, fd) X char *string; X FILE *fd; X{ X register char *p = string; X static char *buf = 0; X char *q; X X /* Print string with variable interpolations.. X * Currently available: X * %f -- filename at start of page X * %s -- filename at end of page X * %p -- page number within file X * %P -- page number within entire output X * %l -- line number within file (at start of page) X * %L -- line number within entire output (at start of page) X * %n -- line number within file (at end of page) X * %N -- line number within entire output (at end of page) X * %% - print a % sign X */ X X if (!buf) { X if ((buf = Malloc(width + 1)) == (char *) 0) { X fprintf(stderr, "%s: not enough memory for titles\n", progname); X return 0; X } X } X X for (q = buf; p && *p; p++) { X if (*p == '%') { X switch (*++p) { X case '\0': /* err on the side of silent robustness... */ X --p; /* so we fail the loop test...*/ X /* fall through */ X case '%': X *q++ = '%'; X break; X case 'l': X (void) sprintf(q, "%ld", first_lines[0].File); X while(*q) q++; X break; X case 'L': X (void) sprintf(q, "%ld", first_lines[0].Overall); X while(*q) q++; X break; X case 'n': X (void) sprintf(q, "%ld", last_lines[LastColOnPage-1].File); X while(*q) q++; X break; X case 'N': X (void) sprintf(q, "%ld", last_lines[LastColOnPage-1].Overall); X while(*q) q++; X break; X case 'P': X (void) sprintf(q, "%d", FilePage); X while(*q) q++; X break; X case 'p': X (void) sprintf(q, "%d", DocPage); X while(*q) q++; X break; X case 's': X if (names[0]) { X (void) strcpy(q, names[0]); X while(*q) q++; X } X break; X case 'e': X if (names[LastColOnPage - 1]) { X (void) sprintf(q, names[LastColOnPage - 1]); X while(*q) q++; X } X break; X default: X fprintf(stderr, "%s: warning: unknown escape %%%c in title\n", X progname, *p); X *p = '?'; /* for next time... */ X /* fall through: */ X case '?': /* %? -- silently swallowed */ X break; X } X } else { X *q++ = (*p); X } X } X *q = '\0'; X X if (q == buf) return 0; X X if (fd != (FILE *) 0) { X (void) fputs(buf, fd); X } X X return esclength(buf); X} X X/* esclength -- the width of a string when printed, taking into account X * any embedded escape sequences or overprinting. X */ int esclength(string) X char *string; X{ X register char *p; X register int w = 0; X int m = 0; /* max -- widest of several overprintings */ X X if (!string || !*string) return 0; /* paranoia... */ X X for (p = string; *p; p++) { X switch (*p) { X case '\r': X if (m < w) m = w; X w = 0; X break; X case '\b': X if (m < w) m = w; X if (p > string) w--; X break; X case '\t': X do { X ++w; X } while (w % tabsize); X break; X X /* special case: don't count ESCc as a character, in case X * there are printer escape sequences like START BOLD. X */ X X case '\033': X if (m < w) m = w; X if (*(p + 1)) --w; /* there's another character */ X break; X default: X if (isprint(*p)) { X ++w; X } X break; X } X } X return (m >= w) ? m : w; X} X X/* $Log: pfm.c,v $ X * Revision 1.6 89/12/14 16:05:25 lee X * Fixed a minor bug in -p handling involving a core... er... dump. X * X * Revision 1.5 89/12/11 14:05:30 lee X * oops - previous revision calculated width of tabs incorrectly! X * X * Revision 1.4 89/12/10 17:32:12 lee X * Fixed some trickly little buglets... X * Now prints the correct filename! Still gets the line number out by X * one sometimes, though. X * A little faster now, too. X * X * Revision 1.2 89/12/03 20:35:25 lee X * Re-indented so I could understand it more easily, then made addch() a X * macro and removed the calls to strcpy() in getline() to speed things up X * after profiling... X * X * Revision 1.1 89/12/03 14:43:13 lee X * Initial revision X * X * X */ END_OF_FILE if test 20126 -ne `wc -c <'pfm.c'`; then echo shar: \"'pfm.c'\" unpacked with wrong size! fi # end of 'pfm.c' fi echo shar: End of shell archive. exit 0