#define PROGNAME  "Ptype"
#define VERMSG    PROGNAME " V2.3: "

/*************************************************************************
* Ptype.c - type to the printer        Author:  Bob Alston
*            date: Dec 26, 1991                 805 Chapel Street
*                                               Stratford, CT 06497
*
* Use this program freely.  No warranty is intended or implied.
*
* Written for AMIGA (also works with MSDOS, and UNIX - see below) for:
*
* 1.  printing files
* 2.  printing C programs with page control
* 3.  used with a pipe for printing  (eg. on AMIGA using C Shell)
* 4.  fixing linefeeds in files from other systems (such as an IBM PC)
* 5.  converting tabs in files to spaces
* 6.  printing a file with ascii text, ESC chars, and some binary data
* 7.  printing postcards (file must be correct # of lines long)
* 8.  print to HP Laser printer at 12 chars per inch
* 9.  printing many files using wildcards (eg. AMIGA patterns)
*
* Examples of above:
*
* 1.  ptype -ns letters/Joe
* 2.  ptype prog.c
* 3.  dir | ptype -n
* 4.  ptype -L -N -t -W -omisc/prog.c misc/prog_cr.c
* 5.  ptype -T8 -n -w0 -L0 -oFile_Spaces File_Tabs
* 6.  ptype -ex File_Escapes
* 7.  ptype -n -l -c50 postcard.txt
* 8.  ptype -z -m5 -w90 -i\eE\e(s12H -r\eE prog.c
* 9.  ptype #?/source/#?.c
*
*************************************************************************/

/*************************************************************************
* conditional compile parameters
*
* Although originally written for the AMIGA (Commodore-Amiga, Inc.),
* this program has been compiled and tested on the following systems
* where these parameters are automatically defined.
*
*     AMIGA    (using Lattice, Inc. 5.02 compiler)
*     MSDOS    (using Microsoft, Inc. 6.0a compiler, older will also work)
*     unix     (on an Olivetti CP486 running SCO System V/386 UNIX 3.2
*               using cc, the UNIX system C complier)
*
*************************************************************************/

#define ARPFILE   1                    /* 1 = support ARP file requester */
                                       /*   (if AMIGA)                   */
/*eject*/
/*************************************************************************
*Format:
* Ptype -adensvxz -cN -hS -iS -lN -mN -pN,E -rS -tTABS -wCOL -oOutfile Infiles
* Ptype ?      (Note:  ? may need to be escaped on the command line)
*
*Specification (all command line parameters are optional):
*
* -a           All control chars are passed thru (see below).
* -d           Display option settings and file names.
* -e           No ESC, BS, or DEL char is output, expand to ^[, ^H, & ^?
* -f           Use ARP file requester if there are no input file names.
* -n           No line numbers at start of each line.
* -s           Suppress heading line at top of each page.
* -v           Display version number and file names (don't execute).
* -x           No extended chars are output, expand them such as \C or \^C
* -z           Laser printer, no LineFeed at end of page before FormFeed
*
* -cN          Number of copies to be printed is set to N.  Default is 1.
*
* -hSTR        Heading string "STR" is to be used instead of "infile" name.
*
* -iSTR        Initialize printer string, \xNN is hex byte value (See -r).
*
* -lN          Lines per page is set to N.  Default is 58.
*              (Note "-L0" or "-L" eliminates the formfeed caused by this
*              limit and suppresses the heading line.)
*
* -mN          Left margin is set to column N.  Default is column 1.
*              (Note, these extra spaces appended to the start of each
*              line must be included in the line width.  See option "-w".)
*
* -pN,E        Pages to print, starting with page N and ending with
*              page E.  If ",E" is omitted, the print is to the end.
*              This option will apply to all input files.  The default is
*              to print the whole file.  (May also be -pN.E)
*
* -rSTR        Reset printer string, \xNN is hex byte value (See -i).
*
* -tTABS       Tab spacing is set to TABS.  Default is 3 for the AMIGA.
*              (Note "-t0" or "-t" eliminates expansion of tabs to spaces.)
*
* -wCOL        Width of lines for output is set to COL.  Default is 92,
*              max is 255.  (Note "-w0" or "-w" is the same as "-w255".)
*
* -oOutfile    Output file name is set to "Outfile".  For stdout use "-o".
*              The default output file is the printer, except for unix
*              where it is "stdout" so output can be piped to the spooler.
*
* Infiles      Input file pathname(s).  Wild cards such as #? may be used
*              in the file or directories of the pathname (MSDOS * and ?).
*              The default with none specified is "stdin" (see "-f" option).
*
* Note:  The option parameters ("-", or "/" with MSDOS) can be in any order
*        and the letters may be lower or upper case.  The input file names
*        must be the last parameters on the command line.
*************************************************************************/

/* SPECIAL PROCESSING:
*
* The primary use of this program is with text (edited) files.  The normal
* processing (no special options) insures printable characters for AMIGA
* printers (with the possible execption of escape sequences).  It does
* the following:
*
*     character                              processing
*     ---------                              ----------
*
*     \t              (0x09)                 Expanded to spaces (-t option)
*                                               or passed unchanged
*     \n              (0x0A)                 Proper end of a line sequence
*                                               is output, "\n" or "\r\n"
*     \f              (0x0C)                 Passed unchanged and a page
*                                               heading may be output
*     \r              (0x0D)                 Deleted on input.  Not output
*                                               except as part of \n or \f
*     \b & ESC        (0x08 & 0x1B)          Passed unchanged to output
*     other ctl chars (0x00 - 0x1F, partial) Expanded such as ^C
*     printable ascii (0x20 thru 0x7E)       Passed unchanged to output
*     DEL             (0x7F)                 Passed unchanged to output
*     alt-ctl chars   (0x80 thru 0x9F)       Expanded such as \^C
*     extended chars  (0xA0 thru 0xFF)       Passed unchanged to output
*     
* The options will alter the above processing in the following order:
*
* "-a" Option (all ctl chars passed thru):
*
*     other ctl chars (0x00 - 0x1F, partial) Passed unchanged to output
*     alt-ctl chars   (0x80 thru 0x9F)       Passed unchanged to output
*
*  "-e" Option (no escape, backspace, or delete chars):
*
*     \b              (0x08)                 Expanded to ^H
*     ESC             (0x1B)                 Expanded to ^[
*     DEL             (0x7F)                 Expanded to ^?
*
*  "-x" Option (no extended chars):
*
*     alt-ctl chars   (0x80 thru 0x9F)       Expanded such as \^C
*     extended chars  (0xA0 thru 0xFE)       Expanded such as \C
*     alt-DEL         (0xFF)                 Expanded to \^?
*
* You will notice that just the "-e" and "-x" options limit output to
* printable ascii characters, linefeed, carriage return (eg. MSDOS), and
* formfeed.  This will print even a binary file to any printer.  (Remember
* the default processing for tabs is to expand them to spaces.)  However,
* the intended use for this is to print files with strange escape
* sequences without messing up the printer.
*
*/ /*eject*/
/* OTHER PROCESSING:
*
* "-n" Option:
*
*     Line numbers are added at the front of each each input source line
*     unless the no-numbers option, "-n", is specified.
*
* "-wCOL" Option:
*
*     Extra newlines ('\n') may be inserted based on the line width option
*     (see the option "-wCOL" above). Note that the page heading, which is
*     output in columns 1 thru 75, is not affected by this option.
*
*     *** Important Note ***:  If escape sequences are passed thru from
*     input to output (eg. the printer), the program cannot distinguish the
*     characters after the escape from normal printable characters. 
*     Therefore the newline may be output prematurely causing the line
*     (wrongly) to be wrapped to the next line.  The solution, in this
*     case, is to specify a larger width such as option "-w120".  Also tab
*     expansion can be wrong on lines with escape sequences or control
*     characters which are expanded.
*
* Formfeed processing - "-lN" and "-s" Options:
*
*     Because some editors (ED) do not allow typing a formfeed into the
*     text file, a '\f' is inserted in the output file, after the end of
*     the line containing the page eject sequence like the one at the
*     bottom of this page, to allow page ejects.
*
*     Also formfeeds are inserted into the output file unless the lines per
*     page option is specified as 0 (see option "-lN" above). After every
*     formfeed in the output file (and at the start of the file) a page
*     heading line is output unless option "-L0" or "-s" is specified.
*
* Default values:
*
*     The default values for the optional parameters can be changed by
*     changing the "default parameter definitions" below.  Also, the ARP
*     file requester option "-f" can be removed with a conditional compile
*     parameter (see "conditional compile parameters" above) to save the
*     space in the executable program.
*
*/ /*eject*/
/*************************************************************************
* Revision history:
*  12-12-85    ver 1.1   - Add SH option (now -s option)
*
*  03-26-86    ver 1.2   - Fix lines per page on page after eject
*                          sequence.  Eliminate blank line after line
*                          with length = max width.  Allow abort with
*                          control-C.
*
*  09-26-86    ver 1.3  -  Change end-of-file processing.  If characters
*                          are present after the last newline ("\n")
*                          but before the end of file, a newline is
*                          now appended and the line is output.  The
*                          line was previously lost.
*                       -  The documentation at the front was expanded.
*
*  11-21-87    ver 1.4  -  All options changed to be preceded by "-".
*                       -  Display of version and options is now optional.
*                       -  Allow use with a pipe for printing, when the
*                          input files are omitted (takes input from stdin
*                          but default output is the printer not to stdout).
*                       -  The date printed in the heading is the last
*                          updated date for "infile" if specified,
*                          otherwise it is the system date (for "stdin").
*                       -  The backtab, ESC, and extended chars are also
*                          passed thru.
*                       -  The new option "-a" allows all control chars
*                          to be passed thru to the output.
*                       -  The new option "-hSTR" allows a heading
*                          string to be specified, over-riding the
*                          "infile" (usefull when "infile" is omitted).
*                       -  Conditional compile statements (#if) have been
*                          added for AMIGA, MSDOS, and unix (see above).
*                       -  Format display improved.  ("Ptype ?" shows
*                          the format and option descriptions.)
*                       -  Allow multiple input files.  Output file name
*                          is now specified with the "-o" option.
*
*  03-06-88             -  (no Ver change) Recompile with Lattice 4.0
*
*  01-08-89    ver 1.5  -  Add number of copies option.
*                       -  Add starting page, number of pages option.
*                       -  Add left margin option.
*                       -  Allow program to be resident.  Note that the
*                          program is pure and can be used with the
*                          resident command in AmigaDOS 1.3.
*                       -  Option display, documentation, and
*                          initialization is modified to incorporate
*                          the above.
*                       -  Changed reading of options to insure numeric
*                          digits and provide an error message.
*                       -  A bug was fixed which caused an extra page at
*                          the end when the last page was full.
*                       -  Code was cleaned up here & there.
*                       -  (Now using Lattice 5.0)
*
*  01-16-89    ver 1.51 -  Properly handle formfeed at start of the line.
*                          Previously only '\f' at end of line were ok.
*                       -  Margin is now applied to the heading line.
*
*  05-30-89    ver 1.52 -  No source code changes.  Recompiled with
*                          Lattice C v5.02 plus the fclose() fix.
*
*  11-26-89    ver 2.0  -  Two new options added ("-e" and "-x").
*                       -  Conversion of oddball control characters was
*                          changed to expand them such as ^C. Before they
*                          would have been printed as spaces.
*                       -  The input-line to output-line processing was
*                          re-written to eliminate the input buffer,
*                          improve efficiency, and add "-e" & "-x" options.
*                       -  Null chars are passed thru with "-a" option.
*                       -  Long lines ( > 255) will now be printed wrapped
*                          but without being numbered as seperate lines.
*                       -  Documentation changed.
*                       -  A bug was fixed which caused a blank page to be
*                          printed when a page was full of lines, followed
*                          by a '\f' at the start of the next line.
*                       -  New option "-f" uses ARP file requester when no
*                          input file names are specified.  Using Iconx,
*                          this provides use from the workbench.
*                       -  The display for "-v" option now includes a one
*                          line format display as well as the version and
*                          default options.
*
*  02-04-91    ver 2.1  -  Problem fixed with extra page on laser printers
*                          when the page has the same number of lines as
*                          the number of lines per page defined in the
*                          printer.  The solution is to output \r\f
*                          instead of the \n\f sequence (\r\n\f in MSDOS). 
*                          See option "-z" which controls this.
*
*                       -  The formfeed, which is inserted because of the
*                          page eject sequence, is now inserted at the end
*                          of the line.
*
*  08-06-91    ver 2.2  -  Allow '/' in MSDOS as well as '-' for options.
*
*                       -  Add two new options, "-istring" and "-rstring"
*                          to specify initialize and reset printer strings.
*                          If specified, the -i string is sent to the printer
*                          before any other printing.  Similarly, the reset
*                          string will be sent after all other printing.
*
*                       -  Fix bug - an extra line per page was being
*                          printed on the page following a form feed
*                          at the front of a line.
*
*                       -  Allow '.' between numbers as well as ',' for the
*                          option "-pN.E" because batch file processing in
*                          MSDOS changes the comma to a blank.
*
*  12-26-91    ver 2.3  -  Allow wild cards, such as #? in the file and
*                          directory names for AMIGA and allow * and ? for
*                          MSDOS.  The names expanded from wild cards will
*                          be in low case.  (Wild card processing for unix
*                          is not provided and not needed because it is
*                          done by the shell.)
*
*                       -  No longer stops when a file is not found (can't
*                          be opened), but continues on with the other files.
*
*                       -  Display messages will now go to stdout except when
*                          the normal output is to stdout.  This allows the
*                          messages to be redirected to a file.  It should
*                          also eliminate the slow display (to stderr)
*                          noticed on a unix terminal of the help display.
*
*                       -  The help display was improved.
*
*                       -  Fix hex conversion in -i and -r option strings.
*
*  Someday (v3.0?)      -  Full workbench support:  Tool parameters and
*                          gadgets for print options.
*
*************************************************************************/
/*eject*/
/*************************************************************************
* Test procedure:
*     The following test sequence is used by the author.  Since the proper
*     outputs are not defined here, it may not be useful to anyone else.
*
* Tests which do not execute (do not process files):
*     ptype ?                       ("\?" for C Shell and UNIX)
*     ptype -v
*     ptype -nsvadexz -w80 -l40 -t5 -oB -c2 -p38,50 -m7 A
*     ptype -sv -w0 -l0 -t0 A
*     ptype -sv -w -l -t A
*     ptype -v -p.691 -m
*     ptype -v -p444 -m0
*     ptype -v -oC
*     ptype -v -o
*     ptype -v A BB CCC
*     ptype -v *.c                  (using MSDOS, Amiga C Shell, and UNIX)
*     ptype -v #?.c                 (using Amiga CLI)
*     ptype -v -m80 -w40
*
* Tests which get an error (and therefore do not execxute)
*     ptype -wx -t3,6 -p3,6t
*     ptype -q BB                   (an invalid parameter)
*     ptype -p90,33 A               (start page > end)
*     ptype CC -n                   (parameter after "infile")
*     ptype dd                      (where dd is a non existant file)
*     ptype -d -odd:A               ("cancel" on AMIGA insert volume dd)
*
* Tests using stdout
*     ptype -o -l0 -t5 FileTabs.c
*     ptype -o -l0 -t3 FileTabs.c
*     ptype -w57 -t3 -m6 -o -d -l0 FileTabs.c
*     ptype -d -o -l0      (type 3 lines, ctl-\, the AMIGA end of file)
*     ptype -d -o -l0      (type 3 lines, ctl-c, return to abort)
*     ptype -def -o        (test ARP file requester - AMIGA only)
*
* Tests to the printer
*     ptype FileTabs.c
*     ptype -n -hWorkbench FileTabs.c
*     ptype -d -c2 -p1,2 FileTabs.c Ptype.c
*     ptype -p25,25 Ptype.c
*     ptype FormFeed.c
*     dir | ptype -n -z               (using C Shell, UNIX, or MSDOS)
*     dir -s | ptype -n -w120 -hDirectory_Print
*     ptype -iInitptr. -rResetptr.\x0D\x0CEnd\x0D\x0A FileTabs.c
*
* Test of file conversions:
*     ptype -w0 -l0 -t0 -n -otest.out AllChars
*     ptype -w0 -l0 -t0 -n -a -otest.out AllChars
*     ptype -w -l -t -n -ae -otest.out AllChars
*     ptype -w -l -t -n -ax -otest.out AllChars
*     ptype -ex AllChars
*
*************************************************************************/
/*eject*/
/*************************************************************************
* include files and misc. definitions
*************************************************************************/

#if AMIGA
#include <exec/types.h>             /* predede for NULL redefinition err */
#endif

#if AMIGA && ARPFILE
#include <arp/arpfilereq.h>         /* many includes + ARPbase.h */
#endif

#include <stdio.h>                  /* standard headers */
#include <ctype.h>
#include <string.h>
#include <time.h>

#if AMIGA || MSDOS
#include <stdlib.h>
#include <dos.h>
#include "findfile.h"               /* for function extern's */
#endif

#if MSDOS
#include <sys\types.h>
#include <sys\stat.h>
#endif

#if unix
#include <sys/types.h>
#include <sys/stat.h>
#endif

typedef unsigned char UCHAR;        /* define UCHAR */

#ifndef TRUE
#define TRUE      1                 /* define TRUE & FALSE */
#define FALSE     0
#endif


/*************************************************************************
* macros
*************************************************************************/

                                    /* convert known hex digit to binary */
#define HEX2BIN(c)   ((((int)(c) < 'A') ? (int)(c) : (int)(c) + 9) & 0x0F)

/*eject*/
/*************************************************************************
* local function prototypes
*************************************************************************/
#if AMIGA && ARPFILE
int  breakfunc(void);
UCHAR getarp(void);
#endif

#if AMIGA || MSDOS
UCHAR getparms(void);
void putoptmsg(void);
UCHAR numparam(short *, short *, UCHAR *, UCHAR);
UCHAR get1char(void);
UCHAR next1char(void);
UCHAR convline(void);
UCHAR charbuf2(int);
UCHAR putline(void);
UCHAR doheading(void);
UCHAR printseq(UCHAR *, FILE *);
void initvar(void);
void inithead(void);
void putstring(UCHAR *);
void put3strings(UCHAR *, UCHAR *, UCHAR *);
void putnumstr(int);
void datetime(UCHAR *, UCHAR *, UCHAR *, UCHAR*);
void itoasc(int, int, int, UCHAR *);
#endif

#if unix
UCHAR getparms();                       /* no arg type checking */
void putoptmsg();
UCHAR numparam();
UCHAR get1char();
UCHAR next1char();
UCHAR convline();
UCHAR charbuf2();
UCHAR putline();
UCHAR doheading();
UCHAR printseq();
void initvar();
void inithead();
void putstring();
void put3strings();
void putnumstr();
void datetime();
void itoasc();
#endif

/* status values for get1char() - see variable readerr */
#define FGOOD     0                 /* good return */
#define FBAD      1                 /* bad return */
#define FEOF      2                 /* EOF return */

/*eject*/
/*************************************************************************
* default parameter definitions
*
*  Note that changing these from TRUE to FALSE, FALSE to TRUE, or changing
*  the value of a default number is all that is necessary to change the
*  functioning of the program.  However, the documentation pages at the
*  front of the program and the parameter display in function getparms()
*  would still reflect the original values.  They should be changed also.
*************************************************************************/

#define ALL_CTL_CHAR    FALSE       /* -a  all ctl chars passed thru */
#define DOPTFLAG        FALSE       /* -d  display options */
#define ESCFLAG         TRUE        /* -e  ESC, BS, & DEL pass thru */
#define ARPFLAG         FALSE       /* -f  use ARP file requester */
#define NUMFLAG         TRUE        /* -n  print line numbers */
#define SHEADFLAG       FALSE       /* -s  supress heading */
#define VERFLAG         FALSE       /* -v  display version */
#define EXTFLAG         TRUE        /* -x  Extended characters pass thru */
#define LAZFLAG         FALSE       /* -z  Printer is Laser printer */

#define COPIES          1           /* -c  number of copies to print */
#define MARGIN          1           /* -m  left margin column number */
#define SPAGE           1           /* -p  starting page number */
#define EPAGE           32767       /* -p  ending page number */

/*************************************************************************
* default parameter definitions which are system dependent
*************************************************************************/

#if AMIGA
#define LINES           58          /* -l  lines per page */
#define WIDTH           92          /* -w  output line width */
#define TABS            3           /* -t  tab spacing */
#define NAME2           "prt:"      /* -o  output file name */
#endif

#if MSDOS
#define LINES           60          /* -l  lines per page */
#define WIDTH           132         /* -w  output line width */
#define TABS            3           /* -t  tab spacing */
#define NAME2           "lpt1"      /* -o  output file name */
#endif

#if unix
#define LINES           60          /* -l  lines per page */
#define WIDTH           132         /* -w  output line width */
#define TABS            8           /* -t  tab spacing */
#define NAME2           ""          /* -o  output file name */
#endif

/*eject*/
/*************************************************************************
* default parameter variables
*************************************************************************/

UCHAR     all_ctl_char;             /* -a  all ctl chars passed thru */
UCHAR     doptflag;                 /* -d  display options flag */
UCHAR     escflag;                  /* -e  ESC, BS, & DEL pass thru */
UCHAR     arpflag;                  /* -f  use ARP file requester */
UCHAR     *istring;                 /* -i  init printer string */
UCHAR     numflag;                  /* -n  print line numbers flag */
UCHAR     *rstring;                 /* -r  reset printer string */
UCHAR     sheadflag;                /* -s  supress heading flag */
UCHAR     verflag;                  /* -v  display version flag */
UCHAR     extflag;                  /* -x  Extended characters pass thru */
UCHAR     lazflag;                  /* -z  Printer is laser printer */

short    copies;                    /* -c  number of copies to print */
short    lines;                     /* -l  lines per page */
short    margin;                    /* -m  left margin column number */
short    spage;                     /* -p  starting page number */
short    epage;                     /* -p  ending page number */
short    tabs;                      /* -t  tab spacing */
short    width;                     /* -w  output line width */

/*eject*/
/*************************************************************************
* file parameters
*************************************************************************/

#if AMIGA && ARPFILE                /* for ARP File Requester */

struct Library       *ArpBase;      /* for opening the lib */
struct FileRequester myFileReq;     /* for calling the function */
UCHAR   arp_dir  [LONG_DSIZE+1];    /* my directory string */
UCHAR   arp_file [FCHARS+1];        /* my filename string */
UCHAR   arp_hail [] = "Select File to Print or CANCEL";
#define NAMELEN   (LONG_DSIZE+1 + FCHARS) /* max len of path/file name */

#else
#define NAMELEN   256               /* max length of path/file name */
#endif

FILE *file1, *file2, *fdisp;        /* in, out file, and display pointers */
UCHAR name1[NAMELEN+1];             /* file 1 name (input file) */
UCHAR name2[NAMELEN+1];             /* file 2 name (output file) */

#if AMIGA || MSDOS
#define FOPENPARM    "rb"           /* read binary - param for fopen() */
#endif

#if unix
#define FOPENPARM    "r"            /* read - param for fopen() */
#endif

#if AMIGA                           /* for file name wild card processing */
#define FN_STR_LEN   5000           /* 5K buffer for file name stings */
#define FN_PTR_LEN   501            /* allow 500 file names */
#endif

/*************************************************************************
* page heading string
*************************************************************************/

#define LINENLEN  4                 /* line number length */
#define PAGENLEN  3                 /* page number length */

#define HEADLLEN  75                /* heading line len w/o \n or NULL */
#define HEADPNUM  (HEADLLEN-PAGENLEN) /* offset for heading page number */
#define HEADPAGE  (HEADPNUM-4)      /* offset for heading "PAGE" */
#define HEADTIME  (HEADPAGE-12)     /* offset for heading time */
#define HEADDATE  (HEADTIME-10)     /* offset for heading date */
#define HEADDAY   (HEADDATE-10)     /* offset for heading day */
#define HEADFNL   (HEADDAY-1)       /* room left for "infile" name */

UCHAR heading[HEADLLEN+3];          /* heading line */
UCHAR head_string[HEADFNL+1];       /* string to replace "infile" name */
#define PAGELIT  "PAGE"             /* page literal */

/*eject*/
/*************************************************************************
* misc variables and definitions
*************************************************************************/
#define ESC       0x1B              /* the Escape char */
#define DEL       0x7F              /* the Delete char */

short linecnt;                      /* lines left on current page */
short lineno;                       /* current source file line number */
short linewidth;                    /* calculated line width */
short pageno;                       /* current page number */
UCHAR  headpend;                    /* heading line pending (just '\f') */
UCHAR  wrapflag;                    /* output line exceed max width flag */
short tabcnt;                       /* column counter for tab expansion */
UCHAR  readerr;                     /* status of last get1char() */
short unget1char;                   /* saved char from next1char() or -1 */

#define BUFLEN    256               /* length of line i/o buffers */

UCHAR buf2[BUFLEN+3];               /* output buffer */
short buf2i;                        /* index for buf2 */

UCHAR peject[] = { '/', '*', 'e', 'j', 'e', 'c', 't', '*', '/', 0 };
short pejectlen;                    /* calculated length of eject seq */

#define CTL_EXPAND_CHAR    '^'      /* for expanding control chars */
#define ALT_EXPAND_CHAR    '\\'     /* for expanding alt characters */


/* alternate option char to use as well as '-', use '-' for no alternate */
#if MSDOS
#define ALTOPT             '/'      /* alternate option char (like '-') */
#else
#define ALTOPT             '-'      /* alternate option char (like '-') */
#endif

/*************************************************************************
* variables for command line arguments 
*************************************************************************/

short argc;                         /* argument count */
UCHAR  **argv;                      /* ptr to array of pointers to args */

short sav_argc;                     /* saved (at Infiles) for copies option */
UCHAR  **sav_argv;

/*eject*/
/*************************************************************************
* Ptype - type to the printer
*************************************************************************/

void main(in_argc,in_argv)
int   in_argc;                      /* argument count */
char  *in_argv[];                   /* start of array of pointers to args */
{
   UCHAR error, exitflg;            /* error flags */
   void *lb = NULL;                 /* find file list buffer */

   if (in_argc == 0)                /* if called by icon */
      return;                       /* error - not handled */
   argc = (short) in_argc;          /* make command line info global */
   argv = (UCHAR **) in_argv;
   initvar();                       /* init options and variables */
   if (error = getparms())          /* process cmd line except "Infiles" */
      exit(20);                     /* bad parms = - exit */
   if (!verflag && *name2 == 0) {   /* if not vers & output is to stdout */
      file2 = stdout;
      fdisp = stderr;               /* also set display to stderr */
   }
   if (verflag || doptflag) {       /* if version or options msg desired */
      putstring(VERMSG);
      putoptmsg();                  /* display options message */
      if (*name2)                   /* if not stdout */
         put3strings("to \"", name2, "\" from");
      else                          /* else stdout */
         putstring("to stdout from");
   }
   if (!verflag && *name2 != 0)     /* if not vers & not to stdout */
      if ((file2 = fopen(name2,"w")) == NULL) { /* open output file */
         putstring("\n" PROGNAME ": ERROR: ");
         put3strings("Invalid output file (can't open) \"",name2,"\"\n");
         exit(20);               /* set return error code */
      }
   sav_argc = argc;                 /* save agruments for copies option */
   sav_argv = argv;
#if AMIGA && ARPFILE
   if (argc == 0 && arpflag) {      /* if no input files and ARP wanted */
                                    /* try to open the ARP lib */
      if ((ArpBase = OpenLibrary (ArpName,0L)) == NULL) {
         putstring("\n" PROGNAME ": ERROR: ");
         put3strings("Can't open library \"", ArpName, "\"\n");
         if (*name2 && !verflag)    /* if not using stdout and not just ver */
            fclose(file2);          /* close output file */
         exit(20);                  /* set return error code */
      }
      onbreak(&breakfunc);          /* with ARP we must trap ctl-C */
   }
   if (ArpBase == NULL)             /* if ARP not being used */
      arpflag = FALSE;              /* reset the flag */
#endif
   if (!verflag)                    /* if not just version display */
      error = printseq(istring, file2); /* send printer init seq, if any */
   exitflg = FALSE;                    /* init for the loop */
   do {                                /* open input files and process */
      argc = sav_argc;                 /* restore Infile arguments */
      argv = sav_argv;
      do {                             /* loop for all input files */
         if (argc > 0)                 /* if any 'infile' name */
            strncpy(name1,*argv,NAMELEN); /* copy it from command line */
#if AMIGA && ARPFILE
         else if (arpflag)             /* if using ARP file requester */
            if (exitflg = getarp())    /* get file name from it */
               break;                  /* if end - exit both loops */
#endif
#if AMIGA || MSDOS                     /* process wild cards, build list */
         lb = findfile(name1, NAMELEN+1, TRUE, stderr, PROGNAME);
         while (!error && readerr != FBAD && findfget(name1,lb))
#endif
         {
            if (verflag || doptflag)   /* if version or options msg desired */
               if (*name1)             /* if not stdin */
                  put3strings(" \"", name1, "\"\n");
               else                    /* else stdin */
                  putstring(" stdin\n");
            if (!verflag) {            /* if not version display only */
               inithead();             /* init heading etc. */
               if (*name1 == 0)        /* if from stdin */
                  file1 = stdin;
               else if ((file1 = fopen(name1,FOPENPARM)) == NULL) {
                     putstring("\n" PROGNAME ": ERROR: ");
                     put3strings("Can't open input file \"", name1, "\"\n");
                     break;
                  }                             
               readerr = FGOOD;        /* init for new read of input file */
               unget1char = -1;
                                       /* for all lines */
               while (!error && pageno <= epage && readerr == FGOOD)
                  error = convline();  /* convert and output lines */
               if (!error && lines && !headpend) /* auto paging & not eject */
                  if (putc('\f',file2) == EOF) /* eject the last page */
                     error = TRUE;
               if (!error)             /* if no errors yet */
                  if (fflush(file2) == EOF) /* output all before next ARP req */
                     error = TRUE;
               if (*name1)             /* if a file */
                  fclose(file1);       /* close input file */
            }
         }                             /* end while find next */
         if (lb != NULL)
            free(lb);                  /* free the list buffer */
         argv++;                       /* bump to next argument */
         argc--;
                                       /* loop for all files */
      } while (!error && readerr != FBAD && (argc > 0 || arpflag));
                                       /* loop for all copies */
   } while (!exitflg && !error && readerr != FBAD && --copies > 0);
/* all done - wrap up */

   if (!verflag)                    /* if not just version display */
      error = printseq(rstring, file2); /* send printer reset seq, if any */
   if (*name2 && !verflag)          /* if not using stdout and not just ver */
      fclose(file2);                /* close output file */
#if AMIGA && ARPFILE
   if (arpflag) {                   /* if using ARP file requester */
      CloseLibrary(ArpBase);        /* close the library */
      ArpBase = NULL;
   }
#endif
   if (readerr == FBAD) {           /* if read error */
      putstring("\n" PROGNAME ": ERROR: ");
      put3strings("Error reading file \"", name1, "\"\n");
      exit(20);                     /* if error - exit */
   }
   if (error) {
      putstring("\n" PROGNAME ": ERROR: ");
      put3strings("Error writing file \"", name2, "\"\n");
      exit(20);                     /* if error - exit */
   }
   return;
}

#if AMIGA && ARPFILE
/*************************************************************************
* breakfunc() - trap for ctl-C      (only used with ARP file requester)
*************************************************************************/
breakfunc()
{
   if (ArpBase != NULL) {           /* if using ARP file requester */
      CloseLibrary(ArpBase);        /* close the library - exit will do rest */
      ArpBase = NULL;
   }
   return TRUE;                     /* abort printing */
}

/*************************************************************************
* getarp() - get name1[] using the ARP file requester
*     Return (exit flag) is TRUE if input is to be terminated.
*************************************************************************/
UCHAR getarp()
{
   UCHAR *success;                     /* status of request */

   success = FileRequest(&myFileReq);  /* try to get file name */
   if (success != NULL && *arp_file) { /* if not canceled and a file name */
      strncpy(head_string,arp_file,HEADFNL); /* print just file name */
      strmfp(name1,arp_dir,arp_file);  /* build the path/file name */
      return FALSE;                    /* don't exit yet */
   }
   return TRUE;                        /* exit from program */
}
#endif
/*eject*/
/*************************************************************************
* getparms() - get command line parameters
*************************************************************************/
UCHAR getparms()
{
   UCHAR *cp;                       /* pointer to curr string */
   register UCHAR opc;              /* saved option char */
   register UCHAR error;            /* error flag */
   UCHAR **tv;                      /* temp argv */
   short  tc;                       /* temp argc */

   error = FALSE;                   /* init to no error */
   argv++;                          /* bump to 1st param */
   argc--;                          /* adjust arg count */
   for (; argc > 0; argc--, argv++) { /* while arguments */
      cp = *argv;                   /* point to cmd line arg */
      if (*cp == '-' || *cp == ALTOPT) { /* if an option */
         while (*++cp) {            /* while arguments */
            opc = (UCHAR) toupper(*cp);
            if (opc == 'A') {          /* pass thru all ctl chars */
               all_ctl_char = (TRUE + FALSE - ALL_CTL_CHAR);
            } else if (opc == 'C') {          /* number of copies */
               error |= numparam(&copies,NULL,++cp,opc);
               if (copies == 0)        /* if 0 */
                  copies = COPIES;     /* make it the default */
               break;
            } else if (opc == 'D') {   /* display options */
               doptflag = (TRUE + FALSE - DOPTFLAG);
            } else if (opc == 'E') {   /* no ESC, BS, or DEL */
               escflag = (TRUE + FALSE - ESCFLAG);
#if AMIGA && ARPFILE
            } else if (opc == 'F') {   /* use ARP file requester */
               arpflag = (TRUE + FALSE - ARPFLAG);
#endif
            } else if (opc == 'H') {
               cp++;                   /* bump to start string */
               strncpy(head_string,cp,HEADFNL); /* use it in header */
               break;
            } else if (opc == 'I') {   /* init printer string */
               if (*++cp == 0)
                  istring = NULL;      /* clear the string */
               else
                  istring = cp;        /* save the string */
               break;
            } else if (opc == 'L') {   /* lines */
               error |= numparam(&lines,NULL,++cp,opc);
               break;
            } else if (opc == 'M') {   /* left margin */
               error |= numparam(&margin,NULL,++cp,opc);
               if (margin == 0)        /* if 0 specified */
                  margin = MARGIN;     /* set it to default */
               break;
            } else if (opc == 'N') {   /* no numbers */
               numflag = (TRUE + FALSE - NUMFLAG);
            } else if (opc == 'O') {   /* output file name */
               cp++;                   /* bump to start of name */
               strncpy(name2,cp,NAMELEN);
               name2[NAMELEN] = 0;     /* insure ending null */
               break;
            } else if (opc == 'P') {   /* pages to print */
               error |= numparam(&spage,&epage,++cp,opc);
               if (spage == 0)         /* if 0 specified */
                  spage = SPAGE;       /* set it to default */
               if (epage == 0)         /* if 0 specified */
                  epage = EPAGE;       /* set it to default */
               if (spage > epage)
                  error = TRUE;        /* catch pinheads */
               break;
            } else if (opc == 'R') {   /* reset printer string */
               if (*++cp == 0)
                  rstring = NULL;      /* clear the string */
               else
                  rstring = cp;        /* save the string */
               break;
            } else if (opc == 'S') {   /* supress heading */
               sheadflag = (TRUE + FALSE - SHEADFLAG);
            } else if (opc == 'T') {;  /* tab spacing */
               error |= numparam(&tabs,NULL,++cp,opc);
               break;
            } else if (opc == 'V') {   /* version display */
               verflag = (TRUE + FALSE - VERFLAG);
            } else if (opc == 'W') {   /* width */
               error |= numparam(&width,NULL,++cp,opc);
               if (width > BUFLEN-1 || width < 0)  /* if invalid */
                  width = BUFLEN - 1;  /* set it to max */
               break;
            } else if (opc == 'X') {   /* no extended chars */
               extflag = (TRUE + FALSE - EXTFLAG);
            } else if (opc == 'Z') {   /* laser printer */
               lazflag = (TRUE + FALSE - LAZFLAG);
            } else
               error = TRUE;        /* else unknown option */
         }
      } else {                      /* else not an option */
         if (*cp == '?')            /* if format display desired */
            error = TRUE;           /* fake an error */
         else                       /* else input file name */
            break;                  /* exit to main program */
      }
   }
   linewidth = width ? width : BUFLEN-1;  /* 0 means 255 */
   if (margin > linewidth-LINENLEN-3)     /* set max margin */
      margin = linewidth-LINENLEN-3;
                                    /* test the rest of the params */
   for (tc=argc, tv=argv; tc > 0; tc--, tv++) {
      if (**tv == '-' || **tv == ALTOPT) /* if any options follow 'infile' */
         error = TRUE;              /* set error */
   }
   if (printseq(istring, NULL)) {   /* test printer init string */
      putstring(PROGNAME ": ERROR: Invalid init printer string\n");
      error = TRUE;
   }
   if (printseq(rstring, NULL)) {   /* test printer reset string */
      putstring(PROGNAME ": ERROR: Invalid reset printer string\n");
      error = TRUE;
   }
   if (error) {                     /* if error */
                                    /* display version messsage */
      putstring(VERMSG " Type to Printer  -  Correct format is:\n");
   }
   if (error || verflag) {          /* if error or version display */
#if AMIGA && ARPFILE
      putstring(PROGNAME " -adefnsvxz ");
#else
      putstring(PROGNAME " -adensvxz ");
#endif
      putstring("-cN -hS -iS -lN -mN -pN,E -rS -tN -wN -oOutfile Infile(s)\n");
   }
   if (error) {                     /* if error */
      putstring("\n");

      putstring(" -n  no line numbers                    ");
         putstring(" -a  all control chars pass thru\n");

      putstring(" -m  left margin column (default=1)     ");
         putstring(" -e  no ESC, BS, or DEL are output\n");

      putstring(" -w  line width, -w same as -w255       ");
         putstring(" -x  no extended chars are output\n");

      putstring(" -l  lines/page, -L0 no heading & FF    ");
         putstring("\n");

      putstring(" -t  expand tab size, -t0 no expand     ");
#if unix
         putstring(" -o  output file (default = stdout)\n");
#else
         putstring(" -o  output file (default = printer)\n");
#endif

      putstring("                                        ");
#if AMIGA && ARPFILE
         putstring(" -f  ARP file requester if no Infile");
#endif
         putstring("\n");

      putstring(" -s  suppress heading line              ");
         putstring(" -z  laser printer, no LF before FF \n");

      putstring(" -h  heading str replaces file name     ");
         putstring(" -i  init printer string  (with \\e )\n");

      putstring(" -c  number of copies                   ");
         putstring(" -r  reset printer string ( or \\xNN)\n");

      putstring(" -p  start page, optional end page      ");
         putstring("\n");

      putstring(" -v  display ver & file names only      ");
         putstring(" -d  display options and file names\n");

      putstring("\n");
      putstring(" " PROGNAME " ?  (or invalid option) displays this ");
         putstring("help screen\n"); 
      putoptmsg();                  /* display options message */
   }
   return error;
}
/*eject*/
/*************************************************************************
* putoptmsg() - display option message
*************************************************************************/
void putoptmsg()
{
   putstring("Options:");
   if (all_ctl_char != ALL_CTL_CHAR || doptflag != DOPTFLAG ||
      numflag != NUMFLAG || sheadflag != SHEADFLAG || verflag != VERFLAG ||
      escflag != ESCFLAG || arpflag != ARPFLAG || extflag != EXTFLAG ||
      lazflag != LAZFLAG) {
      putstring(" -");
      if (all_ctl_char != ALL_CTL_CHAR) /* if all ctl char pass thru */
         putstring("a");
      if (doptflag != DOPTFLAG)     /* if options display */
         putstring("d");
      if (escflag != ESCFLAG)       /* if no escape char */
         putstring("e");
      if (arpflag != ARPFLAG)       /* if use ARP file requester */
         putstring("f");
      if (numflag != NUMFLAG)       /* if no numbers */
         putstring("n");
      if (sheadflag != SHEADFLAG)   /* if suppress heading */
         putstring("s");
      if (verflag != VERFLAG)       /* if version display only */
         putstring("v");
      if (extflag != EXTFLAG)       /* if no extended chars */
         putstring("x");
      if (lazflag != LAZFLAG)       /* if laser printer */
         putstring("z");
   }
   if (margin != MARGIN) {          /* left margin column */
      putstring(" margin=");
      putnumstr(margin);
   }
   putstring(" width=");            /* print line width */
   putnumstr(width);
   putstring(" lines=");            /* lines per page */
   putnumstr(lines);
   putstring(" tabs=");             /* tab spacing */
   putnumstr(tabs);
   if (spage != SPAGE || epage != EPAGE) {  /* pages to print */
      putstring(" pages=");
      putnumstr(spage);
      putstring(",");
      if (epage != EPAGE)
         putnumstr(epage);
      else
         putstring("all");
   }
   if (copies != COPIES) {          /* number of copies to print */
      putstring(" copies=");
      putnumstr(copies);
   }
   if (istring != NULL) {           /* if init string */
      putstring(" -i");
      putstring(istring);
   }
   if (rstring != NULL) {           /* if reset string */
      putstring(" -r");
      putstring(rstring);
   }
   putstring("\n");                 /* end the line */
}
/*eject*/
/*************************************************************************
* numparam() - get numeric parameter with error checking
*     This routine gets the parameter(s) following the option letter
*     for options such as:  -t4  -w90  -p39,7
*
*     Called from getparms().
*
* Return:  TRUE = error in the number string
*************************************************************************/
UCHAR numparam(np,np2,sp,optc)
short *np;                          /* ptr to number to be returned */
short *np2;                         /* ptr to 2nd number or NULL */
UCHAR  *sp;                         /* ptr to ascii number string */
UCHAR   optc;                       /* option char for message */
{
   UCHAR flag2;                     /* 2nd number flag */
   register short n;                /* accumulated number */
   register UCHAR c;                /* the char form the string */
   UCHAR optmsg[2];                 /* for building error msg */

   for (flag2=FALSE; ;flag2=TRUE) { /* for both numbers */
      for (n=0; ;) {                /* accumulate the number */
         c = *sp++;
         if (!isdigit(c))           /* stop on a non digit */
            break;
         n = (n << 3) + n + n  + (c & 0x0F);
      }
      if (n < 0)                    /* if overflow */
         n = 0;
      if (!flag2) {                 /* if 1st number */
         if ((c == ',' || c == '.') && np2 != NULL) { /* if 2nd number */
            *np = n;                /* return the 1st number */
         } else if (c == 0) {       /* if end of string */
            *np = n;                /* return the 1st number */
            if (np2 != NULL)        /* if 2nd number expected */
               *np2 = 0;            /* set it to 0 */
            return FALSE;           /* return, no error */
         } else
            break;                  /* go put error message */
      } else {                      /* else 2nd number */
         if (c != 0)                /* if invalid end of string */
            break;                  /* go put error message */
         *np2 = n;                  /* return 2nd number */
         return FALSE;              /* return, no error */
      }
   }
   optmsg[0] = optc;
   optmsg[1] = 0;                   /* make a string of the option char */
   putstring(PROGNAME ": ERROR: ");
   put3strings("Invalid numeric value for option \"-", optmsg, "\"\n");
   return TRUE;                     /* return an error indicator */
}
/*eject*/
/*************************************************************************
* get1char() - get a character from the input file
*     Special processing is performed on control characters.  For a
*     complete description, see "Special processing" on page 3 above).
*
* Return value:
*     The character value (0-255) in a byte (unsigned char).
*     Carriage returns are deleted.
*     Newline is returned on EOF or ERROR.  See readerr.
*
* External output:  readerr
*     FGOOD    = line read ok
*     FBAD     = read error
*     FEOF     = end of file
*************************************************************************/
UCHAR get1char()
{
   register int ich;                /* int of input char */

   if (unget1char != -1) {          /* if an unget to process */
      ich = unget1char;             /* save it */
      unget1char = -1;              /* clear it */
      return (UCHAR) ich;           /* RETURN */
   }
   if (readerr != FGOOD)            /* if previous error */
      return '\n';                  /* RETURN */
   do {                             /* loop until not a carriage return */
      ich = getc(file1);            /* get input char */
      if (ich == EOF) {             /* if possible end of file or error */
         if (ferror(file1)) {
            readerr = FBAD;         /* error in reading file */
            return '\n';            /* RETURN */
         }
         if (feof(file1)) {
            readerr = FEOF;         /* normal end of file */
            return '\n';            /* RETURN */
         }
      }
   } while (ich == '\r');           /* throw away carriage returns */
   return (UCHAR) ich;              /* return value (0-255) */
}

/*************************************************************************
* next1char() - look ahead one char using an unget for get1char()
*     Note, calling this twice without calling get1char() will loose one.
*************************************************************************/
UCHAR next1char()
{
   return (UCHAR) (unget1char = (short) get1char());
}
/*eject*/
/*************************************************************************
* convline() - convert input line to output line
*
*  The line number is placed at the front of the line if numflag
*  is TRUE.  If tabs is not 0, then tabs are expanded to spaces.  The
*  output line will be no longer than linewidth plus a \n, [\f], and \0.
*  If the peject sequence is detected, a \f is inserted after
*  it.  If the output line ends in \f\n, the two chars are
*  reversed to \n\f or changed to just \f depending on the "-z"
*  command line option.  Some of this logic is in char2buf() & putline().
*
* Return value:   TRUE if output error - see putline()
*                 See readerr also - see get1char()
*************************************************************************/
UCHAR convline()
{
   short mcnt;                      /* counter for left margin */
   register UCHAR ch, ch2;          /* chars from input function */
   register UCHAR error;            /* output error returned */
   UCHAR expand;                    /* expanded print flag */

   error = FALSE;                   /* assume no output error */
   buf2i = 0;                       /* init out count */
   tabcnt = 0;                      /* init tab column count */
   wrapflag = FALSE;                /* init no wrap, set by char2buf() */

   while ((ch = get1char()) == '\f') { /* find leading formfeeds */
      if ((ch2 = next1char()) == '\n') /* look ahead one char */
         break;                        /* if end of line don't move it */
      buf2[buf2i++] = ch;           /* move these ff to start of line */
   }
                                    /* if end of file and empty buffer */
   if (ch == '\n' && readerr != FGOOD && buf2i == 0)
      return error;                 /* RETURN */

   for (mcnt=0; mcnt < margin-1; mcnt++)
      buf2[buf2i++] = ' ';          /* put left margin spaces */
   if (numflag) {                   /* if line numbers */
      itoasc(++lineno,LINENLEN,'Z',buf2+buf2i); /* get line number */
      buf2i += LINENLEN;
      buf2[buf2i++] = ' ';          /* add 2 spaces */
      buf2[buf2i++] = ' ';
   }
/*eject*/
   /* ch = next char & there may be 1 unget.  - CAREFULL with changes - */

                                    /* while no errors & not end of input */
   while (!error && ch != '\n' && readerr == FGOOD) {
      if (ch == '\t' && tabs) {     /* if a tab to expand */
         do
            error = charbuf2(' ');  /* put blanks */
         while (!error && tabcnt % tabs != 0);
      } else {                         /* else normal char or no tab expand */
         if (ch < ' ' || ch >= DEL) {  /* if possible expanded print */
            expand = FALSE;            /* assume no expand */
            if (ch > DEL) {            /* if an alt-char */
               if (!extflag)           /* if don't pass thru alt-chars */
                  expand = TRUE;
               else if (ch < 0xA0 && !all_ctl_char) /* if alt-ctl-chars */
                  expand = TRUE;
               if (expand) {           /* if expand this alt-char */
                  ch &= 0x7F;          /* clear alt bit and print prefix */
                  error = charbuf2(ALT_EXPAND_CHAR);
                  if (ch >= ' ' && ch < DEL) /* if no more expansion needed */
                     expand = FALSE;
               }
            } else {                   /* else a ctl-char or DEL */
               switch (ch) {
                  case '\t':           /* tab */
                  case '\f':           /* formfeed - never expand */
                     break;
                  case '\b':           /* backspace */
                  case ESC:            /* escape */
                  case DEL:            /* delete char */
                     if (!escflag)     /* if escape etc. don't pass thru */
                        expand = TRUE;
                     break;
                  default:             /* other ctl-chars */
                     if (!all_ctl_char) /* if all ctl don't pass thru */
                        expand = TRUE;
               }
            }
            if (expand) {              /* if expand of ctl char or DEL */
               ch ^= 0x40;             /* make a normal char out of it */
               error |= charbuf2(CTL_EXPAND_CHAR); /* print ctl prefix */
            }
         }
         error |= charbuf2(ch);     /* put char in buffer */
      }
      if (!error)
         ch = get1char();           /* get next char */
   }
   buf2[buf2i] = '\n';              /* put newline on end */
   if (!error && !wrapflag)         /* no error & not blank line after wrap */
      error = putline();            /* output line to file */
   return error;                    /* return output error code */
}
/*eject*/
/*************************************************************************
* charbuf2() - put a char in buf2
*     Put a character in buf2 and do not allow a line too long.
*************************************************************************/
UCHAR charbuf2(ich)
int ich;
{
   UCHAR error;                     /* error flag */
   register UCHAR ch;               /* byte of the input char */

   if (wrapflag)                    /* if just wrapped */
      while (buf2i < margin-1)
         buf2[buf2i++] = ' ';       /* put left margin spaces */
   wrapflag = FALSE;                /* reset wrap flag */
   ch = (UCHAR) ich;                /* use low byte of input char */
   if ((ch & 0x7F) >= ' ')          /* bump tab count for non ctrl chars */
      tabcnt++;                     /*   (good enough - DEL's included)  */
   error = FALSE;                   /* assume no error */
   buf2[buf2i++] = ch;              /* put char in buffer */
   if (buf2i >= linewidth) {        /* if max chars */
      buf2[buf2i] = '\n';           /* put newline char */
      error = putline();            /* output the line */
      buf2i = 0;                    /* start at front of buffer */
      wrapflag = TRUE;              /* we just wrapped */
   }
   return error;
}
/*eject*/
/*************************************************************************
* putline() - write line to output file
*        See convline() above.  The line ends in '\n' and may have nulls.
*        buf2i is the index to the ending '\n' in buffer buf2.
*************************************************************************/
UCHAR putline()
{
   register UCHAR *p1, error, *pb2; /* pointers, error flag */
   register short size, tsize;      /* line size */
                                    
   error = FALSE;                   /* init to no error */
                                    /* skip 1st formfeed on new page */
   if (headpend && buf2[0] == '\f' && buf2[1] != '\n') {
      pb2 = buf2 + 1;               /* skip leading formfeed */
      size = buf2i;
   } else {
      pb2 = buf2;                   /* init ptr and size */
      size = buf2i + 1;
   }
   if (lines) {                     /* if page heading is to be done */
      if (headpend)                 /* if time for heading */
         error = doheading();       /* do page heading */
      if (linecnt == 1 && buf2[buf2i-1] != '\f') { /* if last line of page */
         if (lazflag)               /* if laser printer */
            buf2[buf2i] = '\r';     /* replace NL with CR */
         buf2[++buf2i] = '\f';      /* add formfeed to the end */
         size++;
      } else {                      /* else not last line */
                                    /* search for page eject sequence */
         for (p1=pb2, tsize=size-pejectlen; tsize > 0; p1++, tsize--)
            if (strncmp(p1,peject,pejectlen) == 0) { /* if match is found */
               if (lazflag)         /* if laser printer */
                  buf2[buf2i] = '\r'; /* replace NL with CR */
               buf2[++buf2i] = '\f'; /* add formfeed to the end */
               size++;
               break;               /* exit the while loop */
            }
      }
      if (buf2[buf2i-1] == '\f' && buf2[buf2i] == '\n') { /* if end is \f\n */
         if (lazflag)               /* if laser printer */
            buf2[buf2i-1] = '\r';   /* make it \r\f */
         else
            buf2[buf2i-1] = '\n';   /* make it \n\f */
         buf2[buf2i] = '\f';
      }
      --linecnt;                    /* decr lines left */
   }
   for (p1=pb2; size && !error; p1++, size--) { /* for the whole buffer */
      if (pageno >= spage && pageno <= epage)    /* if right pages */
         if (putc(*p1, file2) == EOF) /* output the chars */
            error = TRUE;           /* set error on EOF */
      if (*p1 == '\f')              /* if page ejected */
         if (!error && lines)       /* if heading not supressed */
            if (size > 1) {         /* if not end of this buffer */
               error = doheading(); /* do page heading */
               --linecnt;           /* decr lines left */
            } else
               headpend = TRUE;     /* do it only if another line follows */
   }
   return error;                    /* return error status */
}


/*************************************************************************
* doheading() - output the heading line
*     Two types of formfeeds are processed which cause this heading to be
*     output, the ones occuring in the source file and the ones which
*     are generated when the page is full (see lines per page option).
*     Called from putline().
* Return value:
*     TRUE if an output error occurs.
*     (Resets the lines left count.)
*************************************************************************/
UCHAR doheading()
{
   UCHAR *cp;                       /* pointer */
   UCHAR error;                     /* error flag */
   short mcnt;                      /* counter for margin */

   error = FALSE;                   /* init to no error */
   pageno++;                        /* incr page number */
   if (sheadflag)                   /* if supressing heading */
      linecnt = lines;              /* reset line count */
   else {
      if (pageno >= spage && pageno <= epage) { /* if right pages */
         mcnt = margin - 1;         /* get space count for left margin */
                                    /* don't push heading past right margin */
         if (mcnt > linewidth - HEADLLEN)
            mcnt = linewidth - HEADLLEN;
         for (; mcnt > 0 && !error; mcnt--)
            if (putc(' ',file2) == EOF) /* put left margin spaces */
               error = TRUE;        /* set error on end of file */
                                    /* put page number in heading */
         itoasc(pageno,PAGENLEN,'Z',heading+HEADPNUM);
         for (cp = heading; *cp && !error; cp++)
            if (putc(*cp,file2) == EOF) /* write heading to output */
               error = TRUE;        /* set error on end of file */
      }
      linecnt = lines -2;           /* reset line count */
   }
   if (pageno > epage)              /* if we're about to quit */
      headpend = TRUE;              /* supress the ending '\f' */
   else
      headpend = FALSE;             /* normal heading done now */
   return error;
}
/*eject*/
/************************************************************************
* printseq() - send init or reset sequence to the printer
*
* Input:  str = ptr to the string to output or NULL
*         fp = file pointer to printer or NULL
*
* Return: Non-zero if error
************************************************************************/
UCHAR printseq(str, fp)
UCHAR *str;
FILE *fp;
{
   UCHAR ch, ch2;                   /* saved/translated char */

   if (str == NULL)                 /* if no string */
      return 0;
   while ((ch = *str++) != 0) {     /* for all chars */
      if (ch == '\\') {
         if ((ch = *str++) == 0)    /* get another char */
            return 1;
         switch (ch) {
            case 'e':               /* ESC char */
               ch = ESC;
               break;
            case 'x':               /* hex number */
                                    /* get another char */
               if ((ch = *str++) == 0 || !isxdigit(ch))
                  return 1;
               ch = (UCHAR) (HEX2BIN(ch) << 4);
                                    /* get another char */
               if ((ch2 = *str++) == 0 || !isxdigit(ch2))
                  return 1;
               ch = (UCHAR) (ch | HEX2BIN(ch2)); /* complete the byte value */
               break;
            default:
               return 1;
         }
      }
      if (fp != NULL)
         if (putc(ch, fp) == EOF)   /* write the chars */
            return 1;
   }
   return 0;                        /* all ok - return */
}
/*eject*/
/*************************************************************************
* initvar() - initialize global variables (one time at start)
*************************************************************************/
void initvar()
{
   all_ctl_char = ALL_CTL_CHAR;     /* init option variables */
   doptflag = DOPTFLAG;
   escflag = ESCFLAG;
   arpflag = ARPFLAG;
   istring = NULL;
   numflag = NUMFLAG;
   rstring = NULL;
   sheadflag = SHEADFLAG;
   verflag = VERFLAG;
   extflag = EXTFLAG;
   lazflag = LAZFLAG;

   copies = COPIES;
   lines = LINES;
   margin = MARGIN;
   spage = SPAGE;
   epage = EPAGE;
   tabs = TABS;
   width = WIDTH;

   fdisp = stdout;                  /* default display is to stdout */
   *name1 = 0;                      /* set to no input file name */
   name1[NAMELEN] = 0;              /* insure ending null in name */
   *head_string = 0;                /* set to no heading string */
   head_string[HEADFNL] = 0;        /* insure trailing null */
   strcpy(name2,NAME2);             /* init output file name */
   pejectlen = strlen(peject);      /* init misc variables */
   
#if AMIGA && ARPFILE
   ArpBase = NULL;                  /* init to library not open */
   arp_dir[0] = 0;                  /* clear ARP dir name */
   arp_file[0] = 0;                 /* clear ARP file name */
   memset((char *) &myFileReq, 0, sizeof(struct FileRequester));
   myFileReq.fr_Hail = arp_hail;    /* init the ARP file req */
   myFileReq.fr_File = arp_file;
   myFileReq.fr_Dir  = arp_dir;
   myFileReq.fr_Flags2 = FR2F_LongPath; /* allow long dir names */
#endif
}
/*eject*/
/*************************************************************************
* inithead() - initialize the heading string
*************************************************************************/
void inithead()
{
   UCHAR *cp1, *cp2;                /* char pointers */
   short i;                         /* counter */

   linecnt = lineno = pageno = 0;   /* init global variables */
   if (lines == 0)                  /* if no formfeed & headings */
      pageno = spage;               /* put page number in range */
   headpend = TRUE;                 /* do heading before 1st line */
   cp1 = heading;                   /* init pointer for clear */
   for (i=0; i<HEADLLEN; i++)       /* clear heading to blanks */
      *cp1++ = ' ';
   *cp1++ = '\n';                   /* put two newlines */
   *cp1++ = '\n';
   *cp1 = 0;                        /* put NULL on end */
   cp1 = name1;                     /* init pointers for move */
   if (*head_string)                /* if heading string specified */
      cp1 = head_string;            /* override the file name */
   cp2 = heading;
   for (i=0; i<HEADFNL && *cp1; i++)
      *cp2++ = *cp1++;              /* move file name to heading w/o null */
                                    /* move date and time */
   datetime(name1,heading+HEADDAY,heading+HEADDATE,heading+HEADTIME);
   cp1 = (UCHAR *) PAGELIT;
   cp2 = heading+HEADPAGE;
   while(*cp1)                      /* move "PAGE" to heading w/o null */
      *cp2++ = *cp1++;
}
/*eject*/
/*************************************************************************
* putstring() - put a string to the display file
*************************************************************************/
void putstring(cp)
register UCHAR *cp;                 /* pointer to the string */
{
   for(; *cp; cp++) {               /* while a char */
      putc(*cp,fdisp);              /* put out chars */
      if (*cp == '\n')              /* if end of line */
         fflush(fdisp);             /* flush buffer (display it) */
   }
}


/*************************************************************************
* put3strings() - concatenate three strings to stderr
*************************************************************************/
void put3strings(s1, s2, s3)
UCHAR *s1, *s2, *s3;                /* pointers to the 3 strings */
{
   putstring(s1);
   putstring(s2);
   putstring(s3);
}


/*************************************************************************
* putnumstr() - put a number to stderr (converted to an ascii string)
*************************************************************************/
void putnumstr(n)
int n;
{
   UCHAR ns[6], *p;

   itoasc(n,5,'Z',ns);              /* convert it to ascii */
   ns[5] = 0;                       /* put trailing null */
   for (p=ns; *p == ' '; p++)       /* bypass the leading spaces */
      ;
   putstring(p);                    /* write it to stderr */
}
/*eject*/
/*************************************************************************
* get current date and time        Author:  Bob Alston
*                                  Date:    11-21-85
*
*     Modified 11-30-87 to get file date instead of the system date,
*     except when input is from "stdin" (filename pointer is NULL or 
*     filename points to a null string).
*
*     Day name, date (mm-dd-yy), and time (hh:mm:ss) are moved to
*     locations pointed to by the three pointers.  No trailing NULLs
*     are moved.  Any pointer may be NULL to supress the move.
*
*************************************************************************/
void datetime(fname,dayp,datep,timep)
UCHAR *fname;                       /* ptr to filename for date */
UCHAR *dayp;                        /* place to store 9 char day name */
UCHAR *datep;                       /* place to store 8 char date */
UCHAR *timep;                       /* place to store 8 char time */
{
   long t;                          /* long to receive time */
   struct tm *tp;                   /* ptr for gmtime() */
   UCHAR *cp;                       /* misc pointer */
#if MSDOS || unix
   struct stat statbuf;             /* for finding file time */
#endif
   static char *dayname[] = {
      "Sunday   ","Monday   ","Tuesday  ","Wednesday",
      "Thursday ","Friday   ","Saturday "
   };
   static UCHAR dateplate[] = "mm-dd-yy";
   static UCHAR timeplate[] = "hh:mm:ss";

#if AMIGA
   if (fname && *fname) {           /* if input is a file */
      t = getft(fname);             /* get time of file */
      tp = gmtime(&t);              /* file times need true translate */
   } else {                         /* input is from stdin */
      t = time((long *) NULL);      /* get system time */
      tp = localtime(&t);           /* system time has been converted */
   }
#endif
#if MSDOS || unix
   if (fname && *fname) {           /* if input is a file */
      stat(fname,&statbuf);         /* get file stats */
      t = statbuf.st_mtime;         /* get last modified time */
   } else {                         /* input is from stdin */
      t = time((long *) NULL);      /* get system time */
   }
   tp = localtime(&t);              /* translate the time */
#endif

   itoasc(tp->tm_mon+1,2,'Z',dateplate); /* convert date to ascii */
   itoasc(tp->tm_mday,2,'9',dateplate+3);
   itoasc(tp->tm_year,2,'9',dateplate+6);
   itoasc(tp->tm_hour,2,'Z',timeplate); /* convert time to ascii */
   itoasc(tp->tm_min,2,'9',timeplate+3);
   itoasc(tp->tm_sec,2,'9',timeplate+6);
   cp = (UCHAR *) dayname[tp->tm_wday]; /* get pointer to day name */
   if (dayp != NULL)                /* if caller wants day name */
      while (*cp)                   /* move day name to caller area */
         *dayp++ = *cp++;
   cp = dateplate;                  /* get pointer to date */
   if (datep != NULL)               /* if caller wants date */
      while (*cp)                   /* move date */
         *datep++ = *cp++;
   cp = timeplate;                  /* get pointer to time */
   if (timep != NULL)               /* if caller wants time */
      while (*cp)                   /* move time */
         *timep++ = *cp++;
   return;
}


/*************************************************************************
* integer to ascii decimal         Author:  Robert C. Alston
*                                  Date:    11-21-85
*
*     The low w digits of the input number n are placed in the
*     field.  Pointer cp points to the start of the field.
*     Zero supression is optional. Upper digits of the number
*     which will not fit are ignored.  No trailing NULL is output.
*************************************************************************/
void itoasc(n,iw,c,cp)
register int  n;                    /* number to convert */
int  iw;                            /* width of field */
int  c;                             /* 'Z' for zero supress */
register UCHAR *cp;                 /* start of field */
{
   register int t;                  /* temp number */
   short i;                         /* index for field */
   short w;                         /* width */

   w = (short) iw;                  /* use short for width */
   for (i = w-1; i >= 0; i--) {
      t = n / 10;                   /* calculate decimal digits */
      cp[i] = (UCHAR)(n - ((t << 3) + t + t) + '0'); /* store digit */
      n = t;
   }
   if (c == 'Z' && w > 1)           /* if supression should be done */
      for (i=1; *cp == '0' && i < w; i++)
         *cp++ = ' ';               /* supress leading zeroes */
   return;
}
