[comp.sources.amiga] pr--a file printer

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