[comp.sources.misc] v08i075: cz text to PostScript system, part 11 of 14

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (10/01/89)

Posting-number: Volume 8, Issue 75
Submitted-by: howard@dahlbeck.ericsson.se (Howard Gayle)
Archive-name: cz/part11

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 11 (of 14)."
# Contents:  cz0.c
# Wrapped by howard@dahlbeck on Mon Sep 25 07:15:24 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cz0.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cz0.c'\"
else
echo shar: Extracting \"'cz0.c'\" \(54952 characters\)
sed "s/^X//" >'cz0.c' <<'END_OF_FILE'
X/*
X * cz0 - Convert text in any octet-based character set into PostScript.
X */
X
X#include <stdio.h>
X#include <howard/port.h>
X#include <howard/version.h>
X#include <howard/usage.h>
X
XMAINVER ("@(#)$Header: cz0.c,v 2.25 89/08/21 10:54:56 howard Exp $");
XUSAGE ("[-o file] {-command argument}");
X
X#include <ctype.h>
X#include <string.h>
X#include <time.h>
X#include <howard/a2.h>
X#include <howard/malf.h>
X#include <howard/registers.i>
X#include "FREEZE.i"
X#include "cz.h"
X#include "ps-abbrev.i"
X#include "cz0.h"
X#include "state0.i"
X
X/* a2ocol - convert string to output column number */
X
XPRIVATE ushrtT a2ocol (s, n)
XbStrT s; /* Input string.*/
XbStrT n; /* Field name.*/
X
X/* Function:
X *    s is a string representation of an output column number, e.g.
X *    FoldFirst.  It is either "Inf" for the maximum output column,
X *    or an integer numeric literal.
X * Algorithm:
X *    
X * Returns:
X *    
X * Notes:
X *    
X */
X{
Xreturn (bStrEQ (S("Inf"), s) ? MOCOL : ADA_US (s, n, 0, MOCOL));
X}
X
X/* addFnt - add a font to fonts[] if it's not already there */
X
XPRIVATE void addFnt (f, s)
XR2 bStrT f; /* Font name.*/
X   boolT s; /* A symbol font.*/
X
X/* Function:
X *    If font f is not already in the list of fonts used, add it.
X *    If it *is* already in the list, make sure its stored symbol font
X *    status is the same as s.
X * Algorithm:
X *    Linear search.
X * Returns:
X *    
X * Notes:
X *    1) It's necessary to distinguish symbol fonts from non-symbol
X *       fonts because symbol fonts must not be reencoded.
X */
X{
XR1 fontT *fp; /* Steps through fonts[].*/
X
Xfor (fp = fonts; (fp != fontP) && !bStrEQ (f, fp->fntName); ++fp)
X   ;
Xif (fp == fontP)
X   {
X   if (fp == &fonts[MFONTS]) malf1 (eMFonts, MFONTS);
X   fp->fntName = f;
X   fp->fntSymb = s;
X   ++fontP;
X   }
Xelse if (fp->fntSymb != s)
X   malf1 (eSymFnt, f);
X}
X
X/* addso - add one state-octet pair to state table */
X
XPRIVATE void addso (sos, pss)
XR4 bStrT sos; /* State-octet pair as a string.*/
XR5 bStrT pss; /* Corresponding PostScript string.*/
X
X/* Function:
X *    sos is a string representing a state-octet pair.
X *    pss is a string containing the text to emit for that pair,
X *    and optionally a new state at the end.  addso() enters
X *    this into the state table, building new parts if necessary.
X * Algorithm:
X *    Convert state and octet to numbers.  Check pss for bad escapes.
X *    If there is no entry in state table for s, make one.
X *    If there is a next state at the end of pss, convert it to a number.
X *    Store pss and next state for this pair.
X * Returns:
X *    
X * Notes:
X *    1) Should probably convert pss to nextc() output format here.
X */
X{
XR6 unsigned  ns = 0; /* Next state.*/
XR7 unsigned  o;      /* Octet.*/
XR1 bStrT     p1;     /* General purpose.*/
XR8 unsigned  s;      /* State.*/
XR2 soT      *sop1;   /* Current soT.*/
XR3 soT      *sop2;   /* End of new stateT.*/
XR9 stateT   *sp;     /* Points to current stateT.*/
X
Xp1 = bStrChr (sos, SOSEP);
Xif (NULBSTR == p1) malf1 (eSOSep, SOSEP, sos, pss);
Xs = mra2u (sos, p1, FALSE, S("State"), (unsigned) 0, (unsigned) MSTATE,
X           (bStrT *) NULL);
Xo = mra2u (1 + p1, NULBSTR, FALSE, S("Octet"), (unsigned) 0, (unsigned) 255,
X           (bStrT *) NULL);
Xfor (p1 = pss; NULBSTR != (p1 = bStrChr (p1, E_ESC)); ++p1)
X   {
X   ++p1;
X   if (T_RES == ncMap[B(*p1)]) malf1 (eEsc, E_ESC, B(*p1), sos, pss);
X   }
Xsp = states[s];
Xif (((stateT *) NULL) == sp)
X   {
X   sp = (stateT *) mcalloc (1, sizeof (stateT), "State table");
X   sop1 = (soT *) sp;
X   sop2 = sop1 + 256;
X   for (; sop1 != sop2; ++sop1)
X      sop1->so_ps = NULBSTR;
X   states[s] = sp;
X   }
Xp1 = bStrRChr (pss, E_ESC);
Xif ((NULBSTR != p1) && (E_NEXTS == B(p1[1])))
X   {
X   ns = mra2u (2 + p1, NULBSTR, FALSE, S("Next state"), (unsigned) 0,
X               (unsigned) MSTATE, (bStrT *) NULL);
X   *p1 = EOS;
X   }
Xif (strlen (pss) > MPSSTR) malf1 (eBigPS, sos, MPSSTR, pss);
Xsop1 = ((soT *) sp) + o;
Xsop1->so_nxt = ns;
Xsop1->so_ps = mcpstr (pss);
Xif (DB(D_OCT))
X   FPRINTF (stderr, "State %u Octet %c 8#%o# (%s%c%c%u)\n",
X            s, o, o, sop1->so_ps, E_ESC, E_NEXTS, sop1->so_nxt);
X}
X
X/* cmd - execute one command */
X
XPRIVATE void cmd (cp, ap, fn, ln)
XR1 bStrT    cp; /* Command.*/
X   bStrT    ap; /* Argument.*/
X   cStrT    fn; /* File name.*/
X   unsigned ln; /* Line number in file.*/
X
X/* Function:
X *    Execute the given command with the given argument.  On error,
X *    write an error message, using the file name and line number.
X * Algorithm:
X *    Linear search for command name.
X * Notes:
X *    1) Should use perfect hashing.
X */
X{
XR2 bStrT p; /* General putpose.*/
X
Xif (DB(D_CMD)) FPRINTF (stderr, "%s: %u: %s %s\n", fn, ln, cp, ap);
Xif (NULBSTR != (p = prefix (S("AutoColumn"), cp)))
X   param0.AutoCol[ADA_US (p, "Number of columns", 1, MPAGCOL)] =
X      a2ocol (ap, S("Maximum number of output columns"));
Xelse if (bStrEQ (S("AutoFile"), cp))
X   param0.AutoFil = ms2bool (ap, cp);
Xelse if (bStrEQ (S("AutoLandscape"), cp))
X   param0.AutoLnd = a2ocol (ap, cp);
Xelse if (bStrEQ (S("BodySize"), cp))
X   param0.BodySiz = mra2bp (ap, cp, 1.0, 1000.0);
Xelse if (bStrEQ (S("BottomMarginLandscape"), cp))
X   param0.BMargL = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("BottomMarginPortrait"), cp))
X   param0.BMargP = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("BottomSkip"), cp))
X   param0.BSkip = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("CommandFile"), cp))
X   {
X   if (bStrEQ (ap, S("-"))) readrc (stdin, sStdin); else readpth (ap);
X   }
Xelse if (bStrEQ (S("Columns"), cp))
X   param0.Columns = ADA_US (ap, cp, 1, MPAGCOL);
Xelse if (bStrEQ (S("ColumnSeparation"), cp))
X   param0.ColSep = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("Debug"), cp))
X   Debug = ADA_INT (ap, cp, 0, 9);
Xelse if (bStrEQ (S("EndControlD"), cp))
X   EndCtlD = ms2bool (ap, cp);
Xelse if (bStrEQ (S("FixedWidthBodyFont"), cp))
X   param0.FWFont = mcpstr (ap);
Xelse if (bStrEQ (S("File"), cp))
X   {
X   if (filp == &fil[MFILES + 1]) malf1 (eMFiles, MFILES);
X   filp->filName = mcpstr (ap);
X   filp->filPara = param0;
X   ++filp;
X   }
Xelse if (bStrEQ (S("FixedWidth"), cp))
X   param0.FixWid = ms2bool (ap, cp);
Xelse if (bStrEQ (S("FoldFirst"), cp))
X   param0.FoldFst = a2ocol (ap, cp);
Xelse if (bStrEQ (S("FoldIndent"), cp))
X   param0.FoldInd = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("FoldRest"), cp))
X   param0.FoldRst = a2ocol (ap, cp);
Xelse if (bStrEQ (S("FooterFont"), cp))
X   param0.FootFnt = mcpstr (ap);
Xelse if (bStrEQ (S("FooterHeight"), cp))
X   param0.FootHt = mra2bp (ap, cp, 0.0, 72.0);
Xelse if (bStrEQ (S("Header"), cp))
X   filp->filHdr = mcpstr (ap);
Xelse if (bStrEQ (S("HeaderFont"), cp))
X   param0.HeadFnt = mcpstr (ap);
Xelse if (bStrEQ (S("HeaderHeight"), cp))
X   param0.HeadHt = mra2bp (ap, cp, 0.0, 1000.0);
Xelse if (bStrEQ (S("LandscapeRotation"), cp))
X   {
X   param0.LandRot = ADA_INT (ap, cp, -90, 90);
X   if ((-90 != param0.LandRot) && (90 != param0.LandRot)) malf1 (eRot);
X   }
Xelse if (bStrEQ (S("LeftMarginLandscape"), cp))
X   param0.LMargL = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("LeftMarginPortrait"), cp))
X   param0.LMargP = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("LineNumberFont"), cp))
X   param0.LNFnt = mcpstr (ap);
Xelse if (bStrEQ (S("LineNumberHeight"), cp))
X   param0.LNHt = mra2bp (ap, cp, 0.0, 72.0);
Xelse if (bStrEQ (S("LineNumberMultiple"), cp))
X   param0.LNMult = ADA_US (ap, cp, 0, 1000);
Xelse if (bStrEQ (S("LineNumberWidth"), cp))
X   param0.LNWid = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (NULBSTR != (p = prefix (S("Octet"), cp)))
X   addso (p, ap);
Xelse if (bStrEQ (S("PageHeight"), cp))
X   param0.PageHt = mra2bp (ap, cp, MM(100.0), MM(1000.0));
Xelse if (bStrEQ (S("PageWidth"), cp))
X   param0.PageWid = mra2bp (ap, cp, MM(100.0), MM(1000.0));
Xelse if (bStrEQ (S("PostScript"), cp))
X   FPRINTF (psos, "%s\n", ap);
Xelse if (bStrEQ (S("Reverse"), cp))
X   Reverse = ms2bool (ap, cp);
Xelse if (bStrEQ (S("RightMarginLandscape"), cp))
X   param0.RMargL = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("RightMarginPortrait"), cp))
X   param0.RMargP = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("Spacing"), cp))
X   param0.Spacing = mra2d (ap, NULBSTR, TRUE, cp, 0.5, 10.0, (bStrT *) NULL);
Xelse if (bStrEQ (S("SymbolFont"), cp))
X   param0.SymbFnt = mcpstr (ap);
Xelse if (bStrEQ (S("TabWidth"), cp))
X   param0.TabWid = ADA_US (ap, cp, 1, 80);
Xelse if (bStrEQ (S("TextVertical"), cp))
X   param0.TxtVert = ms2bool (ap, cp);
Xelse if (bStrEQ (S("TopMarginLandscape"), cp))
X   param0.TMargL = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("TopMarginPortrait"), cp))
X   param0.TMargP = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (bStrEQ (S("TopSkip"), cp))
X   param0.TSkip = mra2bp (ap, cp, 0.0, MM(100.0));
Xelse if (NULBSTR != (p = prefix (S("Undefine"), cp)))
X   undef (p, ap);
Xelse if (bStrEQ (S("VariableWidthBodyFont"), cp))
X   param0.VWFont = mcpstr (ap);
Xelse if (bStrEQ (S("XAdjust"), cp))
X   param0.XAdjust = mra2bp (ap, cp, MM(-50.0), MM(50.0));
Xelse if (bStrEQ (S("YAdjust"), cp))
X   param0.YAdjust = mra2bp (ap, cp, MM(-50.0), MM(50.0));
Xelse malf1 (eCmd, fn, ln, cp, ap);
X}
X
X/* cpStdin - copy standard input to temporary file */
X
XPRIVATE void cpStdin (fp)
XR1 filT *fp; /* Points to data for file.*/
X
X/* Function:
X *    This is the result of a "File -" command.  The standard input
X *    must be copied to a temporary file because cz needs to read it
X *    twice.  If there was no Header command, make the header
X *    "Standard Input."
X * Algorithm:
X *    Make temporary file.  Open it for append.  Call mfcopy().
X *    Close files.
X * Returns:
X *    
X * Notes:
X *    1) Don't exit on a read error, just write a warning.
X */
X{
XR2 streamT tos; /* Temporary file output stream.*/
Xstatic char  tfnb2[] = "/tmp/czsXXXXXX"; /* Temporary file name buffer.*/
X
Xif (NULBSTR == fp->filHdr) fp->filHdr = sStdin;
Xfp->filName = (bStrT) mktemp (tfnb2);
Xtos = mfopen (fp->filName, "w+");
Xfp->filTmp = TRUE;
Xmfcopy (stdin, 0, sStdin, tos, 1, fp->filName);
Xif (fclose (stdin)) malf0 (eClose, sStdin);
Xmfclose (tos, fp->filName);
Xif (DB(D_FILE)) FPRINTF (stderr, "stdin -> %s\n", fp->filName);
X}
X
X/* cvcol - try to output one page column */
X
XPRIVATE int cvcol (fp, pc, clm, moc)
XR6 filT     *fp;  /* Points to data for file to convert.*/
XR7 unsigned  pc;  /* Page column number.*/
XR9 unsigned  clm; /* Max number of output lines in column.*/
X   unsigned  moc; /* Max output column.*/
X
X/* Function:
X *    Attempt to emit one output column.  The output goes to a temporary
X *    file, so if it fails, because of line overflow, the caller can
X *    fseek back to the beginning of the failed attempt.
X * Algorithm:
X *    Loop until failure or until a complete column is output.
X *    Call cvline() to attempt to convert each line, and switch
X *    on the return code.  On success, end of column, end of page,
X *    end of file in middle of line, or success (folded line),
X *    print the line, using the appropriate PostScript functions
X *    at the beginning and end of the line.  One end of file at the
X *    beginning of a line or line overflow,  don't print anything, just
X *    return the appropriate code.
X * Returns:
X *    SUCCESS or R_* code.
X * Notes:
X *    1) Variable s is used for for loop control (when negative) and
X *       as return code (when non-negative).  This is a common trick.
X *    2) linNum is incremented at the *end* of an input line, so eln
X *       is 1 + linNum for folded lines.
X */
X{
XR8 unsigned cl = 1;  /* Output line number within column.*/
XR5 unsigned eln;     /* Effective line number.*/
XR4 boolT    p;       /* Print this output line.*/
XR2 bStrT    pnb;     /* PostScript procedure for beginning of line.*/
XR3 bStrT    pne;     /* PostScript procedure for end of line.*/
XR1 int      s = -1;  /* Return code.*/
X   char     lns[20]; /* Line number string.*/
X
XFPRINTF (psts, "(%u)%u %s\n", pc, pc, PCOLB);
Xfor (; s < 0; ++cl)
X   {
X   outBufP = outBuf;
X   p = FALSE;
X   psLinL = 0;
X   switch (s = cvline (fp, moc))
X      {
X      case SUCCESS:
X      case R_COLE:
X      case R_PAGEE:
X      case R_EOFM:
X         if (linCont)
X            {
X            pnb = PLLB;
X            pne = PLLE;
X            linCont = FALSE;
X            }
X         else
X            {
X            pnb = PSLB;
X            pne = PSLE;
X            }
X         if ((SUCCESS == s) && (cl != clm)) s = -1;
X         p = TRUE;
X         break;
X      case R_CONT:
X         if (linCont)
X            {
X            pnb = PMLB;
X            pne = PMLE;
X            }
X         else
X            {
X            pnb = PFLB;
X            pne = PFLE;
X            linCont = TRUE;
X            }
X         s = ((cl == clm) ? SUCCESS : -1);
X         p = TRUE;
X         break;
X      case R_EOFB:
X         if (1 != cl) s = R_EOFM;
X         break;
X      case R_OVFL:
X         break;
X      default:
X         malf1 (eIntern, "cvcol", __LINE__);
X         break;
X      }
X   if (p)
X      {
X      eln = linNum;
X      if (linCont) ++eln;
X      PSPUTC (EOS);
X      if ((0 != fp->filPara.LNMult) &&
X          ((0 == (eln % fp->filPara.LNMult)) || (eln==lines)))
X         SPRINTF (lns, "(%u)", eln);
X      else
X         STRCPY (lns, "()");
X      FPRINTF (psts, "%s%u %s\n%s%s%u %s\n", lns, eln, pnb,
X            outBuf, lns, eln, pne);
X      }
X   }
XFPRINTF (psts, "(%u)%u %s\n", pc, pc, PCOLE);
Xif (DB(D_COL)) FPRINTF (stderr, "cvcol (%u, %u, %u) = %d\n", pc, clm, moc, s);
Xreturn (s);
X}
X
X/* cvfile - convert one file into PostScript */
X
XPRIVATE void cvfile (fp)
XR2 filT *fp; /* Points to data for file to convert.*/
X
X/* Function:
X *    Convert the file to PostScript.
X * Algorithm:
X *    If the file is standard input, copy it to a temporary file.
X *    Call doTop() to generate the output that is the same for every page.
X *    Open the file and call preScan() to compute the starting number
X *    of page columns for each page (pc0), and starting orientation (por0).
X *    preScan also counts input lines.  Open a temporary output file.
X *    The outer (mf) loop is executed once for each page.
X *    The inner (mp) loop is executed once for each page attempt.
X *    Start with pc0 and por0.  Compute the maximum allowed output
X *    columns for this page attempt (moc) from pc, por, and the
X *    AutoFile, AutoLandscape, and AutoColumn data.  Call cvpage()
X *    to make the try.  On overflow, first try decreasing pc.  If pc
X *    is 1, try going to landscape.  Seek back to the beginning of the
X *    page and try again.  At the end of the outer loop, step through
X *    each page and call putPage() to write it for real.
X * Notes:
X *    
X */
X{
XR9     streamT   f;                   /* Input stream.*/
XR10    long      isp;                 /* Offset in input stream.*/
XR8     boolT     mf;                  /* More of file to read.*/
XR3     unsigned  moc;                 /* Max number of output columns to try.*/
XR1     boolT     mp;                  /* More of page to convert.*/
X       ncStatT   ncs;                 /* Save nc state at beginning of page.*/
XR11    long      osp;                 /* Offset in output stream.*/
XR14    unsigned  pages;               /* Pages of output for this file.*/
XR5     unsigned  pc;                  /* Current number of page columns.*/
X       unsigned  pc0;                 /* Initial number of page columns.*/
XR4     boolT     por;                 /* Current page in portrait mode.*/
X       boolT     por0;                /* Initially portrait mode.*/
XR7     unsigned  pn;                  /* Page number.*/
XR6     int       s;                   /* Return code.*/
XR12    unsigned  sln;                 /* Save line number.*/
X       pageT     pageBy[MPAGES + 2];  /* Offset in psts of each page.*/
XR13    pageT    *pp;                  /* Steps through pageBy[].*/
Xstatic char      tfnb1[] = "/tmp/czpXXXXXX"; /* Temporary file name buffer.*/
X
Xif (DB(D_FILE)) FPRINTF (stderr, "cvfile %s\n", fp->filName);
Xif (bStrEQ (S("-"), fp->filName)) cpStdin (fp);
Xif (!doTop (fp)) return;
Xf = fopen (fp->filName, "r");
Xif (NULSTRM == f)
X   {
X   malf0 (eOpen, fp->filName);
X   return;
X   }
XpreScan (fp, f, &pc0, &por0);
Xif (0 == pc0)
X   {
X   malf0 (eEmpty, fp->filName);
X   if (fclose (f)) malf0 (eClose, fp->filName);
X   if (fp->filTmp)
X      {
X      if (unlink (fp->filName)) malf0 (eUnlink, fp->filName);
X      }
X   return;
X   }
Xif (NULCSTR == pstfn) pstfn = mktemp (tfnb1);
Xpsts = mfopen (pstfn, "w+");
Xnc = nc0;
Xnc.ncIStrm = f;
XlinNum = 0;
Xpn = 0;
Xpp = &pageBy[1];
Xfor (mf = TRUE; mf;)
X   {
X   ++pn;
X   pc = pc0;
X   por = por0;
X   isp = ftell (f);
X   ncs = nc;
X   osp = ftell (psts);
X   sln = linNum;
X   for (mp = TRUE; mp;)
X      {
X      if (fp->filPara.AutoFil)
X         moc = MOCOL;
X      else if (por && (1 == pc))
X         moc = fp->filPara.AutoLnd;
X      else if (1 != pc)
X         moc = fp->filPara.AutoCol[pc];
X      else
X         moc = MOCOL;
X      s = cvpage (fp, pc, por, moc);
X      switch (s)
X         {
X         case R_EOFB:
X            --pn;
X            /* Falls through.*/
X         case R_EOFM:
X            mf = FALSE;
X            /* Falls through.*/
X         case SUCCESS:
X         case R_PAGEE:
X            mp = FALSE;
X            break;
X         case R_OVFL:
X            if (por && (1 == pc))
X               por = FALSE;
X            else if (1 != pc)
X               --pc;
X            else
X               malf1 (eIntern, "cvfile", __LINE__);
X            mfseek (f, isp, 0, fp->filName);
X            mfflush (psts, pstfn);
X            mfseek (psts, osp, 0, pstfn);
X            linNum = sln;
X            nc = ncs;
X            break;
X         default:
X            malf1 (eIntern, "cvfile", __LINE__);
X            break;
X         }
X      }
X   if (pn > MPAGES) malf1 (eMPages, fp->filName, MPAGES);
X   if (R_EOFB != s)
X      {
X      mfflush (psts, pstfn);
X      pp->pgPos = osp;
X      pp->pgCols = pc;
X      pp->pgPor = por;
X      ++pp;
X      if (DB(D_PAGE))
X         FPRINTF (stderr, "page %u: %u cols %s\n", pn,
X                  pc, por ? "portrait" : "landscape");
X      }
X   }
Xif (ferror (f)) malf0 (eReadL, fp->filName, linNum);
Xif (fclose (f)) malf0 (eClose, fp->filName);
Xif (fp->filTmp)
X   {
X   if (unlink (fp->filName)) malf0 (eUnlink, fp->filName);
X   }
Xpages = pn;
Xif (Reverse)
X   {
X   pp->pgPos = ftell (psts);
X   for (--pp; pp != pageBy; --pp)
X      putPage (fp, pp, pn--, pages, pp[1].pgPos - pp->pgPos);
X   }
Xelse
X   {
X   pn = 1;
X   pp->pgPos = ftell (psts);
X   for (pp = &pageBy[1]; pn <= pages; ++pp)
X      putPage (fp, pp, pn++, pages, pp[1].pgPos - pp->pgPos);
X   }
Xif (fclose (psts)) malf0 (eClose, pstfn);
XtotPag += pages;
X}
X
X/* cvline - Try to convert one output line to PostScript */
X
XPRIVATE int cvline (fp, moc)
XR9 filT     *fp;  /* Points to data for file to convert.*/
XR8 unsigned  moc; /* Max output column.*/
X
X/* Function:
X *    Attempt to convert one output line.  The output goes into a buffer;
X *    it is not written onto the temporary file here.  This is because
X *    the output must be preceded by a call to the appropriate PostScript
X *    procedure, but which one is appropriate depends on whether the line
X *    is folded.
X * Algorithm:
X *    Loop.  Call nextc() to get next input character and convert it
X *    to nextc() output form, which is an array of unsigned shorts.
X *    Elements less than 256 are literal bytes to be output; other
X *    elements are from the T_* codes, with a bias of 256.
X *    (T_END + 256) marks the end of the array.
X *
X *    Set endF if this character ends the line in some way.
X *    Set tabF is this character is a tab.  Compute the number of
X *    spaces.  (The -1 is because nextc() has already incremented
X *    nc.ncICol and oCol.)  Check for output column overflow and folding.
X *    Step through each element in the array.  Put literal bytes into
X *    the buffer and switch on the T_* codes.  If a PostScript line
X *    could become longer than MPSLINE characters, force the end of a
X *    string.
X * Returns:
X *    Return code.
X * Notes:
X *    1) The ENDSTR macro assumes that PSPUTC is one character.
X *    2) Warning messages for undefined characters have already
X *       been printed in the preScan() pass.
X */
X{
X/* If outputting a PostScript string, end it:*/
X#define ENDSTR {if(ins){PSPUTC(')');PSPUTC(*PSHOW);PSPUTC('\n');ins=FALSE;}}
XR2  ushrtT   *pp;          /* Returned by nextc().*/
XR1  ushrtT   *pp2;         /* Steps through pp[].*/
XR3  boolT     endF;        /* T_{LINE,COLE,PAGEE,EOF} in pp[].*/
XR4  boolT     tabF;        /* T_TAB in pp[].*/
XR5  boolT     ins = FALSE; /* Flag set when PS string being output.*/
XR6  int       s = -1;      /* Loop control and return code.*/
XR7  unsigned  spcs;        /* Tab expands into this many spaces.*/
XR10 int       i;           /* Count octets.*/
X
XoCol = 0;
Xwhile (s < 0)
X   {
X   pp = nextc();
X   endF = FALSE;
X   tabF = FALSE;
X   for (pp2 = pp; (T_END + 256) != *pp2; ++pp2)
X      {
X      if (((T_LINEE + 256) == *pp2) || ((T_COLE + 256) == *pp2) ||
X          ((T_PAGEE + 256) == *pp2) || ((T_EOF + 256) == *pp2))
X         endF = TRUE;
X      else if ((T_TAB + 256) == *pp2)
X         tabF = TRUE;
X      }
X   if (!tabF)
X      spcs = 1;
X   else
X      {
X      spcs = fp->filPara.TabWid - ((nc.ncICol - 1) % fp->filPara.TabWid);
X      nc.ncICol += spcs - 1;
X      oCol += spcs - 1;
X      }
X   if (!endF)
X      {
X      if (oCol >= moc)
X         s = R_OVFL;
X      else if (oCol >= (linCont ? fp->filPara.FoldRst : fp->filPara.FoldFst))
X         {
X         if (nc.ncUn) malf1 (eIntern, "cvline", __LINE__);
X         nc.ncUn = TRUE;
X         nc.ncICol -= spcs;
X         s = R_CONT;
X         }
X      }
X   if (s < 0)
X      {
X      do
X         {
X         if (*pp <= 256)
X            PSPUTC (*pp);
X         else
X            {
X            switch (*pp - 256)
X               {
X               case T_UNDEF:
X                  ENDSTR;
X                  PSPUTC ('(');
X                  if (fp->filPara.FixWid)
X                     SPRINTF (outBufP, "%02X", nc.ncOb[nc.ncN - 1]);
X                  else
X                     {
X                     SPRINTF (outBufP, "%o", nc.ncOb[0]);
X                     for (i = 1; i != nc.ncN; ++i)
X                        {
X                        outBufP = strend (outBufP);
X                        SPRINTF (outBufP, " %o", nc.ncOb[i]);
X                        }
X                     }
X                  outBufP = strend (outBufP);
X                  SPRINTF (outBufP, ")%s", PUNDEF);
X                  outBufP = strend (outBufP);
X                  PSPUTC ('\n');
X                  break;
X               case T_TAB:
X                  if (!ins)
X                     {
X                     PSPUTC ('(');
X                     ins = TRUE;
X                     }
X                  while (spcs--)
X                     PSPUTC (' ');
X                  break;
X               case T_STRB:
X                  if (!ins)
X                     {
X                     PSPUTC ('(');
X                     ins = TRUE;
X                     }
X                  break;
X               case T_STRE:
X                  ENDSTR;
X                  break;
X               case T_LINEE:
X                  ++linNum;
X                  nc.ncICol = 0;
X                  s = SUCCESS;
X                  break;
X               case T_COLE:
X                  s = R_COLE;
X                  break;
X               case T_PAGEE:
X                  s = R_PAGEE;
X                  break;
X               case T_EOF:
X                  s = ((0 == oCol) ? R_EOFB : R_EOFM);
X                  break;
X               }
X            }
X         }
X      while ((T_END + 256) != *++pp);
X      }
X   if (!ins)
X      PSPUTC ('\n');
X   else if (psLinL > (MPSLINE - MMULOCT * MPSSTR - 1))
X      ENDSTR;
X   }
XENDSTR;
Xif (DB(D_LINE)) FPRINTF (stderr, "cvline %u = %d\n", linNum, s);
Xreturn (s);
X}
X
X/* cvpage - try to convert one page to PostScript */
X
XPRIVATE int cvpage (fp, pc, por, moc)
XR4 filT     *fp;  /* Points to data for file to convert.*/
XR3 unsigned  pc;  /* Page columns to try.*/
X   boolT     por; /* Portrait mode.*/
XR6 unsigned  moc; /* Max input column.*/
X
X/* Function:
X *    Try to convert one page.
X * Algorithm:
X *    Compute clm, the maximum number of output lines that will fit
X *    in one output column.  This has to be recomputed for each page
X *    attempt because pc and por can be different.  Loop over the
X *    page columns.  Call cvcol() to try to convert one column, and
X *    switch on the return code.
X * Returns:
X *    Return code.
X * Notes:
X *    
X */
X{
X   double    bodHt;  /* Body height.*/
XR2 unsigned  c = 1;  /* Current page column.*/
XR5 unsigned  clm;    /* Max number of output lines in a page column.*/
XR7 paramT   *p = &fp->filPara;
X   double    leading = p->BodySiz * p->Spacing; /* Space between lines.*/
XR1 int       s;      /* Return code.*/
X   double    yHead;  /* Y coordinate of header baseline.*/
X
Xif (por)
X   {
X   yHead = p->PageHt - p->TMargP - p->HeadHt;
X   if (yHead <= 0.0) malf1 (eBigHd, p->TMargP + p->HeadHt, p->PageHt);
X   bodHt = yHead - p->TSkip - p->BSkip - p->FootHt - p->BMargL;
X   }
Xelse
X   {
X   yHead = p->PageWid - p->TMargL - p->HeadHt;
X   if (yHead <= 0.0) malf1 (eBigHd, p->TMargL + p->HeadHt, p->PageWid);
X   bodHt = yHead - p->TSkip - p->BSkip - p->FootHt - p->BMargP;
X   }
Xif (bodHt < leading) malf1 (eNoBod, bodHt, leading);
Xclm = (unsigned)(bodHt / leading);
Xfor (s = -1; s < 0; ++c)
X   {
X   switch (s = cvcol (fp, c, clm, moc))
X      {
X      case SUCCESS:
X      case R_COLE:
X         s = ((c == pc) ? SUCCESS : -1);
X         break;   
X      case R_EOFM:
X      case R_PAGEE:
X      case R_OVFL:
X         break;
X      case R_EOFB:
X         if (1 != c) s = R_EOFM;
X         break;
X      default:
X         malf1 (eIntern, "cvpage", __LINE__);
X         break;
X      }
X   }
Xif (DB(D_PAGEA))
X   FPRINTF (stderr, "cvpage (%u, %s, %u) = %d\n", pc,
X            por ? "portrait" : "landscape", moc, s);
Xreturn (s);
X}
X
X/* docFnts - output %%DocumentFonts: comment */
X
XPRIVATE void docFnts()
X
X/* Function:
X *    Write the DocumentFonts structure comment that lists all fonts
X *    used.  Get as many font names on a line as possible, without
X *    exceeding MPSLINE.  Use the %%+ line continuation convention
X *    if necessary.
X * Algorithm:
X *    Step through fonts[], write each font name, and keep track of
X *    the output column.
X * Returns:
X *    
X * Notes:
X *    
X */
X{
XR2     unsigned  c;  /* Output column.*/
XR1     fontT    *fp; /* Steps through fonts[].*/
XR3     unsigned  l;  /* Length of font name and leading space.*/
Xstatic char      df[] = "%%DocumentFonts:";
X
Xc = strlen (df);
XFPUTS (df, psos);
Xfor (fp = fonts; fp != fontP; ++fp)
X   {
X   l = 1 + strlen (fp->fntName);
X   if ((c + l) >= MPSLINE)
X      {
X      FPUTS ("\n%%+", psos);
X      c = 0;
X      }
X   PUTC (' ', psos);
X   FPUTS (fp->fntName, psos);
X   c += l;
X   }
XPUTC ('\n', psos);
X}
X
X/* doTop - set up topBuf[] for a new file */
X
XPRIVATE boolT doTop (fp)
XR3 filT *fp; /* Points to data for file to convert.*/
X
X/* Function:
X *    There is some repetitive stuff at the beginning of every page
X *    that is the same for every file.  It's repeated so that every
X *    PostScript page is independent of every other page.  This
X *    allows page reversal, page selection, and parallel page interpretation.
X *    This function generates the constant top-of-page stuff, and
X *    places it in the topBuf[] buffer.
X * Algorithm:
X *    Store the constant defs.  Call cvline() to store the header.
X * Returns:
X *    TRUE of success; FALSE on error.
X * Notes:
X *    
X */
X{
XR2 paramT *p = &fp->filPara;
XR1 bStrT   tp; /* Steps through topBuf[].*/
X
Xtp = topBuf;
XSPRINTF (tp, "%s\n", PSAVE);
Xtp = strend (tp);
XSPRINTF (tp, "/%s /%sE %s\n", PFONT, p->FixWid ? p->FWFont : p->VWFont,
X         PDEF);
Xtp = strend (tp);
XSPRINTF (tp, "/%s /%sE %s\n", FOOTFNT, p->FootFnt, PDEF);
Xtp = strend (tp);
XSPRINTF (tp, "/%s /%sE %s\n", HEADFNT, p->HeadFnt, PDEF);
Xtp = strend (tp);
XSPRINTF (tp, "/%s /%sE %s\n", PLNFNT, p->LNFnt, PDEF);
Xtp = strend (tp);
XSPRINTF (tp, "/%s /%s %s\n", SYMBFNT, p->SymbFnt, PDEF);
Xtp = strend (tp);
XSPRINTF (tp, "/%s %s %s\n", PFIXWID, p->FixWid ? "true" : "false", PDEF);
XoutBufP = strend (tp);
XoutBufE = &topBuf[MTOPBUF];
XputDimO (BODYSIZ, p->BodySiz);
XputDimO (PFOOTHT, p->FootHt);
XputDimO (PHEADHT, p->HeadHt);
XputDimO (LEADING, p->BodySiz * p->Spacing);
XputDimO (PLNHT,   p->LNHt);
XputDimO (PLNWID,  p->LNWid);
XputDimO (XADJUST, p->XAdjust);
XputDimO (YADJUST, p->YAdjust);
XSPRINTF (outBufP, "/%s", PHEADER);
XoutBufP = strend (outBufP);
XPSPUTC ('\n');
XPSPUTC ('{');
XPSPUTC ('\n');
Xnc = nc0;
Xnc.ncFP = fp;
Xif (NULBSTR == fp->filHdr) fp->filHdr = fp->filName;
Xnc.ncStr = fp->filHdr;
Xswitch (cvline (fp, MOCOL))
X   {
X   case R_EOFB:
X   case R_EOFM:
X      break; 
X   case SUCCESS:
X      malf0 (eHdrEnd, "line", fp->filName, fp->filHdr);
X      return (FALSE);
X   case R_COLE:
X      malf0 (eHdrEnd, "column", fp->filName, fp->filHdr);
X      return (FALSE);
X   case R_PAGEE:
X      malf0 (eHdrEnd, "page", fp->filName, fp->filHdr);
X      return (FALSE);
X   default:
X      malf1 (eIntern, "cvfile", __LINE__);
X      break;
X   }
XPSPUTC ('}');
XSTRCPY (outBufP, PDEF);
XoutBufP = strend (outBufP);
XPSPUTC ('\n');
XPSPUTC (EOS);
Xnc = nc0;
Xreturn (TRUE);
X}
X
X/* headCom - output PostScript structure header comments */
X
XPRIVATE void headCom()
X
X/* Function:
X *    Output the structure comments at the beginning of the PostScript
X *    output.
X * Algorithm:
X *    
X * Returns:
X *    
X * Notes:
X *    1) The %%For: line is entirely optional.  If your system
X *       has any trouble with the code to generate it, you can
X *       safely comment it out.
X */
X{
XR1     struct tm *tmp;       /* Returned by localtime().*/
X       long       ut;        /* Current system time.*/
X       byteT      fnb[1024]; /* Store user's full name here.*/
Xextern long       time();    /* (3C).*/
X
XFPUTS ("%!PS-Adobe-2.1\n", psos);
XFPRINTF (psos, "%%%%Creator: cz%d.%d\n", FRZ_MAJ, FRZ_MIN);
Xut = time ((long *) NULL);
Xtmp = localtime (&ut);
XFPRINTF (psos, "%%%%CreationDate: %d-%02d-%02d %02d:%02d:%02d\n",
X         1900 + tmp->tm_year, 1 + tmp->tm_mon, tmp->tm_mday,
X         tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
Xif (NULBSTR != userfn (fnb)) FPRINTF (psos, "%%%%For: %s\n", fnb);
XFPUTS ("%%Pages: (atend)\n", psos);
XFPUTS ("%%DocumentFonts: (atend)\n", psos);
X}
X
X/* init - initialize at start of execution */
X
XPRIVATE void init()
X
X/* Function:
X *    Initialize some things.
X * Algorithm:
X *    
X * Returns:
X *    
X * Notes:
X *    1) The umask setting will make the temporary files unreadable by
X *       others than the creator, for security.
X */
X{
XR1     bStrT   bp1;     /* Steps through ncMap[].*/
XR2     bStrT   bp2;     /* End of ncMap[].*/
XR5     int     i;       /* Loop counter.*/
XR3     ushrtT *up1;     /* Steps through AutoCol[].*/
XR4     ushrtT *up2;     /* End of AutoCol[].*/
Xextern int     umask(); /* (2).*/
X
X(void) umask (0077);
Xnc0.ncStr = NULBSTR;
Xbp1 = ncMap;
Xbp2 = &ncMap[256];
Xwhile (bp1 != bp2)
X   *bp1++ = T_RES;
XncMap[E_STRB]  = T_STRB;
XncMap[E_STRE]  = T_STRE;
XncMap[E_COM]   = T_COM;
XncMap[E_EESC]  = T_EESC;
XncMap[E_COLE]  = T_COLE;
XncMap[E_LINEE] = T_LINEE;
XncMap[E_PAGEE] = T_PAGEE;
XncMap[E_TAB]   = T_TAB;
Xup1 = param0.AutoCol;
Xup2 = up1 + MPAGCOL + 1;
Xwhile (up1 != up2)
X   *up1++ = MOCOL;
Xparam0.AutoLnd = MOCOL;
Xfor (i = 1; MSTATE != i; ++i)
X   states[i] = (stateT *) NULL;
X#include "cz0-init.h"
X}
X
X/* mpsputc - handle output buffer overflow */
X
XPRIVATE void mpsputc ()
X
X/* Function:
X *    The buffer in which PSPUTC stores bytes has overflowed.
X      Write an error message.
X * Algorithm:
X *    Figure out which buffer and call malf1().
X * Returns:
X *    No return.
X * Notes:
X *    
X */
X{
Xif (&outBuf[MOUTBUF] == outBufE)
X   malf1 (ePSPutC, "outBuf", MOUTBUF);
Xelse if (&topBuf[MTOPBUF] == outBufE)
X   malf1 (ePSPutC, "topBuf", MTOPBUF);
Xelse
X   malf1 (eIntern, "mpsputc", __LINE__);
X}
X
X/* mra2bp - convert string to typographical units and check for errors */
X
XPRIVATE double mra2bp (s, n, l, h)
XR3 bStrT  s; /* Input string.*/
X   bStrT  n; /* Name of field, for error messages.*/
X   double l; /* Lower bound.*/
X   double h; /* Upper bound.*/
X
X/* Function:
X *    String s is supposed to be a real numeric literal followed by a
X *    two-letter units abbreviation.  Convert it to big points. 
X * Algorithm:
X *    Linear search on units, then call mra2d().
X * Returns:
X *    PostScript internal units.
X * Notes:
X *    1) Maybe I should use perfect hashing on the units?
X *    2) For now, loose syntax is allowed, e.g. 0bp.  But this is not
X *       documented, so I can tighten up whenever I want.
X */
X{
X   double sf; /* Scaling factor.*/
XR2 int    ls; /* Length of input string.*/
XR1 bStrT  p;  /* Should point to units.*/
X
Xif (DB(D_PSC)) FPRINTF (stderr, "mra2bp (%s, %s, %G, %G)\n", s, n, l, h);
Xls = strlen (s);
Xif (ls < 3) malf1 ("%s [%s]: Bad format", n, s);
Xp = &s[ls - 2];
Xif      (bStrEQ (S("bp"), p)) sf = 1.0;
Xelse if (bStrEQ (S("cc"), p)) sf = CF_CC;
Xelse if (bStrEQ (S("cm"), p)) sf = CF_CM;
Xelse if (bStrEQ (S("dd"), p)) sf = CF_DD;
Xelse if (bStrEQ (S("in"), p)) sf = CF_IN;
Xelse if (bStrEQ (S("mm"), p)) sf = CF_MM;
Xelse if (bStrEQ (S("pc"), p)) sf = CF_PC;
Xelse if (bStrEQ (S("pt"), p)) sf = CF_PT;
Xelse malf1 (eUnits, n, s, p);
Xreturn (sf * mra2d (s, p, TRUE, n, l, h, (bStrT *) NULL));
X}
X
X/* ms2bool - convert string to Boolean and handle errors */
X
XPRIVATE boolT ms2bool (s, fn)
XR1 bStrT s;  /* Input string.*/
X   bStrT fn; /* Field name.*/
X
X/* Function:
X *    Convert string representations of Booleans to internal representation.
X * Algorithm:
X *    Linear search.
X * Returns:
X *    TRUE or FALSE.  No return on error.
X * Notes:
X *    1) Case is significant.
X */
X{
Xif (bStrEQ (S("false"), s)) return (FALSE);
Xelse if (bStrEQ (S("true"), s)) return (TRUE);
Xelse malf1 ("%s [%s] neither false nor true", s, fn);
X/*NOTREACHED*/
X}
X
X/* nextc - get next input character */
X
XPRIVATE ushrtT *nextc()
X
X/* Function:
X *    Convert one input character to output format.  The input character
X *    can come from a stream or a string (for headers).  A character
X *    can be up to MMULOCT bytes.  Output format is an array of unsigned
X *    shorts.  Elements in the range [0, 255] are bytes to be output literally.
X *    Higher elements are one of the T_* codes biased by 256.
X *    (T_END + 256) marks the end of the array.
X * Algorithm:
X *    If previous result was pushed back by line folding, unpush it and return.
X *    Otherwise, start in state 0 and loop.  Get one byte from the string
X *    or stream.  Handle end of string/file.  Get the state-octet entry.
X *    Step through the PostScript.  Copy ordinary characters to the return
X *    buffer, but switch on escapes.
X * Returns:
X *    A pointer to the array.
X * Notes:
X *    
X */
X{
XR7 stateT   *sp;  /* Points to current stateT.*/
XR4 soT      *sop; /* Points to current state-octet pair.*/
XR6 boolT     m;   /* More to do.*/
XR5 unsigned  st;  /* State.*/
XR3 ushrtT   *rbp; /* Steps through return buffer.*/
XR9 bStrT     obp; /* Steps through octet buffer.*/
XR8 rcharT    o;   /* Current input octet.*/
XR1 unsigned  p;   /* Current result byte.*/
XR2 bStrT     psp; /* Steps through PostScript for each state-octet pair.*/
X
Xif (nc.ncUn)
X   {
X   nc.ncUn = FALSE;
X   return (nc.ncRb);
X   }
Xst = 0;
Xobp = nc.ncOb;
Xrbp = nc.ncRb;
Xfor (m = TRUE; m;)
X   {
X   if (NULBSTR != nc.ncStr)
X      {
X      o = B(*nc.ncStr);
X      ++nc.ncStr;
X      if (EOS == o)
X         {
X         if (0 != st)
X            {
X            malf0 (eEOSSt, st, nc.ncFP->filHdr);
X            st = 0;
X            }
X         o = EOF;
X         }
X      }
X   else
X      {
X      o = getc (nc.ncIStrm);
X      if (EOF == o)
X         {
X         if (ferror (nc.ncIStrm)) malf0 (eRead, nc.ncFP->filName);
X         if (0 != st)
X            {
X            malf0 (eEOFSt, nc.ncFP->filName, st);
X            st = 0;
X            }
X         }
X      }
X   if (EOF == o)
X      {
X      *rbp++ = T_EOF + 256;
X      m = FALSE;
X      }
X   else
X      {
X      if ((o < 0) || (o > 255)) malf1 (eIntern, "nextc", __LINE__);
X      *obp++ = o;
X      if (0 == st)
X         {
X         ++nc.ncICol;
X         ++oCol;
X         }
X      p = 0;
X      sp = states[st];
X      if (((stateT *) NULL) == sp)
X         {
X         if (DB(D_OCT)) FPRINTF (stderr, "State %u undefined\n", st);
X         p = T_UNDEF + 256;
X         }
X      else
X         {
X         sop = ((soT *) sp) + o;
X         psp = sop->so_ps;
X         if (DB(D_OCT))
X            FPRINTF (stderr, "State %u Octet %c 8#%o# (%s%c%c%u)\n",
X                     st, o, o, psp, E_ESC, E_NEXTS, sop->so_nxt);
X         if (NULBSTR == psp) p = T_UNDEF + 256;
X         }
X      if ((T_UNDEF + 256) == p)
X         {
X         rbp = nc.ncRb;
X         *rbp++ = p;
X         st = 0;
X         }
X      else
X         {
X         for (; EOS != (p = B(*psp)); ++psp)
X            {
X            if (E_ESC != p)
X               *rbp++ = p;
X            else
X               {
X               ++psp;
X               p = ncMap[B(*psp)];
X               switch (p)
X                  {
X                  case T_RES: /* Can't happen.*/
X                     malf1 (eMap, E_ESC, *psp, *psp, st, o, o);
X                     break;
X                  case T_COM:
X                     *rbp++ = COMMENT;
X                     break;
X                  case T_EESC:
X                     *rbp++ = E_ESC;
X                     break;
X                  default:
X                     *rbp++ = p + 256;
X                     break;
X                  }
X               }
X            }
X         st = sop -> so_nxt;
X         }
X      }
X   if (0 == st) m = FALSE;
X   }
X*rbp = T_END + 256;
Xnc.ncN = obp - nc.ncOb;
Xif (DB(D_PSC))
X   {
X   for (rbp = nc.ncRb; (T_END + 256) != *rbp; ++rbp)
X      {
X      if (*rbp < 256)
X         PUTC (*rbp, stderr);
X      else
X         {
X         switch (*rbp - 256)
X            {
X            case T_UNDEF:
X               FPRINTF (stderr, "%cU", E_ESC);
X               break;
X            case T_STRB:
X               FPRINTF (stderr, "%c%c", E_ESC, E_STRB);
X               break;
X            case T_STRE:
X               FPRINTF (stderr, "%c%c", E_ESC, E_STRE);
X               break;
X            case T_TAB:
X               FPRINTF (stderr, "%c%c(%u)", E_ESC, E_TAB, nc.ncICol);
X               break;
X            case T_COLE:
X               FPRINTF (stderr, "%c%c", E_ESC, E_COLE);
X               break;
X            case T_LINEE:
X               FPRINTF (stderr, "%c%c", E_ESC, E_LINEE);
X               break;
X            case T_PAGEE:
X               FPRINTF (stderr, "%c%c", E_ESC, E_PAGEE);
X               break;
X            case T_EOF:
X               FPRINTF (stderr, "%cE", E_ESC);
X               break;
X            default:
X               malf1 (eIntern, "nextc", __LINE__);
X               break;
X            }
X         }
X      }
X   PUTC ('\n', stderr);
X   }
Xreturn (nc.ncRb);
X}
X
X/* preScan - find widest line in input file, and count lines */
X
XPRIVATE void preScan (fp, f, pcp, porp)
X   filT     *fp;   /* Points to data for file to convert.*/
XR8 streamT   f;    /* Input stream.*/
X   unsigned *pcp;  /* Store initial number of page columns here.*/
X   boolT    *porp; /* Store initial orientation here.*/
X
X/* Function:
X *    If AutoColumn or AutoLandscape are being done on a whole-file
X *    basis (AutoFile true), then we need to know the widest line in
X *    the file.  Also, we need to know the number of lines in the file
X *    so we can always output the line number of the last one.
X *    This function reads through the entire file and then rewinds it
X *    for the conversion pass.
X * Algorithm:
X *    Loop through each input character.  Switch on T_* codes.
X *    Expand tabs.  On end of line set mc = max (mc, oCol).
X *    At the end of the loop, compute pc0 and por0.
X * Returns:
X *    Stores pc0 in *pcp and por0 in *porp.  pc0 is 0 for an empty file.
X * Notes:
X *    1) This pass could be avoided if AutoFile is false and line numbers
X *       are not being printed.
X */
X{
XR4 boolT     nl = TRUE; /* Start of new line.*/
XR5 unsigned  mc = 0;    /* Max input columns in file.*/
XR6 unsigned  mcl = 0;   /* Line number of (first) widest line in file.*/
XR2 paramT   *p = &fp->filPara;
XR7 unsigned  pc;        /* Initial page columns.*/
XR1 ushrtT   *pp;        /* Returned by nextc().*/
XR3 unsigned  spcs;      /* Tab expands into this many spaces.*/
X
XlinNum = 0;
XoCol = 0;
Xnc.ncIStrm = f;
Xpp = nextc();
Xdo
X   {
X   if ((T_END + 256) == *pp) pp = nextc();
X   if (*pp <= 256)
X      nl = FALSE;
X   else
X      {
X      switch (*pp - 256)
X         {
X         case T_TAB:
X            spcs = p->TabWid - ((nc.ncICol - 1) % p->TabWid) - 1;
X            nc.ncICol += spcs;
X            oCol += spcs;
X            nl = FALSE;
X            break;
X         case T_UNDEF:
X            malf0 (eUndef, fp->filName, linNum + 1);
X            /* Falls through.*/
X         case T_STRB:
X         case T_STRE:
X            nl = FALSE;
X            break;
X         case T_LINEE:
X            ++linNum;
X            nc.ncICol = 0;
X            if (oCol > mc)
X               {
X               mcl = linNum;
X               mc = oCol;
X               }
X            nl = TRUE;
X            oCol = 0;
X            break;
X         case T_COLE:
X         case T_PAGEE:
X            if (oCol > mc)
X               {
X               mcl = linNum + 1;
X               mc = oCol;
X               }
X            oCol = 0;
X            break;
X         case T_EOF:
X            if (!nl) malf0 (eEOF, fp->filName, linNum + 1);
X            if (oCol > mc)
X               {
X               mcl = linNum + 1;
X               mc = oCol;
X               }
X            break;
X         default:
X            malf1 (eIntern, "cvfile", __LINE__);
X            break;
X         }
X      }
X   }
Xwhile ((T_EOF + 256) != *pp++);
Xif (0 == mc)
X   { /* Empty file.*/
X   *pcp = 0;
X   return;
X   }
Xlines = linNum;
X--mc;
Xif (p->AutoFil)
X   {
X   for (pc = p->Columns; (1 != pc) && (mc > p->AutoCol[pc]); --pc)
X      ;
X   *porp = (mc <= p->AutoLnd);
X   }
Xelse
X   {
X   pc = p->Columns;
X   *porp = (0 != p->AutoLnd);
X   }
X*pcp = pc;
Xrewind (f);
XlinCont = FALSE;
XoutBufE = &outBuf[MOUTBUF];
Xif (DB(D_FILE))
X   FPRINTF (stderr,
X      "preScan: widest line: %u (%u cols)\n\ttotal lines: %u\n\tinitial page columns: %u %s\n",
X      mcl, mc, linNum, pc, *porp ? "portrait" : "landscape");
X}
X
X/* psputc - append one byte to output buffer */
X
XPRIVATE void psputc (c)
XR1 rcharT c; /* Byte to append.*/
X
X/* Function:
X *    Store c in the output buffer.  Handle long lines and buffer overflow.
X * Algorithm:
X *    psLinL is the current number of bytes already output on the current
X *    output line.  Just ignore a newline after another newline.
X * Returns:
X *    
X * Notes:
X *    1) This is inefficient.  We shouldn't be copying so many bytes around.
X *       We should use pointers instead.
X */
X{
Xif ('\n' == c)
X   {
X   if (0 != psLinL)
X      {
X      *outBufP++ = c;
X      psLinL = 0;
X      }
X   }
Xelse
X   {
X   *outBufP++ = c;
X   ++psLinL;
X   }
Xif (outBufP >= outBufE) mpsputc();
X}
X
X/* putDim - write a dimension to output buffer */
X
XPRIVATE void putDim (a, v)
XcStrT  a; /* Abbreviation string for dimension.*/
Xdouble v; /* Value of dimension.*/
X
X/* Function:
X *    Write a def directly to the output stream.
X * Algorithm:
X *    
X * Returns:
X *    
X * Notes:
X *    1) Two decimal places are enough precision for printers with up
X *       to 7200 dots per inch resolution.
X */
X{
XFPRINTF (psos, "/%s %.2f %s\n", a, v, PDEF);
X}
X
X/* putDimO - write a dimension to output buffer */
X
XPRIVATE void putDimO (a, v)
XcStrT  a; /* Abbreviation string for dimension.*/
Xdouble v; /* Value of dimension.*/
X
X/* Function:
X *    Store a def in the output buffer.
X * Algorithm:
X *    The first call to PSPUTC makes psLinL nonzero for the second call.
X *    Otherwise the second call would be ignored.  The second call
X *    checks for output buffer overflow.
X * Returns:
X *    
X * Notes:
X *    
X */
X{
XPSPUTC ('/');
XSPRINTF (outBufP, "%s %.2f %s", a, v, PDEF);
XoutBufP = strend (outBufP);
XPSPUTC ('\n');
X}
X
X/* putFnts - write definition for each font used */
X
XPRIVATE void putFnts()
X
X/* Function:
X *    Create the list of all fonts used by all files in the job.
X *    Output reencode commands for all fonts used except symbol fonts.
X * Algorithm:
X *    Loop through each file.  Call addFnt() for each font.
X *    Then loop through each font.
X * Returns:
X *    
X * Notes:
X *    
X */
X{
XR1 filT   *fp;  /* Steps through fil[].*/
XR2 fontT  *fnp; /* Steps through fonts[].*/
XR3 paramT *p;   /* &fp->filPara.*/
X
Xfor (fp = &fil[1]; fp != filp; ++fp)
X   {
X   p = &fp->filPara;
X   addFnt (p->FixWid ? p->FWFont : p->VWFont, FALSE);
X   addFnt (p->FootFnt, FALSE);
X   addFnt (p->HeadFnt, FALSE);
X   addFnt (p->LNFnt,   FALSE);
X   addFnt (p->SymbFnt, TRUE);
X   }
Xfor (fnp = fonts; fnp != fontP; ++fnp)
X   {
X   if (!fnp->fntSymb)
X      FPRINTF (psos, "/%sE /%s %s\n", fnp->fntName, fnp->fntName, REENCOD);
X   }
X}
X
X/* putPage - Write one page to psos */
XPRIVATE void putPage (fp, pp, pn, tp, nb)
XR4 filT     *fp; /* Points to file data.*/
XR7 pageT    *pp; /* Points to data for page.*/
XR5 unsigned  pn; /* Page number.*/
XR6 unsigned  tp; /* Total number of pages in file.*/
XR2 long      nb; /* Number of bytes to copy from temporary file.*/
X
X/* Function:
X *    
X * Algorithm:
X *    The width available for one column is the (effective) page width
X *    less the left and right margins and column separation gutters,
X *    all divided by the number of columns.
X *    The maximum number of lines per column is the (effective) page
X *    height less the top and bottom margins and header and footer,
X *    divided by the distance between lines, then rounded down.
X *
X *    Output all the dimensions, then copy bytes.
X * Returns:
X *    
X * Notes:
X *    1) I have no idea if the TxtVert code is the right thing to do.
X *       The theory is that landscaped vertical text should be like
X *       portrait horizontal text, and vice versa.
X *    2) It would be faster to copy the bytes in big chunks, not
X *       one at a time.
X */
X{
XR1 rcharT  c;        /* Current byte from temp file.*/
XR3 paramT *p = &fp->filPara;
X   double  cw;       /* Page column width.*/
X   double  ePageHt;  /* Effective page height.*/
X   double  ePageWid; /* Effective page width.*/
X   double  eLMarg;   /* Effective left margin.*/
X   double  eRMarg;   /* Effective right margin.*/
X   double  eTMarg;   /* Effective top margin.*/
X   double  eBMarg;   /* Effective bottom margin.*/
X
Xmfseek (psts, pp->pgPos, 0, pstfn);
XFPRINTF (psos, "%%%%Page: %s-%u %u\n", fp->filName, pn, pn + totPag);
XFPUTS (topBuf, psos);
Xif (pp->pgPor != p->TxtVert)
X   {
X   ePageHt  = p->PageHt;
X   ePageWid = p->PageWid;
X   eLMarg   = p->LMargP;
X   eRMarg   = p->RMargP;
X   eTMarg   = p->TMargP;
X   eBMarg   = p->BMargP;
X   FPRINTF (psos, "/%s{}%s\n", PROTATE, PDEF);
X   }
Xelse
X   {
X   ePageHt  = p->PageWid;
X   ePageWid = p->PageHt;
X   eLMarg   = p->LMargL;
X   eRMarg   = p->RMargL;
X   eTMarg   = p->TMargL;
X   eBMarg   = p->BMargL;
X   FPRINTF (psos, "/%s/%s %s %s\n",
X            PROTATE, (90 == p->LandRot) ? PROTPOS : PROTNEG, PLOAD, PDEF);
X   }
Xif (eTMarg + p->HeadHt > ePageHt)
X   malf1 (eBigHd, p->TMargP + p->HeadHt, p->PageHt);
Xcw = (ePageWid - eLMarg - eRMarg - (pp->pgCols - 1) * p->ColSep) / pp->pgCols;
Xif (cw <= 0.0) malf1 (eColWid, cw);
XputDim (LMARGIN, eLMarg);
XputDim (RMARGIN, eRMarg);
XputDim (BMARGIN, eBMarg);
XputDim (TMARGIN, eTMarg);
XputDim (PPAGEWD, ePageWid);
XputDim (PPAGEHT, ePageHt);
XFPRINTF (psos, "/%s %u %s\n", PPAGEN, pn, PDEF);
XFPRINTF (psos, "/%s(- %u / %u -)%s\n", PPAGES, pn, tp, PDEF);
XputDim (FOLDIND, p->FoldInd);
XputDim (TOPSKIP, p->TSkip);
XputDim (PDX, cw + p->ColSep);
XFPRINTF (psos, "%s\n", PPAGEB);
Xwhile (nb--)
X   {
X   c = getc (psts);
X   if (EOF == c)
X      {
X      if (ferror (psts)) malf1 (eRead, pstfn);
X      malf1 (eUEOF, pstfn);
X      }
X   PUTC (c, psos);
X   }
XFPRINTF (psos, "%s\n%%%%PageTrailer\n", PPAGEE);
X}
X
X/* readenv - process environment variables */
X
XPRIVATE void readenv()
X
X/* Function:
X *    Execute any commands in the environment.
X * Algorithm:
X *    Linear search through the environment variables for any matches.
X *    Call cmd() on a match.
X * Notes:
X *    1) Ignore CZPATH.  It's handled elsewhere.
X */
X{
Xextern cStrT *environ;         /* (5V).*/
XR4     bStrT *ep;              /* Steps through environment variables.*/
XR3     bStrT  cp;              /* General purpose.*/
XR2     bStrT  cp2;             /* General purpose.*/
XR1     bStrT  cp3;             /* General purpose.*/
X       byteT  cbuf[MLINE + 1]; /* Command buffer.*/
X
Xfor (ep = (bStrT *) environ; NULBSTR != (cp = *ep++);)
X   {
X   if ((NULBSTR != (cp3 = prefix (S(ENVPRE), cp))) &&
X       (NULBSTR == prefix (S(CZPATH), cp)))
X      {
X      cp2 = cbuf;
X      for (; (EOS != B(*cp3)) && ('=' != B(*cp3)); ++cp3)
X         {
X         if (cp2 == &cbuf[MLINE]) malf1 (eMLine, ep[-1], MLINE);
X         *cp2++ = B(*cp3);
X         }
X      if (('=' == B(*cp3)) && (EOS != B(cp3[1])))
X         {
X         ++cp3;
X         *cp2 = EOS;
X         cmd (cbuf, cp3, "Environment", 0);
X         }
X      }
X   }
X}
X
X/* readpth - read every command file on the path */
X
XPRIVATE void readpth (cfn)
XR7 bStrT cfn; /* Command file name.*/
X
X/* Function:
X *    Read and execute every cz.rc file on the search path.
X *    Copy every cz.ps file to output.
X * Algorithm:
X *    Starting at the beginning of the path,
X *    and working toward the end, copy each segment into
X *    fnbuf[], append the command file name, and try to open the
X *    file.  If the file can be opened, call readrc() on it, then
X *    close it and proceed to the next segment.
X */
X{
XR1     bStrT   p1;           /* Step through path.*/
XR2     bStrT   p2;           /* Step through path.*/
XR3     streamT f;            /* Command file.*/
XR4     int     i;            /* Path length.*/
XR5     int     l;            /* Length of command file name.*/
XR6     int     m;            /* Max path length.*/
Xextern cStrT   getenv();     /* (3).*/
X       byteT   fnbuf[MFILE]; /* Buffer for current file name.*/
X
Xl = strlen ((cStrT) cfn);
Xm = MFILE - 1 - l - MAX (strlen (CZSUF), strlen (PSSUF));
Xp1 = path;
Xif (DB(D_CMD)) FPRINTF (stderr, "readpth %s\n", cfn);
Xdo
X   {
X   p2 = bStrChr (p1, PATHSEP);
X   i = ((NULBSTR == p2) ? strlen (p1) : p2 - p1);
X   if ((0 == i) || (i > m)) malf1 (eMPath, m, p1);
X   (void) strncpy ((cStrT) fnbuf, (cStrT) p1, i);
X   p1 = &fnbuf[i];
X   if ('/' != B(p1[-1])) *p1++ = '/';
X   STRCPY ((cStrT) p1, (cStrT) cfn);
X   p1 += l;
X   STRCPY ((cStrT) p1, CZSUF);
X   f = fopen ((cStrT) fnbuf, "r");
X   if (NULSTRM == f)
X      {
X      if (DB(D_CMD)) FPRINTF (stderr, "trying %s...failed\n", fnbuf);
X      }
X   else
X      {
X      if (DB(D_CMD)) FPRINTF (stderr, "trying %s...ok\n", fnbuf);
X      readrc (f, fnbuf);
X      if (fclose (f)) malf0 (eClose, fnbuf);
X      }
X   STRCPY ((cStrT) p1, PSSUF);
X   f = fopen ((cStrT) fnbuf, "r");
X   if (NULSTRM == f)
X      {
X      if (DB(D_CMD)) FPRINTF (stderr, "trying %s...failed\n", fnbuf);
X      }
X   else
X      {
X      if (DB(D_CMD)) FPRINTF (stderr, "trying %s...ok\n", fnbuf);
X      FPRINTF (psos, "%%%%BeginFile: %s\n", fnbuf);
X      mfcopy (f, 0, fnbuf, psos, 1, Out);
X      if (fclose (f)) malf0 (eClose, fnbuf);
X      FPUTS ("%%EndFile\n", psos);
X      }
X   if (NULBSTR != p2) p1 = p2 + 1;
X   }
Xwhile (NULBSTR != p2);
X}
X
X/* readrc - read one command file */
X
XPRIVATE void readrc (f, fn)
XR4 streamT f;  /* File.*/
XR3 bStrT   fn; /* File name.*/
X
X/* Function:
X *    Read through one command file and execute each command.
X * Algorithm:
X *    Read each line.  Strip comments and trailing white space.
X *    Skip leading white
X *    space.  Find command.  Put NUL at end of it.  Skip white
X *    space.  Find value.  Call cmd().
X * Notes:
X *    
X */
X{
XR1 bStrT    cp;        /* General purpose.*/
XR2 bStrT    cmdp;      /* Points to command.*/
X   unsigned ln = 0;    /* Current line number.*/
X   byteT    lb[MLINE]; /* Input line buffer.*/
X
Xwhile (NULBSTR != getlic (lb, MLINE, f, fn, &ln, 1, COMMENT))
X   {
X   for (cp = lb; ' ' == B(*cp); ++cp)
X      ;
X   cmdp = cp++;
X   for (; (EOS != B(*cp)) && (' ' != B(*cp)); ++cp)
X      ;
X   if (EOS != B(*cp))
X      {
X      *cp++ = EOS;
X      for (; ' ' == B(*cp); ++cp)
X         ;
X      if (EOS != B(*cp)) cmd (cmdp, cp, fn, ln);
X      else malf0 (eNoArgL, fn, ln, lb);
X      }
X   }
X}
X
X/* undef - undefine one state-octet pair or one whole state */
X
XPRIVATE void undef (ss, os)
X   bStrT ss; /* State as a string.*/
XR1 bStrT os; /* Octet as a string.*/
X
X/* Function:
X *    cz0 starts out configured for ISO 8859/1.  For other character
X *    sets, it's sometimes necessary to undefine state-octet pairs.
X * Algorithm:
X *    Convert state to number.  If "octet" is All, zap the whole state.
X *    Otherwise, convert octet to number and zap that entry.
X * Returns:
X *    
X * Notes:
X *    1) Dynamic memory is not freed.
X */
X{
XR2 unsigned  o;    /* Octet.*/
XR3 unsigned  s;    /* State.*/
XR4 soT      *sop1; /* Current soT.*/
XR5 stateT   *sp;   /* Points to current stateT.*/
X
Xs = mra2u (ss, NULBSTR, FALSE, S("State"), (unsigned) 0, (unsigned) MSTATE,
X           (bStrT *) NULL);
Xif (bStrEQ (S("All"), os))
X   states[s] = (stateT *) NULL;
Xelse
X   {
X   o = mra2u (os, NULBSTR, FALSE, "Octet", (unsigned) 0, (unsigned) 255,
X              (bStrT *) NULL);
X   sp = states[s];
X   if (((stateT *) NULL) != sp)
X      {
X      sop1 = ((soT *) sp) + o;
X      sop1->so_ps = NULBSTR;
X      }
X   }
X}
X
X/* main - main function							*/
X
XPUBLIC void main (argc, argv)
X   int    argc; /* Number of arguments.*/
XR2 bStrT *argv; /* Points to array of argument strings.*/
X
X/* Function:
X *    Main function.
X * Algorithm:
X *    If there's a -o option, open the output stream.
X *    Call init(), etc.
X *    Step through command line arguments and call cmd() on them.
X *    Step through files and call cvfile() on each.
X * Notes:
X *	
X */
X
X{
XR1 bStrT cp; /* Used in argument decoding.*/
XR3 filT *fp; /* Steps through fil[].*/
X
X++argv;
Xcp = *argv++;
Xif ((argc >= 3) && (bStrEQ (S("-o"), cp)))
X   {
X   cp = *argv++;
X   if (PIPECHR != B(*cp))
X      psos = fopen ((cStrT) cp, "w");
X   else if (EOS == B(cp[1]))
X      malf1 (eNoPipe, PIPECHR);
X   else
X      psos = popen ((cStrT) &cp[1], "w");
X   if (NULSTRM == psos) malf1 (eOpen, cp);
X   Out = cp;
X   cp = *argv++;
X   }
Xinit();
XheadCom();
Xipath();
Xreadpth ("header");
XFPUTS ("%%EndComments\n", psos);
XFPRINTF (psos, "/%s/load load def\n/%s/def %s def\n/Version(cz%d.%d)%s\n",
X         PLOAD, PDEF, PLOAD, FRZ_MAJ, FRZ_MIN, PDEF);
Xreadpth ("prolog-beg");
Xreadenv();
Xwhile ((NULBSTR != cp) && ('-' == B(*cp)))
X   {
X   ++cp;
X   if (NULBSTR == *argv) malf1 (eNoArg, cp);
X   else cmd (cp, *argv++, sCLine, 0);
X   cp = *argv++;
X   }
Xif (NULBSTR != cp) usage();
Xif (filp == &fil[1]) malf1 (eNoFile);
Xif (DB(D_JOB)) FPRINTF (stderr, "%d file(s)\n", filp - fil - 1);
XputFnts();
Xreadpth ("prolog-end");
XFPUTS ("%%EndProlog\n", psos);
Xif (Reverse)
X   {
X   for (fp = filp - 1; fp != fil;)
X      cvfile (fp--);
X   }
Xelse
X   {
X   for (fp = &fil[1]; fp != filp;)
X      cvfile (fp++);
X   }
XFPRINTF (psos, "%%%%Trailer\n%%%%Pages: %u\n", totPag);
XdocFnts();
Xif (EndCtlD) PUTC (4, psos);
Xmfflush (psos, Out);
Xif ((PIPECHR == B(*Out)) ? pclose (psos) : fclose (psos)) malf0 (eClose, Out);
Xif ((NULCSTR != pstfn) && unlink (pstfn)) malf0 (eUnlink, pstfn);
Xexit (0);
X}
END_OF_FILE
if test 54952 -ne `wc -c <'cz0.c'`; then
    echo shar: \"'cz0.c'\" unpacked with wrong size!
fi
# end of 'cz0.c'
fi
echo shar: End of archive 11 \(of 14\).
cp /dev/null ark11isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 14 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0