ahh@j.cc.purdue.edu (Brent L. Woods) (02/27/88)
Program Name: pr
Submitted By: Jim Levie REMTECH Inc Huntsville, Al
Summary: A file printer program like the UNIX program of the same name.
Poster Boy: Brent Woods (ahh@j.cc.purdue.edu)
Tested.
NOTES: We were able to test the epson version (worked fine, too), but,
not having access to a laserjet printer, we weren't able to test
that one. Other than that, no problems at all.
Brent Woods, Co-Moderator, comp.{sources,binaries}.amiga
USENET: ...!j.cc.purdue.edu!ahh ARPANET: ahh@j.cc.purdue.edu
BITNET: PODUM@PURCCVM PHONE: +1 (317) 743-8421
USNAIL: 320 Brown St., #406 / West Lafayette, IN 47906
================================================================
# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# author
# README.pr
# makefile
# getopt.c
# pr.epson.c
# pr.laserjet.c
# This archive created: Sat Feb 27 01:10:30 1988
# By: Brent L. Woods (Co-Moderators Unlimited.)
cat << \SHAR_EOF > author
Seeing the requests for 2-up laser printed docs made me think of
posting this to the archive. It's a Unix like 'pr' utility for the
Amiga that can, amoung other things, do 2-up printing on a Laserjet.
I have provided Laserjet and Epson variants of 'pr', though the Epson
variety cannot do 2-up printing in landscape mode.
=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Jim Levie REMTECH Inc Huntsville, Al
The opinions expressed above are just that.
Ph. (205) 536-8581 email: uunet!ingr!b11!jim
SHAR_EOF
cat << \SHAR_EOF > README.pr
This is my attempt at a Unix like pr utility. Actually I wrote this
originally for a Xerox 4045 printer on a Unix system 'cause I wanted to
print 2-up in landscape mode and pr didn't know how to do that automatically.
Since then I improved the code and made variants for Epson and Laserjet
printers, and modified the code to work on the Amiga.
The Laserjet variant automagically shifts to landscape mode for multi-column
output, for obvious reasons the Epson variety doesn't. Also since I have
only narrow carriage printers the margins built in to the code don't use more
than 8.5in of with, it's easy to change.
The utility can be built with Manx C V3.4b, I have the printer codes hard
coded into the source so that I can use the same basic code on several
machines. Since most people only have one printer the makefile just makes
'pr' from the source specified ('make pr-epson' or 'make pr-lj').
This code is freely redistributible, I only ask that you not remove the
author credit.
MANIFEST:
makefile - makefile to build either variant
pr.epson.c - source for Epson variant
pr.lasetjet.c - source for Laserjet variant
getopt.c - source for getopt
pr-epson - executable Epson variant
pr-lj - executable Laserjet variant
=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
Jim Levie REMTECH Inc Huntsville, Al
The opinions expressed above are just that.
Ph. (205) 536-8581 email: uunet!ingr!b11!jim
SHAR_EOF
cat << \SHAR_EOF > makefile
#
# Makefile for pr using Manx C
#
pr-epson: pr.epson.o getopt.o
ln -o pr pr.epson.o getopt.o -lc
pr-lj: pr.lj.o getopt.o
ln -o pr pr.lj.o getopt.o -lc
pr.epson.o: pr.epson.c
cc pr.epson.c
pr.lj.o: pr.laserjet.c
cc -o pr.lj.o pr.laserjet.c
getopt.o: getopt.c
cc getopt.c
clean:
delete #?.o
delete pr
install:
copy pr sys:utilities
SHAR_EOF
cat << \SHAR_EOF > getopt.c
/*
* GETOPT.C
*
* SYNOPSIS
* int getopt(argc, argv, optstring)
* int args;
* char **argv;
* char *optstring;
*
* extern char *optarg;
* extern int optind, opterr;
*
* DESCRIPTION
* getopt returns the next option letter in argv that matches a
* letter in optstring. optstring is a string of recognized
* option letters; if a letter is followed by a colon, the
* option is expected to have an argument that may or may not
* be separated from it by white space. optarg is set to point
* to the start of the option argument on return from getopt.
*
* getopt places in optind the argv index of the next argument
* to be processed. Because optind is external, it is normally
* initialized to zero automatically before the first call to
* getopt.
*
* When all options have been processed (i.e., up to the first
* non-option argument), getopt returns EOF. The special option
* -- may be used to delimit the end of the options; EOF will
* be returned, and -- will be skipped.
*
* DIAGNOSTICS
* getopt prints an error message on stderr and returns a ques-
* tion mark (?) when it encounters an option letter not
* included in optstring. This error message may be disabled
* by setting opterr to zero.
*
*/
/*LINTLIBRARY*/
#define NULL 0
#define EOF (-1)
#define ERR(s, c) if(opterr){\
extern int strlen(), write();\
char errbuf[2];\
errbuf[0] = c; errbuf[1] = '\n';\
(void) write(2, argv[0], (unsigned)strlen(argv[0]));\
(void) write(2, s, (unsigned)strlen(s));\
(void) write(2, errbuf, 2);}
extern int strcmp();
extern char *strchr();
int opterr=1;
int optind=1;
int optopt;
char *optarg;
int getopt(argc, argv, opts)
int argc;
char **argv, *opts;
{
static int sp = 1;
register int c;
register char *cp;
if(sp==1)
{
if(optind>=argc || argv[optind][0]!='-' || argv[optind][1]=='\0')
{
return(EOF);
}
else if(strcmp(argv[optind], "--")==NULL)
{
optind++;
return(EOF);
}
}
optopt=c=argv[optind][sp];
if(c==':' || (cp=strchr(opts, c))==NULL)
{
ERR(": illegal option -- ", c);
if(argv[optind][++sp]=='\0')
{
optind++;
sp=1;
}
return('?');
}
if(*++cp==':')
{
if(argv[optind][sp+1]!='\0')
optarg=&argv[optind++][sp+1];
else if(++optind>=argc)
{
ERR(": option requires an argument -- ", c);
sp=1;
return('?');
}
else
optarg=argv[optind++];
sp=1;
}
else
{
if(argv[optind][++sp]=='\0')
{
sp=1;
optind++;
}
optarg=NULL;
}
return(c);
}
SHAR_EOF
cat << \SHAR_EOF > pr.epson.c
/*
* PR.LASERJET.C - HP Laserjet Series II printer driver
*
* pr - prepare file(s) for printing, perhaps in multiple columns
*
* SYNOPSIS:
* pr [-n] [+n] [-h string] [-wn] [-ln] [-t] [file] ...
*
* DESCRIPTION:
* pr prepares one or more files for printing. By default the output is
* separated into pages headed by the date, the name of the file, and page
* number. pr processes standard input if no input file is specified, and
* sends output to standard out. Formfeed characters in the input cause
* page breaks as expected and tabs are expanded.
*
* OPTIONS:
* -n Produce n-column output. Column width is calculated as
* (page width-(n-1)*3)/n. Columns are separated by 3 spaces.
*
* +n Begin printing at page n.
*
* -h string Use string instead of the file name in the page header.
*
* -wn Use n for the page width rather than the defaults. Pr
* normally uses 80 for single column output and 160 for
* multicolumn output.
*
* -ln Take the length of the page to be n lines rather than the
* default of 60.
*
* -t Do not print the header at the top of the page.
*
* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*
* This utility writes directly to the printer connected to the par: device.
* It does not use the prt: device or codes. I did it this way to be able
* to use the same basic code on the Amiga and Unix. To use on Unix the
* freopen of stdout has to be removed.
*
* Author: James K. Levie III
*
* Version 1.0 05-Oct-1987 - Initial version
* Version 1.1 17-Oct-1987 - Fixed bug that failed to close file just printed.
* Version 1.2 13-Nov-1987 - By popular demand changed the separation between
* columns from 5 to 3.
*/
#include <stdio.h>
extern char *fgets();
extern char *malloc();
extern int errno;
extern long time();
extern char *ctime();
extern char *optarg;
extern int optind, opterr;
/*
* Local definitions
*/
#define MAX_WIDTH 124
#define MAX_LENGTH 66
#define DEF_WIDTH 86
#define DEF_LENGTH 60
#define WIDTH_80 94
#define TRUE 1
#define FALSE 0
/*
* Allocate storage for global variables
*/
char loctim[27]; /* buffer to hold current time */
char *title=NULL; /* title used in page header */
int length; /* length of page in lines */
int width; /* width of column in characters */
int columns=1; /* number of columns on a page */
int header=3; /* flag and size of headers */
int page_start=1; /* starting page number */
page_width; /* printable page width */
page_length; /* printable page length */
char *pagebuf; /* ptr to page buffer (malloc'd) */
char *pageptr[MAX_LENGTH*2]; /* ptr's to lines in buffer */
int cur_page; /* current page number */
int cur_line; /* current line on page */
int cur_char; /* current character in current line */
/*
* Printer specific stuff. This really should be done in a printer
* independent manner, but can't think of one that will work on all systems
*
* Codes for HP Laserjet Series II
*/
char Reset[]="\033@";
char Margin_80[]="\033l\010\033Q\136";
char Margin_132[]="\033l\014\033Q\210";
char *Margins;
char Font_80[]="\033!\001";
char Font_132[]="\033!\004";
char *Font;
char Bold[]="\033E";
char unBold[]="\033F";
FILE *in;
FILE *sfp, *freopen(); /* a kludge 'cause no real pipes in */
/* Amiga shell, should fix that */
^L
/*
* The main for pr. This module examines the command line for options
* and files and dispatches the printing module.
*/
main(argc, argv)
int argc;
char *argv[];
{
char *cp, *ct, **myargv, c;
register int i;
int myargc, look_again, looking, user_width=0, user_length=0;
long tim;
/*
* Some setup stuff, get the system time and
* format it the way we want to print it.
*/
tim=time(NULL); /* get system time */
cp=ctime(&tim); /* convert to ascii time */
ct=loctim;
for(i=8; i<11; i++) *ct++=cp[i]; /* load day */
for(i=4; i<8; i++) *ct++=cp[i]; /* load month */
for(i=20; i<24; i++) *ct++=cp[i]; /* load year */
*ct++=' ';
for(i=10; i<16; i++) *ct++=cp[i]; /* and finally time */
*ct='\0';
/*
* Begin the main loop. Each time we will need to check for options
* since we allow options to be reset between files. To do this we
* will have to diddle the arguments and internal variables of getopt().
* Accordingly this program should always use the version of getopt()
* supplied, rather than the library version.
*/
opterr=0; /* disable error messages from getopt */
myargv=argv; /* we'll need to be able to ... */
myargc=argc; /* ... diddle argc and argv to getopt() */
/*
* Until we get real pipes on the Amiga I think I'll close sdtout and
* write directly to the par: device.
*/
if((sfp=freopen("par:", "w", stdout))==NULL)
{
fprintf("Can't reopen stdout\n");
exit(20);
}
do
{
looking=TRUE;
while(looking)
{
c=getopt(myargc, myargv, "12h:w:l:t");
switch(c)
{
case '1':
columns=1;
break;
case '2':
columns=2;
break;
case 'h':
title=optarg;
break;
case 'w':
user_width=atoi(optarg);
if(user_width<1 || user_width>MAX_WIDTH)
{
user_width=DEF_WIDTH;
fprintf(stderr, "%s: error in width, reset to %d", argv[0], DEF_WIDTH);
}
break;
case 'l':
user_length=atoi(optarg);
if(user_length<1)
{
user_length=DEF_LENGTH;
fprintf(stderr, "%s: error in length, reset to %d", argv[0], DEF_LENGTH);
}
break;
case 't':
header=0;
break;
case EOF:
if(optind<myargc && myargv[optind][0]=='+')
{
if(myargv[optind][1]=='\0')
{
page_start=atoi(myargv[optind+1]);
optind+=2;
}
else
{
page_start=atoi(&myargv[optind][1]);
optind++;
}
/*
* Now see if there might be more options on the command
* line following the '+n' option. We are going to
* going to diddle the getopt stuff to get it to keep on
* searching. Since we will not have a program name in
* argv[0] we want to set optind to 0 rather than 1.
*/
myargc-=optind;
myargv= &myargv[optind];
optind=0;
}
else
looking=FALSE;
} /* end of switch(c) */
} /* end of while(looking) */
/*
* Now we should either have a file or stdin for input. If it is
* stdin we don't want to look again.
*/
if(user_width)
page_width=user_width;
else
page_width=(columns>1)? MAX_WIDTH:DEF_WIDTH;
width=(page_width-(columns-1)*3)/columns;
if(user_length)
page_length=user_length;
else
page_length=DEF_LENGTH;
length=(page_length-header)*columns+header;
Margins=(page_width>WIDTH_80)? Margin_132:Margin_80;
Font=(page_width>WIDTH_80)? Font_132:Font_80;
look_again=FALSE; /* assume we won't look anymore */
if(optind<myargc)
{
while(optind<myargc && myargv[optind][0]!='-'
&& myargv[optind][0]!= '+')
{
if(title==NULL) title=myargv[optind];
if((in=fopen(myargv[optind], "r"))==NULL)
printf("%s: Failed to open %s\n", argv[0], myargv[optind]);
else
{
prt_file();
fclose(in);
}
title=NULL;
optind++;
}
if(optind<myargc) look_again=TRUE;/* we can look again, not stdin */
}
else
{
in=stdin; /* no file, use stdin */
prt_file();
}
}while(look_again); /* end of do ... while(look_again) */
}
prt_file()
{
char *bp, *rp, buffer[512];
int i, ii, nchars;
/*
* Check the size of the page buffer and adjust if necessary. The
* number of bytes for a line must allow for the null byte.
*/
if(pagebuf==NULL)
{
if((pagebuf=malloc((page_width+1)*page_length))==NULL)
{
fprintf(stderr, "Failed to allocate page buffer\n");
exit(20);
}
}
else
{
if(sizeof(pagebuf)<((page_width+1)*page_length))
{
free(pagebuf);
if((pagebuf=malloc(page_width*page_length))==NULL)
{
fprintf(stderr, "Failed to allocate page buffer\n");
exit(20);
}
}
}
/*
* Set up the pointers to the virtual lines.
*/
for(i=0; i<page_length; i++) pageptr[i]=pagebuf+i*(page_width+1);
/*
* Set up the pointers to the second or more columns.
* If we are doing multicolumn stuff we have to allow for the
* whitespace that separates each column. Two column output is not
* a problem since the space is in the page width, three or more
* is.
*/
if(columns>1)
{
ii=page_length;
while(ii<length)
{
for(i=header; i<page_length; i++,ii++)
{
pageptr[ii]=pageptr[i]+width+(columns-2)*3;
}
}
}
/*
* Initialize some variables, remember that line and character references
* are from zero.
*/
printf("%s%s%s", Reset, Font, Margins);
cur_page=1;
cur_line=0;
cur_char=0;
/*
* Process the input stream.
*/
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
while((nchars=fread(buffer, sizeof(*buffer), 512, in))>0)
{
rp=buffer;
while(nchars>0)
{
if(cur_page<page_start)
{
switch(*rp)
{
case '\f':
cur_page++;
cur_line=0;
if(header) mak_hdr();
break;
case '\n':
cur_line++;
if(cur_line>=length)
{
cur_page++;
cur_line=0;
if(header) mak_hdr();
}
break;
default:
break;
}
}
else
{
switch(*rp)
{
case '\f':
/*
* This gets a little tricky. When we are doing
* multicolumns we only want a formfeed to eject the
* page if we are on the last column. For the other columns
* we want to fill out the rest of the column with spaces
* and start at the top of the next column.
*/
if(columns>1 && (length-cur_line)>=(page_length-header))
{
/*
* Well we weren't on the last column. Do we need to
* space fill the rest of this line?.
*/
if(cur_char!=0)
{
/*
* Yep, do it
*/
while(cur_char<width)
{
*bp++=' ';
cur_char++;
}
*bp='\0';
/*
* Okay, point to the next line.
*/
bp=pageptr[cur_line++];
*bp='\0';
cur_char=0;
}
/*
* Now we need to space fill the rest of this column
*/
i=(page_length-header)
-((cur_line-header)%(page_length-header));
while(i>0)
{
ii=(cur_line>page_length)? 3:0;
for(cur_char=0; cur_char<(width+ii); cur_char++)
*bp++=' ';
*bp='\0';
bp=pageptr[cur_line++];
*bp='\0';
cur_char=0;
i--;
}
}
else
{
dump_buffer();
cur_page++;
cur_char=0;
cur_line=0;
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
}
break;
case '\t':
ii=8-(cur_char)%8;
while(ii>0 && cur_char<width)
{
*bp++=' ';
cur_char++;
*bp='\0';
ii--;
}
break;
case '\n':
/*
* Did we just finish the last line on this physical
* page?
*/
if(cur_line>=length)
{
dump_buffer();
cur_page++;
cur_line=0;
cur_char=0;
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
}
/*
* Should we space fill the remainder of this line?
* We only need to if there are more columns to follow.
*/
if(columns>1 && cur_char<width &&
(length-cur_line)>=(page_length-header))
{
while(cur_char<width)
{
*bp++=' ';
cur_char++;
}
*bp='\0';
}
/*
* Columns 2, 3, and so on need withspace at the beginning
* that is not actually a part of the line to separate
* the columns.
*/
bp=pageptr[cur_line++];
if(columns>1 && cur_line>page_length)
for(i=0; i<3; i++) *bp++=' ';
*bp=0;
cur_char=0;
break;
default:
if(cur_char<width)
{
*bp++= *rp;
cur_char++;
*bp='\0';
}
}
} /* end of if(cur_page< ... */
rp++;
nchars--;
}
}
if((header && cur_line>3) || (!header && cur_line>0)) dump_buffer();
return(0);
}
/*
* MAK_HDR
*
* This module takes the local time, title, and page number and forms
* the header line that is printed at the top of each page. The local time
* is justified against the left margin and the title and page number is
* justified against the right margin as determined by "width". The header
* is always printed in bold type. There are two blank lines following the
* header.
*
*/
mak_hdr()
{
char *bp, tmp[MAX_WIDTH];
int i, ii, iii;
/*
* Put the header into buffer
*/
bp=pageptr[cur_line++];
strcpy(bp, loctim);
/*
* Now get the page number string into buf
*/
sprintf(tmp, "%s Page %d", title, cur_page);
ii=strlen(bp);
bp=bp+ii;
iii=strlen(tmp);
for(i=ii; i<(page_width-iii); i++) *bp++=' ';
for(i=0; i<iii; i++) *bp++=tmp[i];
*bp++='\0';
*pageptr[cur_line++]='\0';
*pageptr[cur_line++]='\0';
}
/*
* dump_buffer()
*
* This module dumps the virtual page.
*/
dump_buffer()
{
int i, ii;
/*
* Decide how much to print. We need to print all of the lines
* that were actually used, but no more. When we are doing
* multicolumn we only want to index on the left most column.
*/
ii=(cur_line>page_length)? page_length:cur_line;
for(i=0; i<ii; i++)
{
if(header && i==0)
printf("%s%s%s\r\n", Bold, pageptr[i], unBold);
else
printf("%s\r\n", pageptr[i]);
}
printf("\r\f");
fflush(stdout);
}
SHAR_EOF
cat << \SHAR_EOF > pr.laserjet.c
/*
* PR.C - prepare file(s) for printing, perhaps in multiple columns
*
* SYNOPSIS:
* pr [-n] [+n] [-h string] [-wn] [-ln] [-t] [file] ...
*
* DESCRIPTION:
* pr prepares one or more files for printing. By default the output is
* separated into pages headed by the date, the name of the file, and page
* number. pr processes standard input if no input file is specified, and
* sends output to standard out. Formfeed characters in the input cause
* page breaks as expected and tabs are expanded.
*
* OPTIONS:
* -n Produce n-column output. Column width is calculated as
* (page width-(n-1)*3)/n. Columns are separated by 3 spaces.
*
* +n Begin printing at page n.
*
* -h string Use string instead of the file name in the page header.
*
* -wn Use n for the page width rather than the defaults. Pr
* normally uses 80 for single column output and 160 for
* multicolumn output.
*
* -ln Take the length of the page to be n lines rather than the
* default of 60.
*
* -t Do not print the header at the top of the page.
* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*
* This utility writes directly to the printer connected to the par: device.
* It does not use the prt: device or codes. I did it this way to be able
* to use the same basic code on the Amiga and Unix. To use on Unix the
* freopen of stdout has to be removed.
*
* Author: James K. Levie III
*
* Version 1.0 05-Oct-1987 - Initial version
* Version 1.1 17-Oct-1987 - Fixed bug that failed to close file just printed.
* Version 1.2 13-Nov-1987 - By popular demand changed the separation between
* columns from 5 to 3.
*/
#include <stdio.h>
/*#include <prcodes.h>*/
extern char *fgets();
extern char *malloc();
extern int errno;
extern long time();
extern char *ctime();
extern char *optarg;
extern int optind, opterr;
/*
* Local definitions
*/
#define MAX_WIDTH 167
#define MAX_LENGTH 84
#define DEF_WIDTH 75
#define DEF_LENGTH 60
#define TRUE 1
#define FALSE 0
/*
* Allocate storage for global variables
*/
char loctim[27]; /* buffer to hold current time */
char *title=NULL; /* title used in page header */
int length; /* length of page in lines */
int width; /* width of column in characters */
int columns=1; /* number of columns on a page */
int header=3; /* flag and size of headers */
int page_start=1; /* starting page number */
page_width; /* printable page width */
page_length; /* printable page length */
char *pagebuf; /* ptr to page buffer (malloc'd) */
char *pageptr[MAX_LENGTH*2]; /* ptr's to lines in buffer */
int cur_page; /* current page number */
int cur_line; /* current line on page */
int cur_char; /* current character in current line */
/*
* Xerox 4045 specific stuff, this will be replaced later with termcap
* stuff.
*/
char Reset[]="\033E\033&k0G";
char Margin_80[]="\033&l0O\033&l2E\033&l61F\033&a5L\033&a83M";
char Margin_132[]="\033&l1O\033&l8D\033&l6E\033&l61F\033&a3L\033&a167M";
char *Margins;
char Font_80[]="\033(s10H";
char Font_132[]="\033(s16.66H";
char *Font;
char Bold[]="\033(s3B";
char unBold[]="\033(s0B";
FILE *in;
FILE *sfp, *freopen(); /* a kludge 'cause no real pipes in shell */
^L
/*
* The main for pr. This module examines the command line for options
* and files and dispatches the printing module.
*/
main(argc, argv)
int argc;
char *argv[];
{
char *cp, *ct, **myargv, c;
register int i;
int myargc, look_again, looking, user_width=0, user_length=0;
long tim;
/*
* Some setup stuff, get the system time and
* format it the way we want to print it.
*/
tim=time(NULL); /* get system time */
cp=ctime(&tim); /* convert to ascii time */
ct=loctim;
for(i=8; i<11; i++) *ct++=cp[i]; /* load day */
for(i=4; i<8; i++) *ct++=cp[i]; /* load month */
for(i=20; i<24; i++) *ct++=cp[i]; /* load year */
*ct++=' ';
for(i=10; i<16; i++) *ct++=cp[i]; /* and finally time */
*ct='\0';
/*
* Begin the main loop. Each time we will need to check for options
* since we allow options to be reset between files. To do this we
* will have to diddle the arguments and internal variables of getopt().
* Accordingly this program should always use the version of getopt()
* supplied, rather than the library version.
*/
opterr=0; /* disable error messages from getopt */
myargv=argv; /* we'll need to be able to ... */
myargc=argc; /* ... diddle argc and argv to getopt() */
/*
* Until we get real pipes on the Amiga I think I'll close sdtout and
* write directly to the par: device.
*/
if((sfp=freopen("par:", "w", stdout))==NULL)
{
fprintf("Can't reopen stdout\n");
exit(20);
}
do
{
looking=TRUE;
while(looking)
{
c=getopt(myargc, myargv, "12h:w:l:t");
switch(c)
{
case '1':
columns=1;
break;
case '2':
columns=2;
break;
case 'h':
title=optarg;
break;
case 'w':
user_width=atoi(optarg);
if(user_width<1 || user_width>MAX_WIDTH)
{
user_width=DEF_WIDTH;
fprintf(stderr, "%s: error in width, reset to %d", argv[0], DEF_WIDTH);
}
break;
case 'l':
user_length=atoi(optarg);
if(user_length<1)
{
user_length=DEF_LENGTH;
fprintf(stderr, "%s: error in length, reset to %d", argv[0], DEF_LENGTH);
}
break;
case 't':
header=0;
break;
case EOF:
if(optind<myargc && myargv[optind][0]=='+')
{
if(myargv[optind][1]=='\0')
{
page_start=atoi(myargv[optind+1]);
optind+=2;
}
else
{
page_start=atoi(&myargv[optind][1]);
optind++;
}
/*
* Now see if there might be more options on the command
* line following the '+n' option. We are going to
* going to diddle the getopt stuff to get it to keep on
* searching. Since we will not have a program name in
* argv[0] we want to set optind to 0 rather than 1.
*/
myargc-=optind;
myargv= &myargv[optind];
optind=0;
}
else
looking=FALSE;
} /* end of switch(c) */
} /* end of while(looking) */
/*
* Now we should either have a file or stdin for input. If it is
* stdin we don't want to look again.
*/
if(user_width)
page_width=user_width;
else
page_width=(columns>1)? MAX_WIDTH:DEF_WIDTH;
width=(page_width-(columns-1)*3)/columns;
if(user_length)
page_length=user_length;
else
page_length=DEF_LENGTH;
length=(page_length-header)*columns+header;
Margins=(page_width>80)? Margin_132:Margin_80;
Font=(page_width>80)? Font_132:Font_80;
look_again=FALSE; /* assume we won't look anymore */
if(optind<myargc)
{
while(optind<myargc && myargv[optind][0]!='-'
&& myargv[optind][0]!= '+')
{
if(title==NULL) title=myargv[optind];
if((in=fopen(myargv[optind], "r"))==NULL)
printf("%s: Failed to open %s\n", argv[0], myargv[optind]);
else
{
prt_file();
fclose(in);
}
title=NULL;
optind++;
}
if(optind<myargc) look_again=TRUE;/* we can look again, not stdin */
}
else
{
in=stdin; /* no file, use stdin */
prt_file();
}
}while(look_again); /* end of do ... while(look_again) */
}
prt_file()
{
char *bp, *rp, buffer[512];
int i, ii, nchars;
/*
* Check the size of the page buffer and adjust if necessary. The
* number of bytes for a line must allow for the null byte.
*/
if(pagebuf==NULL)
{
if((pagebuf=malloc((page_width+1)*page_length))==NULL)
{
fprintf(stderr, "Failed to allocate page buffer\n");
exit(20);
}
}
else
{
if(sizeof(pagebuf)<((page_width+1)*page_length))
{
free(pagebuf);
if((pagebuf=malloc(page_width*page_length))==NULL)
{
fprintf(stderr, "Failed to allocate page buffer\n");
exit(20);
}
}
}
/*
* Set up the pointers to the virtual lines.
*/
for(i=0; i<page_length; i++) pageptr[i]=pagebuf+i*(page_width+1);
/*
* Set up the pointers to the second or more columns.
* If we are doing multicolumn stuff we have to allow for the
* whitespace that separates each column. Two column output is not
* a problem since the space is in the page width, three or more
* is.
*/
if(columns>1)
{
ii=page_length;
while(ii<length)
{
for(i=header; i<page_length; i++,ii++)
{
pageptr[ii]=pageptr[i]+width+(columns-2)*3;
}
}
}
/*
* Initialize some variables, remember that line and character references
* are from zero.
*/
printf("%s%s%s", Reset, Margins, Font);
cur_page=1;
cur_line=0;
cur_char=0;
/*
* Process the input stream.
*/
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
while((nchars=fread(buffer, sizeof(*buffer), 512, in))>0)
{
rp=buffer;
while(nchars>0)
{
if(cur_page<page_start)
{
switch(*rp)
{
case '\f':
cur_page++;
cur_line=0;
if(header) mak_hdr();
break;
case '\n':
cur_line++;
if(cur_line>=length)
{
cur_page++;
cur_line=0;
if(header) mak_hdr();
}
break;
default:
break;
}
}
else
{
switch(*rp)
{
case '\f':
/*
* This gets a little tricky. When we are doing
* multicolumns we only want a formfeed to eject the
* page if we are on the last column. For the other columns
* we want to fill out the rest of the column with spaces
* and start at the top of the next column.
*/
if(columns>1 && (length-cur_line)>=(page_length-header))
{
/*
* Well we weren't on the last column. Do we need to
* space fill the rest of this line?.
*/
if(cur_char!=0)
{
/*
* Yep, do it
*/
while(cur_char<width)
{
*bp++=' ';
cur_char++;
}
*bp='\0';
/*
* Okay, point to the next line.
*/
bp=pageptr[cur_line++];
*bp='\0';
cur_char=0;
}
/*
* Now we need to space fill the rest of this column
*/
i=(page_length-header)
-((cur_line-header)%(page_length-header));
while(i>0)
{
ii=(cur_line>page_length)? 3:0;
for(cur_char=0; cur_char<(width+ii); cur_char++)
*bp++=' ';
*bp='\0';
bp=pageptr[cur_line++];
*bp='\0';
cur_char=0;
i--;
}
}
else
{
dump_buffer();
cur_page++;
cur_char=0;
cur_line=0;
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
}
break;
case '\t':
ii=8-(cur_char)%8;
while(ii>0 && cur_char<width)
{
*bp++=' ';
cur_char++;
*bp='\0';
ii--;
}
break;
case '\n':
/*
* Did we just finish the last line on this physical
* page?
*/
if(cur_line>=length)
{
dump_buffer();
cur_page++;
cur_line=0;
cur_char=0;
if(header) mak_hdr();
bp=pageptr[cur_line++];
*bp='\0';
}
/*
* Should we space fill the remainder of this line?
* We only need to if there are more columns to follow.
*/
if(columns>1 && cur_char<width &&
(length-cur_line)>=(page_length-header))
{
while(cur_char<width)
{
*bp++=' ';
cur_char++;
}
*bp='\0';
}
/*
* Columns 2, 3, and so on need withspace at the beginning
* that is not actually a part of the line to separate
* the columns.
*/
bp=pageptr[cur_line++];
if(columns>1 && cur_line>page_length)
for(i=0; i<3; i++) *bp++=' ';
*bp=0;
cur_char=0;
break;
default:
if(cur_char<width)
{
*bp++= *rp;
cur_char++;
*bp='\0';
}
}
} /* end of if(cur_page< ... */
rp++;
nchars--;
}
}
if((header && cur_line>3) || (!header && cur_line>0)) dump_buffer();
return(0);
}
/*
* MAK_HDR
*
* This module takes the local time, title, and page number and forms
* the header line that is printed at the top of each page. The local time
* is justified against the left margin and the title and page number is
* justified against the right margin as determined by "width". The header
* is always printed in bold type. There are two blank lines following the
* header.
*
*/
mak_hdr()
{
char *bp, tmp[MAX_WIDTH];
int i, ii, iii;
/*
* Put the header into buffer
*/
bp=pageptr[cur_line++];
strcpy(bp, loctim);
/*
* Now get the page number string into buf
*/
sprintf(tmp, "%s Page %d", title, cur_page);
ii=strlen(bp);
bp=bp+ii;
iii=strlen(tmp);
for(i=ii; i<(page_width-iii); i++) *bp++=' ';
for(i=0; i<iii; i++) *bp++=tmp[i];
*bp++='\0';
*pageptr[cur_line++]='\0';
*pageptr[cur_line++]='\0';
}
/*
* dump_buffer()
*
* This module dumps the virtual page.
*/
dump_buffer()
{
int i, ii;
/*
* Decide how much to print. We need to print all of the lines
* that were actually used, but no more. When we are doing
* multicolumn we only want to index on the left most column.
*/
ii=(cur_line>page_length)? page_length:cur_line;
for(i=0; i<ii; i++)
{
if(header && i==0)
printf("%s%s%s\r\n", Bold, pageptr[i], unBold);
else
printf("%s\r\n", pageptr[i]);
}
printf("\r\f");
fflush(stdout);
}
SHAR_EOF
# End of shell archive
exit 0