home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / dos / printer / eprnew / epr.c next >
Encoding:
C/C++ Source or Header  |  1986-02-06  |  30.9 KB  |  1,069 lines

  1. /************************************************************************
  2.  
  3.   File:        epr.c
  4.   Function:    Permits listing of files on Epson printer with control
  5.         over format of listing and printer attributes.
  6.   Version:    1.10-A
  7.   Author:    Robert Sjoberg (SJOBRG@MIT-OZ)
  8.   Implementation notes:
  9.     Originally written to be compiled using Computer Innovations C86
  10.     compiler running under IBM PC-DOS.  Requires external functions
  11.     in "timestmp.c"; should run under CP/M without modification,
  12.     provided compiler and C library has support for stdin, stdout, and
  13.     stderr output (Aztec C certainly does).
  14.     Code written applies to Epson FX-80 printer.
  15.   Revision History:
  16.     V1.00 (09/24/83) - original program.
  17.     V1.01 - put in h= option.
  18.     V1.02 - added new dash options, fixed the way initialization done.
  19.     V1.03 - added -p option to allow change of sheet-fed paper.
  20.     V1.04 - added code to turn off autoskip on paper perforation;
  21.     modified to use timestmp external function.
  22.     V1.05 (11/28/83) - fixed bug in optfile routine and added o= option.
  23.     V1.06 (01/08/84) - Fixed line overflow problem, introduced b= option.
  24.     V1.07 (01/27/84) - Added -1 option, changed -t option to -h and so
  25.     that it only suppresses the header, not top margin.
  26.     V1.08 (05/23/84) - Modified for Unix V7.
  27.     V1.09 (06/27/84) - Added -d switch, modified -s to simply suppress
  28.     printer control codes.  Added "UPMODE" hack.  Sigh.
  29.     V1.10 (07/16/84) - Removed -d switch (it is redundant with o=),
  30.     added c= keyword to select character set.
  31.  
  32.     11 Dec 85  Craig Milo Rogers at USC/ISI (Rogers@USC-ISIB.ARPA)
  33.     Added another debugging line.
  34.  
  35.     Converting to Lattice C (ver. 2.12).
  36.  
  37.     Changed "c" and "firstc" from "char" to "int" in getline() --
  38.     EOF tests weren't working!  Same for "term" in main() and
  39.     "c" in optfile().
  40.  
  41.     Tabs weren't working when international characters were
  42.     enabled, uparrow mode didn't work -- I recoded the whole
  43.     section.
  44.  
  45.     I added code to allow Epson escape sequences to be specified
  46.     in the user's header.  Should be redone more cleanly, though.
  47.  
  48.     The table of printer widths didn't specify the full 137 chars
  49.     possible in condensed (ie, compressed) mode.  It didn't tell
  50.     about enlarged compressed pitch, either.  I rewrote it.
  51.  
  52.     The program didn't allow specification of the full set of Epson
  53.     FX-80 print modes.  I rewrote the code involved.
  54.  
  55.     I added code to allow the header print modes to be assigned the
  56.     same way as the modes for the body.  However, I didn't give
  57.     the header a seperate columns-per-line value.
  58.  
  59.     I changed the default left margin to be 0 (this isn't completely
  60.     arbitrary, my measurements of the margin didn't agree with
  61.     what the program claimed it was doing.
  62.  
  63.   NOTICE    This program is furnished to the public domain.  It may
  64.   ------    not be sold or otherwise commercially distributed for
  65.         profit, in its original form or in any derivational form.
  66.         It may be copied and distributed freely, without charge
  67.         to recipients. If you see fit to improve this program,
  68.         you are welcome to do so PROVIDED you clearly indicate
  69.         the changes in the Revision History above, you include
  70.         your name and handle (network or U.S. Mail address is
  71.         sufficient), and you provide an original copy of this
  72.         program to anyone who receives the modified copy.
  73.  
  74. ************************************************************************/
  75.  
  76. /* Select host operating system by defining appropriate symbol.
  77.  * The default is PCDOS for PC-DOS or MS-DOS or CP/M.  The alternative
  78.  * is UNIX for UNIX V7 (also works for newer UNIX).
  79.  */
  80.  
  81. #ifndef UNIX
  82. #ifndef PCDOS
  83. #define PCDOS
  84. #endif
  85. #endif
  86.  
  87. /* Format of include file names may be changed at different sites */
  88. #include <stdio.h>
  89. #include "timestmp.h"
  90.  
  91. #ifdef UNIX
  92. #include <ctype.h>
  93. #endif
  94.  
  95. #ifndef EOS
  96. #define EOS '\0'    /* end of string character */
  97. #endif
  98.  
  99. /* Define the name of the device attached to printer */
  100.  
  101. #ifdef UNIX
  102. #define Epson "/dev/tty01"    /* name of printer device */
  103. #define UPMODE 2        /* Unix will not output certain control
  104.                  * codes unless in RAW mode, but then it
  105.                  * doesn't like XON/XOFF protocol.  This
  106.                  * gets around some (temporary) problems
  107.                  * the author is having.  It should not
  108.                  * affect use of epr. */
  109. #endif
  110.  
  111. #ifdef PCDOS
  112. #define Epson "PRN:"        /* name of printer device */
  113. #define UPMODE 0
  114. #endif
  115.  
  116. /* CP/M converts its command line to upper case, so use the following
  117.  * to convert critical characters to lower case, for both CP/M and
  118.  * PC-DOS/MS-DOS implementations.
  119.  */
  120. #define casecvt(x) if (isupper(x)) x = tolower(x)
  121.  
  122. /* Debugging information (lots of it) is available by defining the
  123.  * symbol "Dbug".  All diagnostic output is written to stderr.
  124.  */
  125. /*
  126. #define Dbug 1
  127.  */
  128.  
  129. #ifdef Dbug
  130. #define DBG(form) form
  131. FILE    *dfp = stderr;
  132. #else
  133. #define DBG(form)
  134. #endif
  135.  
  136. /* Convenience definitions */
  137. #define BS '\b' /* Ascii 010 */
  138. #define HT '\t' /* Ascii 011 */
  139. #define LF '\n' /* Ascii 012 */
  140. #define FF '\f' /* Ascii 014 */
  141. #define CR '\r' /* Ascii 015 */
  142. #define ESC '\033'
  143. #define SP ' '
  144. #define DEL '\177'
  145. #define LL  0276    /* used to indicate long line on input */
  146. #define EOH 0277    /* marks end of page header sequence */
  147.  
  148. /* Dash Option Variables */
  149. char    altset = 0;    /* -a, 1 if use alternate character set */
  150. char    cchar = 0;    /* -c, 1 if print control chars as ^X */
  151. char    ffeed = 0;    /* -f, 1 if use form feed to end page */
  152. char    nohead = 0;    /* -h, 1 if header should be suppressed */
  153. char    intl = 0;    /* -i, 1 if allow international chars */
  154. char    trunc = 0;    /* -o, 1 if truncate long lines */
  155. char    pagpaus = 0;    /* -p, 1 if special start-of-page pause */
  156. char    nopcc = 0;    /* -s, 1 if suppress printer control codes */
  157. char    nopage1 = 0;    /* -1, 1 if suppress heading on page 1 */
  158. char    space8 = 0;    /* -8, 1 if use 8 lines/inch */
  159.  
  160. /* Output Control and Printer Modes (currently only Epson) */
  161. FILE    *ofp;        /* output file pointer */
  162. char    outfile[50]=Epson; /* name of output file (o= option) */
  163. short    pmode = 0;    /* mode byte for p= option */
  164. short    qmode = 0;    /* mode byte for q= option */
  165.  
  166. #define F_t 010000        /* top (ie, superscript) */
  167. #define F_s  04000        /* subscript */
  168. #define F_p  02000        /* proportional */
  169. #define F_u  01000        /* underlined */
  170. #define F_i   0400        /* italic */
  171. #define F_e    040        /* expanded */
  172. #define F_d    020        /* double strike */
  173. #define F_b    010        /* bold (ie, emphasized) */
  174. #define F_c     04        /* compressed */
  175. #define F_l     01        /* elite */
  176.  
  177. short    cmode = 0;    /* intl character set (defaults to U.S.A.) */
  178. char *cntries[] = {    /* table of country mnemonics */
  179.   "us","fr","ge","uk","de","sw","it","sp","ja",NULL
  180. };
  181.  
  182. /* Input control */
  183. char    **nextf = NULL;        /* list of input file names */
  184. short    nfiles;            /* number of files to process */
  185.  
  186. /* Page formatting */
  187. short    topmar = -1;        /* top margin, in lines */
  188. short    leftmar = -1;        /* left margin, in chars */
  189. short    cpl = -1;        /* chars per line */
  190. short    lpp = -1;        /* number of lines per page before FF */
  191. short    phycpl;            /* number of physical chars per line */
  192. short    phylpp;            /* number of physical lines per page */
  193. short    tabspaces = 8;        /* tabs expand into this many spaces */
  194. short    headpf = 0;        /* flags order of pagen, filename */
  195. short    hlines;            /* number of lines in header */
  196. char    header[80] =        /* holds header string */
  197.     "%d %t  %f Page %p\\n\\n";    /* default header */
  198. char    hformat[130];        /* actual header format string */
  199. char    textline[256];        /* holds input text line */
  200. struct timedata timeinfo;    /* holds time and date info */
  201.  
  202.  
  203. Abort (msg, param)
  204.     char *msg;
  205.     int param;
  206. /*
  207.  * Prints out an error message with optional parameter, then exits
  208.  * with non-zero exit code.
  209.  */
  210. {
  211.     fprintf(stderr,"epr: ");
  212.     fprintf(stderr,msg,param);
  213.     fprintf("\n");
  214.     exit (1);
  215. }
  216.  
  217.  
  218. /* The main sequence following handles input file specs and reading
  219.  * in characters.  Makes line-by-line output.
  220.  * If no file names are specified in the command, then input is
  221.  * from standard input.
  222.  */
  223.  
  224. main (argc,argv)
  225.     int        argc;
  226.     char    **argv;
  227. {
  228.     FILE    *ifp;        /* fp for input file */
  229.     char    *filename;    /* input filename */
  230.     int        pagen;        /* page number */
  231.     int        lines;        /* lines processed per page */
  232.     int        c, l, n, endfile;
  233.     int        term;        /* line terminator */
  234.     char    *tl;
  235.  
  236.     DBG(fprintf(dfp,"About to parse command line.\n");)
  237.     pcl (argc, argv);        /* parse command line options */
  238.  
  239.     DBG(fprintf(dfp,"About to initialize.\n");)
  240.     initialize ();
  241.  
  242.     /* For debugging */
  243. #ifdef Dbug
  244.     fprintf (dfp,"Output is to %s\n",
  245.     (outfile[0] ? outfile : "stdout"));
  246.     fprintf (dfp,"altset %d, cchar %d, pagpaus %d, ffeed %d\n",
  247.     altset,cchar,pagpaus,ffeed);
  248.     fprintf (dfp,"intl %d, trunc %d, nohead %d, space8 %d\n",
  249.     intl,trunc,nohead,space8);
  250.     fprintf (dfp,"pmode %04o, qmode %04o, headpf %d, nopcc %d, nopage1 %d\n",
  251.     pmode,qmode,headpf,nopcc,nopage1);
  252.     fprintf (dfp,"topmar %d, leftmar %d, cpl %d, lpp %d\n",
  253.     topmar,leftmar,cpl,lpp);
  254.     fprintf (dfp,"header [%s]\n",header);
  255.     fprintf (dfp,"hlines %d, hformat [%s]\n",hlines,hformat);
  256.     fprintf (dfp,"nfiles %d",nfiles);
  257.     for (n = 0; n < nfiles; n++) fprintf (dfp,", %s",nextf[n]);
  258.     fprintf (dfp,"\n");
  259. #endif
  260.  
  261.     if (nfiles == 0) {        /* input from stdin */
  262.     ifp = stdin;
  263.     }
  264.     do {            /* repeat for each file */
  265.     if (ifp != stdin) {
  266.         filename = *nextf++;
  267.         nfiles--;
  268.         DBG( fprintf (dfp,"Opening %s ...\n",filename); )
  269.         ifp = fopen (filename, "r");
  270.         if (ifp == NULL) {
  271.         fprintf (stderr,"Couldn't open %s -- skipping\n",filename);
  272.         continue;
  273.         }
  274.     }
  275.     else {
  276.         filename = "stdin";
  277. /*        DBG( fprintf (dfp,"Using stdin ...\n"); )        */
  278.     }
  279.     endfile = 0;
  280.     term = 0;
  281.     for (pagen = 1; ; pagen++) {    /* repeat for each page */
  282.         /* Pause at start of each page, if requested */
  283.         if (pagpaus) {
  284.         printf ("Set up printer and type CR...");
  285.         while (getchar() != LF);
  286.         }
  287.         /* Output any top margin and header */
  288.         lines = 0;
  289.         if (n = topmar) {        /* write top margin */
  290.         sendpcc(pmode);        /* Send body printer modes. */
  291.         lines += n;
  292.         while (n--) putc (LF,ofp);
  293.         }
  294.         if (!(nohead || (pagen == 1 && nopage1))) {
  295.         sendpcc(qmode);        /* Send header printer modes. */
  296.         lines += outhead (pagen, filename);
  297.         }
  298.         sendpcc(pmode);        /* Send body printer modes. */
  299.  
  300.         while (lines < lpp) {
  301.         if (term != LL && term != CR) {
  302.             /* Not continuation of long line or overprint */
  303.             if ((term = getline (ifp, textline)) == EOF) {
  304.             endfile++; break;
  305.             }
  306.             if (term == FF) break;        /* end of page */
  307.             /*
  308.              * Routine putline writes a full line of text to
  309.              * output and returns the line-terminating char.
  310.              * A CR means the line is being overprinted; a LF
  311.              * means true end-of-line; a FF means a lone FF seen;
  312.              * and LL means long line (wrapped).
  313.              */
  314.             tl = textline;
  315. /*            DBG(showtl(tl);)        */
  316.         }
  317.         term = putline (&tl, 0);
  318.         if (term == CR) putc(term,ofp);    /* overprint */
  319.         else {
  320.             putc(LF,ofp);        /* end of line */
  321.             lines++;
  322.         }
  323.         }
  324.         /* Write any trailing blank lines */
  325.         if (ffeed) putc(FF,ofp);
  326.         else while (lines++ < phylpp) putc(LF,ofp);
  327.  
  328.         if (endfile) break;
  329.     }
  330.     if (ifp != stdin) fclose (ifp);
  331.     } while (nfiles > 0);
  332.  
  333.     DBG(fprintf(dfp,"Closing output file\n");)
  334.     fflush (ofp);
  335.     if (ofp != stdout) fclose (ofp);    /* close output file */
  336. }
  337.  
  338. char htext[120];        /* holds formatted header text */
  339.  
  340. outhead (pagen, filename)
  341.     int pagen;
  342.     char *filename;
  343. /*
  344.  * Print the header using the given page number and filename.
  345.  */
  346. {
  347.     int c, lines;
  348.     char *tl;
  349.  
  350.     /* There are two forms for the header format string.  In one, the
  351.      * page number comes before the file name (or just the page number
  352.      * is used), and in the other the file name comes before the page
  353.      * number (or just the file name is used).
  354.      */
  355.     if (headpf) sprintf (htext, hformat, pagen, filename);
  356.     else sprintf (htext, hformat, filename, pagen);
  357.     tl = htext;
  358.     lines = 0;
  359.     do {
  360.     c = putline (&tl, 1);
  361. /*    DBG(fprintf(dfp,"Got %03o\n",c&0377);)   */
  362.     if (c == LF) { putc(c,ofp); lines++; }
  363.     } while (c != EOH);
  364.     return (lines);
  365. }
  366.  
  367. #ifdef Dbug
  368.  
  369. showtl (s)
  370.     register char *s;
  371. /*
  372.  * Display the text line to be output for debugging.
  373.  */
  374. {
  375.     fprintf(dfp,"showtl[");
  376.     while (*s) {
  377.     if (*s < SP || *s == DEL) fprintf(dfp,"^%c",*s ^ 0100);
  378.     else if (*s < DEL) fprintf(dfp,"%c",*s);
  379.     else fprintf(dfp,"\\%03o ",*s & 0377);
  380.     s++;
  381.     }
  382.     fprintf(dfp,"]\n");
  383. }
  384. #endif
  385.  
  386.  
  387. initialize ()
  388.  
  389. /*
  390.  * Obtains current date and time information and places it in a global
  391.  * structure.  Establishes default values for formatting options, validates
  392.  * them, and if output is to printer, outputs appropriate format information.
  393.  * These defaults may be changed to suit individual preferences.
  394.  */
  395. {
  396.     timestmp (&timeinfo);        /* gets date and time */
  397.  
  398.     /* The Epson FX-80 has three basic pitches:  pica (the default),
  399.      * elite, and compressed.  They are mutually exclusive (but may
  400.      * be enabled by seperate commands, in which case elite has
  401.      * priority over compressed, which is over pica).  All three
  402.      * basic pitches may be "expanded", for a total of six pitches.
  403.      *
  404.      * In addition to the six pitches there are other modes (italic,
  405.      * double-strike, emphasized, proportional, and underlined) which
  406.      * may be combined with each other and the six pitches in various
  407.      * patterns (but not arbitrarily).
  408.      *
  409.      * Since the FX-80 has a printing width of 8.0 inches, the following
  410.      * table gives the number of characters per line in each of the
  411.      * six pitches (it doesn't apply when proportional mode is turned on,
  412.      * of course):
  413.      *
  414.      *        Basic Pitch    Expanded?    Chars/Line
  415.      *        -----------    ---------    ----------
  416.      *        pica        no        80
  417.      *        elite        no        96
  418.      *        compressed    no        137
  419.      *        pica        yes        40
  420.      *        elite        yes        48
  421.      *        compressed    yes        68
  422.      */
  423.  
  424.     DBG(fprintf(dfp,"Before respmode, pmode = %o, qmode = %o\n",pmode,qmode);)
  425.     pmode = respmode(pmode);    /* Resolve Epson FX-80 conflicts. */
  426.     qmode = respmode(qmode);
  427.     DBG(fprintf(dfp,"Resolved pmode = %o, qmode = %o\n",pmode,qmode);)
  428.  
  429.     if        (pmode & F_l) {
  430.     phycpl = 96;
  431.     } else if (pmode & F_c) {
  432.     phycpl = 137;
  433.     } else {
  434.     phycpl = 80;
  435.     }
  436.     if (pmode & F_e) phycpl = phycpl / 2;
  437.     DBG(fprintf(dfp,"Physical characters per line = %d\n",phycpl);)
  438.  
  439.     if (leftmar < 0) leftmar = 0;
  440.     if (cpl <= 0) cpl = phycpl - leftmar;
  441.  
  442.     if (nohead) hlines = 0;        /* inhibit header */
  443.     else formatheader ();        /* build actual header string */
  444.  
  445.     phylpp = (space8 ? 88 : 66);    /* number lines per 11 inch page */
  446.     if (topmar < 0) topmar = 0;
  447.     /*
  448.      * lpp counts number of actual text lines, top margin, and number of
  449.      * lines in header (i.e., everything but bottom margin).
  450.      */
  451.     if (lpp <= 0) lpp = phylpp - 5;    /* subtract bottom margin */
  452.     else lpp += hlines + topmar;
  453.  
  454.     /* Open printer or output file.  Unless printer codes have been
  455.      * suppressed, output them to condition the printer.
  456.      */
  457.     if (outfile[0] == EOS) ofp = stdout;    /* use stdout */
  458.     else {
  459.     ofp = fopen (outfile,"w");
  460.     if (ofp == NULL) abort ("Can't open %s for output!",outfile);
  461.     }
  462.  
  463.     if (!nopcc) {            /* control codes, unless inhibited */
  464.     putc(ESC,ofp); putc('O',ofp);    /* turn off auto-skip over
  465.                      * perforation */
  466.     /* Page pause also means deselecting out-of-paper detector */
  467.     putc(ESC,ofp);
  468.     if (pagpaus) putc('8',ofp);
  469.     else putc('9',ofp);
  470.     }
  471. }
  472.  
  473. sendpcc(mode)
  474.     int mode;                /* The mode to use. */
  475. /*
  476.  *    This routine sends the printer control codes that are set before
  477.  * and after each page header (allowing the page header to set different
  478.  * values).
  479.  */
  480. {
  481.     if (!nopcc) {            /* control codes, unless inhibited */
  482.     putc(ESC,ofp);            /* line spacing, 6 or 8 per inch */
  483.     if (space8) putc('0',ofp);
  484.     else putc('2',ofp);
  485.  
  486.     putc(ESC,ofp);            /* alternate character set */
  487.     if (altset) putc('4',ofp);
  488.     else putc('5',ofp);
  489.  
  490.  
  491.     putc(ESC,ofp);            /* "master print mode" */
  492.     putc('!',ofp);
  493.     putc((mode|UPMODE) &0377,ofp);
  494.  
  495.     putc(ESC,ofp);            /* underline mode */
  496.     putc('-',ofp);
  497.     if (mode & F_u) putc('1',ofp);
  498.     else putc('0',ofp);
  499.  
  500.     putc(ESC,ofp);            /* italic mode */
  501.     if (mode & F_i) putc('4',ofp);
  502.     else putc('5',ofp);
  503.  
  504.     putc(ESC,ofp);            /* Super/sub-script mode */
  505.     if        (mode & F_t) {    /* Superscript mode. */
  506.         putc('S',ofp);
  507.         putc('0',ofp);
  508.     } else if (mode & F_s) {    /* Subscript mode. */
  509.         putc('S',ofp);
  510.         putc('1',ofp);
  511.     } else putc('T',ofp);        /* Neither. */
  512.  
  513.     putc(ESC,ofp);            /* proportional mode */
  514.     putc('p',ofp);
  515.     if (mode & F_p) putc('1',ofp);
  516.     else putc('0',ofp);
  517.  
  518.     /* Select international character set */
  519.     putc(ESC,ofp);
  520.     putc('R',ofp);
  521.     putc(cmode,ofp);
  522.     }
  523. }
  524.  
  525.  
  526. formatheader ()
  527.  
  528. /*
  529.  * Creates the header format string from the source string in array header.
  530.  */
  531. {
  532.     char *s, *h, c;
  533.     int v;            /* Builds an ASCII character value. */
  534.  
  535.     s = header;            /* source string */
  536.     h = hformat;        /* final format string */
  537.     hlines = 0;            /* number of newlines in header */
  538.  
  539.     while (c = *s++) {        /* Repeat for each character in header. */
  540.     switch (c) {        /* Dispatch on this character. */
  541.  
  542.     case '%':        /* Percent lead-in. */
  543.         h += percent (h, *s++);
  544.         break;
  545.  
  546.     case '\\':        /* Backslash-quoted char. */
  547.         c = *s++;        /* Get the next char. */
  548.         casecvt(c);        /* Lower its case. */
  549.         switch (c) {    /* Dispatch it. */
  550.         case '\\': *h++ = '\\'; break;
  551.         case 'n': *h++ = '\n'; hlines++; break;
  552.         case 't': *h++ = '\t'; break;
  553.         case 'r': *h++ = '\r'; break;
  554.         case 'b': *h++ = '\b'; break;
  555.         case '0': case '1': case '2': case '3':
  556.         case '4': case '5': case '6': case '7':
  557.         v = c - '0';    /* Ugh!  ASCII-specific. */
  558.         c = *s;
  559.         if (isdigit(c)) {
  560.             s++;
  561.             v = (v * 8) + (c - '0');
  562.             c = *s;
  563.            if (isdigit(c)) {
  564.             s++;
  565.             v = (v * 8) + (c - '0');
  566.            }
  567.         }
  568.         *h++ = v;
  569.         break;
  570.  
  571.         default:        /* Unknown backslashed char. */
  572.         *h++ = '\\';
  573.         *h++ = c;
  574.         }
  575.         break;
  576.  
  577.     default:        /* Neither backslash nor percent. */
  578.         *h++ = c;
  579.     }
  580.     }
  581.     *h++ = '\n'; hlines++;
  582.     *h++ = EOH;
  583.     *h++ = EOS;
  584.     headpf &= 1;        /* save only order flag */
  585. }
  586.  
  587. /*
  588.  * Need to keep track of whether %p and %f appear in header, and if
  589.  * both do, in which order, so we can pick the right sprintf when
  590.  * printing the header.
  591.  */
  592. #define hp_f 02        /* %f seen */
  593. #define hp_p 04        /* %p seen */
  594.  
  595. percent (h, c)
  596.     char c, *h;
  597. /*
  598.  * Process one of the special %x options in the header source, where
  599.  * c gives the option (char following the %).  Returns number of
  600.  * chars inserted in h.
  601.  */
  602. {
  603.     casecvt(c);
  604.     DBG(fprintf(dfp,"percent(%c)\n",c);)
  605.     switch (c) {
  606.       case ' ': *h = ' '; return (1);
  607.       case '%': *h = '%'; return (1);
  608.       case 'd': return (getdate (h));
  609.       case 't': return (gettime (h));
  610.       case 'f':
  611.     if (headpf & hp_f) Abort ("%%f given twice in header string");
  612.     headpf |= hp_f;
  613.     if (headpf & hp_p) headpf |= 1;
  614.     *h++ = '%'; *h++ = 's'; return (2);
  615.       case 'p':
  616.     if (headpf & hp_p) Abort ("%%p given twice in header string");
  617.     headpf |= hp_p;
  618.     if (!(headpf & hp_f)) headpf |= 1;
  619.     *h++ = '%'; *h++ = 'd'; return (2);
  620.       default: return (0);
  621.     }
  622. }
  623.  
  624. gettime (s)
  625.     char *s;
  626. /*
  627.  * Gets current time in the form "hh:mm" and places it in string s.
  628.  * Returns number of chars inserted.
  629.  */
  630. {
  631.     sprintf (s,"%2d:%02d",timeinfo.tim_hr,timeinfo.tim_min);
  632.     return (5);
  633. }
  634.  
  635. getdate (s)
  636.     char *s;
  637. /*
  638.  * Gets date in the form "dd mon yyyy" and puts it into string s.
  639.  * Returns the length of the string inserted.
  640.  */
  641. {
  642.     sprintf (s,"%2d %s %4d",timeinfo.tim_dat,
  643.     timemon[timeinfo.tim_mon],timeinfo.tim_yr);
  644.     return (11);
  645. }
  646.  
  647. getline (ifp, s)
  648.     FILE *ifp;
  649.     register char *s;
  650. /*
  651.  * Fills array s with chars from stream ifp.  Stops when it reads
  652.  * LF, FF, or EOF.  Returns the terminator (one of these three)
  653.  * but inserts a LF as the last character in s (plus a EOS).
  654.  * Does special processing:
  655.  *   CR LF, CR FF, CR EOF all ignore the CR;
  656.  *   FF at end of line with no preceding text gets turned into LF FF;
  657.  *   EOF at end of line with no preceding text gets turned into LF EOF.
  658.  * Returns of FF and EOF happen only when they are read at the
  659.  * beginning of the line.
  660.  */
  661. {
  662.     register int c;
  663.     static int firstc = 0;
  664.     int n;
  665.  
  666.     if (firstc) { c = firstc; firstc = 0; }
  667.     else c = getc(ifp);
  668.  
  669.     /* Skip initial CR's */
  670.     while (c == CR) c = getc(ifp);
  671.  
  672.     if (c == EOF || c == FF) return (c);
  673.  
  674.     n = 0;        /* use as flag if any text chars seen */
  675.     while (c != LF) {
  676. /*    DBG(fprintf(dfp,"GET %3o ",c);)        */
  677.     if (c == CR) {
  678.         c = getc(ifp);
  679.         /* CR followed by terminator is ignored */
  680.         if (c != LF && c != FF && c != EOF) *s++ = CR;
  681.     }
  682.     if (c == FF || c == EOF) {
  683.         /*
  684.          * If no text chars have been processed yet, then return c.
  685.          * Otherwise, simulate LF and save c for next call.
  686.          */
  687.         if (n == 0) return (c);
  688.         firstc = c;
  689.         c = LF;
  690.     }
  691.     if (c != LF) {        /* Normal text char or LF */
  692.         *s++ = c;
  693.         n++;
  694.         c = getc(ifp);        /*next char */
  695.     }
  696.     }
  697.     *s++ = LF;
  698.     *s++ = EOS;                /* for safety's sake */
  699.     return (LF);
  700. }
  701.  
  702.  
  703. putline (sp, sflg)
  704.     char **sp;
  705.     int sflg;                /* !=0 ==> pass specials through. */
  706. /*
  707.  * Process one line's worth of input, from the string pointed to by sp,
  708.  * after printing blanks for the left margin.  Stops when termination
  709.  * condition met:
  710.  *   (1) when LF is seen, returns LF;
  711.  *   (2) when CR is seen, returns CR;
  712.  *   (3) when EOH is seen (only when printing header), returns EOH;
  713.  *   (4) when cpl chars have been printed, returns LF.
  714.  * Updates sp to point to the next char to be read.  Expands tabs into
  715.  * appropriate number of spaces (determined by tabspaces variables).
  716.  */
  717. {
  718.     register char *s;
  719.     int c, chrs, num;
  720.     int lmflg = 0;            /* TRUE ==> printed left margin. */
  721.     int pmode;                /* Printing mode. */
  722. #define PRINTMODE 0            /* Normal printing character. */
  723. #define TABMODE 1            /* Expand tabs. */
  724. #define UPARROWMODE 2            /* Uparrow prefix. */
  725.  
  726.     s = *sp;                /* Local copy of pointer. */
  727.     chrs = 0;                /* Carriage position (no margin). */
  728. /*    DBG(fprintf(dfp,"putline (%o)\n",s);)            */
  729.  
  730.     while (1) {
  731.     /* Dispatch on next character */
  732.     c = *s++ & 0377;        /* Pick up a character. */
  733.     if (c == EOH) {            /* Check for end-of-header. */
  734.         *sp = s;            /* Update the string pointer. */
  735.         return (c);            /* Return the terminator. */
  736.     }
  737.     if (c < SP || c == DEL) {
  738. /*        DBG(fprintf(dfp,"Ctrl %03o at %d\n",c,s - *sp);)    */
  739.         switch (c) {
  740.         case CR:            /* Terminators. */
  741.         case LF:
  742.         *sp = s;        /* Update the string pointer. */
  743.         return (c);        /* Return the terminator. */
  744.  
  745.         case BS:            /* Backspace -- backup if we can. */
  746.         if (chrs) {
  747.             putc(c, ofp);
  748.             chrs--;
  749.         }
  750.         continue;        /* Proceed with the next char. */
  751.  
  752.         case HT:            /* Horizontal tab -- expand it. */
  753.         num = tabspaces - (chrs % tabspaces);
  754.         pmode = TABMODE;
  755.         break;
  756.  
  757.         default:            /* Other control characters. */
  758.         if (intl && validintl(c)) {
  759.             num = 1;        /* Int'l mode -- printing char. */
  760.             pmode = PRINTMODE;
  761.         } else if (sflg) {    /* Special header  -- print it. */
  762.             num = 0;        /* Part of an escape sequence? */
  763.             pmode = PRINTMODE;
  764.         } else if (cchar) {
  765.             num = 2;        /* Uparrow mode -- prefix it. */
  766.             pmode = UPARROWMODE;
  767.         } else continue;    /* Unwanted char -- ignore it. */
  768.         }
  769.     } else {            /* Regular printing char. */
  770.         num = 1;
  771.         pmode = PRINTMODE;
  772.     }
  773.  
  774.     /*
  775.      *    If we get here we know we intend to print something.
  776.      * So, first we gotta print a left margin (if needed).  Next,
  777.      * we check whether we're about to cross the right margin.
  778.      * If all's OK we print whatever we're trying to print.
  779.      */
  780.  
  781.     if (!lmflg) {            /* Output blanks for left margin */
  782.         register int n = leftmar;
  783.         while (n--) putc(SP,ofp);
  784.         lmflg = 1;
  785.     }
  786.  
  787.     chrs += num;            /* update count */
  788.     if (chrs > cpl) {        /* handle overflow line */
  789. /*        DBG(fprintf(dfp,"Line overflow. *sp %o, s %o, diff %d\n",
  790.         *sp,s,s - *sp);)                */
  791.         if (!trunc) {        /* fold line */
  792.         if (c != HT) s--;    /* back up a char */
  793.         *sp = s;        /* Return updated pointer. */
  794.         return (LL);        /* Indicate long line. */
  795.         }
  796.     }
  797.  
  798.     switch (pmode) {        /* Dispatch on special print mode. */
  799.     case PRINTMODE:            /* Normal printing char. */
  800.         putc(c, ofp);
  801.         break;
  802.  
  803.     case TABMODE:            /* Expand tabs. */
  804.         while (num--) putc(SP, ofp);
  805.         break;
  806.  
  807.     case UPARROWMODE:        /* Uparrow-prefix. */
  808.         putc('^', ofp);
  809.         if (c == DEL) {
  810.         putc('?', ofp);
  811.         } else {
  812.         putc(c + '@', ofp);
  813.         }
  814.     }
  815.     }
  816. }
  817.  
  818. validintl (c)
  819.     char c;
  820. /*
  821.  * Returns 1 if char c is one of the international characters for
  822.  * the Epson printer, 0 otherwise.  (An international character is
  823.  * one that is assigned an Ascii code in the range 0 to 31 decimal,
  824.  * but that can be printed if in a special mode--Epson calls this
  825.  * mode "control code selection".)
  826.  */
  827. {
  828.     if (c < 6 || c == 16 || c == 17 || c >= 21 && c <= 31 && c != 27)
  829.     return (1);
  830.     else return (0);
  831. }
  832.  
  833.  
  834. pcl (argc, argv)
  835.     int argc; char **argv;
  836. /*
  837.  * Parse command line and set option variables
  838.  */
  839. {
  840.     int n;
  841.  
  842.     argc--;
  843.     argv++;
  844.     n = getopts (argc, argv);
  845.     DBG(fprintf(dfp,"getopts => %d, argc %d\n",n,argc);)
  846.     argc -= n;
  847.     argv += n;
  848.     if (nfiles = argc) nextf = argv;
  849. }
  850.  
  851. getopts (argc, argv)
  852.     int argc; char **argv;
  853. /*
  854.  * Set option variables according to the command line given in argc/argv.
  855.  * Return number of arguments processed.
  856.  */
  857. {
  858.     int cnt;
  859.     char *p;
  860.  
  861.     cnt = argc;
  862.     while (argc) {
  863.     DBG(fprintf(dfp,"Processing %d %s\n",argc,*argv);)
  864.         p = *argv++; argc--;
  865.     if (*p == '-') {        /* found a switch */
  866.         p++;
  867.         while (*p) dodash (*p++);
  868.     } else if (p[1] == '=') {
  869.         casecvt(p[0]);
  870.         if (p[0] == 'h') {        /* collect header string */
  871.         char delim, *h, c;
  872.         p += 2;
  873.         delim = *p++;
  874.         h = header;
  875.         while ((c = *p++) != delim) {
  876.             if (c == EOS) {
  877.             if (argc == 0)
  878.                  Abort ("No terminating quote in h=");
  879.             p = *argv++; argc--;
  880.             *h++ = SP;    /* end of a substring in the
  881.                      * command line before end of
  882.                      * the end of the header is
  883.                      * turned into a blank.
  884.                      */
  885.             }
  886.             else *h++ = c;
  887.         }
  888.         *h++ = EOS;
  889.         }
  890.         else doequal (p);        /* found a keyword */
  891.     } else {
  892.         argc++; argv--;
  893.         break;            /* file names follow */
  894.     }
  895.     }
  896.     return (cnt - argc);
  897. }
  898.  
  899.  
  900. dodash (c)
  901.     char c;
  902. /*
  903.  *  Process dash option given by c
  904.  */
  905. {
  906.     casecvt(c);
  907.     DBG(fprintf(dfp,"dodash(%c)\n",c);)
  908.     switch (c) {
  909.       case 'a': altset++; break;
  910.       case 'c': cchar++; break;
  911.       case 'f': ffeed++; break;
  912.       case 'h': nohead++; break;
  913.       case 'i': intl++; break;
  914.       case 'o': trunc++; break;
  915.       case 'p': pagpaus++; break;
  916.       case 's': nopcc++; break;
  917.       case '1': nopage1++; break;
  918.       case '8': space8++; break;
  919.       default:
  920.     Abort ("unrecognized switch option -%c",c);
  921.     }
  922. }
  923.  
  924.  
  925. doequal (s)
  926.     char *s;
  927. /*
  928.  * Process a keyword=value option given by string s.  Notice that there
  929.  * is no checking on the bounds of the values accepted by the
  930.  * parameters, although there certainly ought to be.
  931.  */
  932. {
  933.     char c;
  934.  
  935.     c = *s;
  936.     casecvt(c);
  937.     s += 2;
  938.     switch (c) {
  939.       case 'b': tabspaces = atoi(s); break;
  940.       case 'c': cmode = country(s); break;
  941.       case 'f': optfile (s); break;
  942.       case 'l': lpp = atoi(s); break;
  943.       case 'm': leftmar = atoi(s); break;
  944.       case 'o': strcpy(outfile,s); break;
  945.       case 'p': pmode = getpmode (s); break;
  946.       case 'q': qmode = getpmode (s); break;
  947.       case 't': topmar = atoi(s); break;
  948.       case 'w': cpl = atoi(s); break;
  949.       default:
  950.     Abort ("unrecognized keyword %c=",c);
  951.     }
  952. }
  953.  
  954. country (s)
  955.     char *s;
  956. /*
  957.  * Matches string s against the list of country mnemonics and, if a
  958.  * match is found, returns the corresponding country code.  Otherwise,
  959.  * returns 0 (default for USA).  Only the first two characters of s
  960.  * are significant.
  961.  */
  962. {
  963.     int n;
  964.     char **p;
  965.  
  966.     casecvt(s[0]); casecvt(s[1]);    /* make lower case */
  967.     for (n = 0, p = cntries; *p; n++, p++) {
  968.     if (s[0] == (*p)[0] && s[1] == (*p)[1]) break;
  969.     }
  970.     if (*p == NULL) n = 0;
  971.     return (n);
  972. }
  973.  
  974.  
  975. getpmode (s)
  976.     char *s;
  977. /*
  978.  * Constructs the printer mode from the string s and returns it.
  979.  */
  980. {
  981.     int n;
  982.     char c;
  983.  
  984.     n = 0;
  985.     while (*s) {
  986.     c = *s;
  987.     casecvt(c);
  988.     switch (c) {
  989.       case 'b': n |= F_b; break;    /* bold (ie, emphasized) */
  990.       case 'c': n |= F_c; break;    /* condensed */
  991.       case 'd': n |= F_d; break;    /* double strike */
  992.       case 'e': n |= F_e; break;    /* enlarged */
  993.       case 'i': n |= F_i; break;    /* italic */
  994.       case 'l': n |= F_l; break;    /* elite */
  995.       case 'p': n |= F_p; break;    /* proportional */
  996.       case 's': n |= F_s; break;    /* subscript */
  997.       case 't': n |= F_t; break;    /* top (superscript) */
  998.       case 'u': n |= F_u; break;    /* underlined */
  999.       default:
  1000.         Abort ("invalid printer mode specifier %c", *s);
  1001.     }
  1002.     s++;
  1003.     }
  1004.     return (n);
  1005. }
  1006.  
  1007. respmode(n)
  1008.     int n;                /* The user's mode spec. */
  1009. /*
  1010.  *    Resolve a printer mode spec by applying the Epson FX-80
  1011.  * priority rules.  In the descriptions below, "->" means overrides.
  1012.  */
  1013. {
  1014.     /* Elite -> Proportional -> Emphasized -> Compressed */
  1015.     if (n & F_l) n &= ~(F_p|F_b|F_c);
  1016.     if (n & F_p) n &= ~(F_b|F_c);
  1017.     if (n & F_b) n &= ~(F_c);
  1018.  
  1019.     /* Proportional -> (Superscript, Subscript, Double Strike) */
  1020.     if (n & F_p) n &= ~(F_t|F_s|F_d);
  1021.  
  1022.     /*
  1023.      * Superscript and Subscript are exclusive.  This program (not
  1024.      * the Epson FX-80) will give Subscript priority.
  1025.      */
  1026.     if (n & F_s) n &= ~(F_t);
  1027.  
  1028.     /* Superscript and Subscript imply Double Strike */
  1029.     if (n & (F_t|F_s)) n |= F_d;
  1030.  
  1031.     return (n);                /* Return the resolved mode. */
  1032. }
  1033.  
  1034. optfile (filnam)
  1035.     char *filnam;
  1036. /*
  1037.  * Found f=filnam option.  Take options from given file, make them
  1038.  * appear like command line (build an argc and argv vector), and call
  1039.  * getopts on the result.
  1040.  */
  1041. {
  1042.     FILE *fp;
  1043.     char buf[160], *p, *argv[16];
  1044.     int c;
  1045.     int argc;
  1046.  
  1047.     fp = fopen (filnam, "r");
  1048.     if (fp == NULL) {
  1049.     fprintf (stderr, "File %s not found. ",filnam);
  1050.     Abort ("Cannot process f= option.");
  1051.     }
  1052.     DBG(fprintf(dfp,"Indirect file %s\n",filnam);)
  1053.     for (p = buf, argc = 0, c = 0; c != EOF; ) {
  1054.     do c = getc (fp); while (isspace(c));    /* skip blanks */
  1055.     if (c == EOF) break;
  1056.     argv[argc++] = p;        /* next argument */
  1057.     do {                /* save argument */
  1058.         *p++ = c;
  1059.         c = getc(fp);
  1060.     } while (!isspace(c) && c != EOF);
  1061.     *p++ = EOS;
  1062.     DBG(fprintf(dfp,"Arg %d is '%s'\n",argc,argv[argc-1]);)
  1063.     if (c == EOF) break;
  1064.     }
  1065.     DBG(fprintf(dfp,"EOF on optfile\n");)
  1066.     fclose (fp);
  1067.     getopts (argc, argv);
  1068. }
  1069.