/*
 *   These routines do most of the communicating with the printer.
 *
 *   LINELENGTH tells the maximum line length to send out.  It's been
 *   reduced to 72 because sometimes PostScript files are included in
 *   mail messages and many mailers mutilate longer lines.
 */
#define LINELENGTH (72)
#include "dvips.h" /* The copyright notice in that file is included too! */
#include <ctype.h>
#include <stdlib.h>

#ifdef OS2
#ifdef _MSC_VER
#define popen(pcmd, pmode)  _popen(pcmd, pmode)
#define pclose(pstream) _pclose(pstream)
#endif
#endif

#ifdef __DJGPP__
#include <unistd.h>	/* for `isatty' */
#endif

/*
 *   The external routines called here:
 */
#include "protos.h"

/*
 *   These are the external variables used by these routines.
 */
extern integer hh, vv ;
extern fontdesctype *curfnt ;
extern FILE *bitfile ;
extern char *oname ;
extern Boolean secure ;
extern Boolean reverse ;
extern Boolean removecomments ;
extern Boolean sendcontrolD, disablecomments, multiplesects ;
extern Boolean shiftlowchars ;
extern Boolean usesPSfonts, headers_off ;
extern Boolean safetyenclose ;
extern Boolean cropmarks ;
extern Boolean tryepsf ;
extern Boolean compressed ;
extern Boolean sepfiles ;
extern int numcopies ;
extern int collatedcopies ;
extern integer pagecopies ;
extern int totalpages ;
extern integer pagenum ;
extern Boolean manualfeed ;
extern int landscape ;
extern int quiet ;
extern int prettycolumn ;
extern int actualdpi, vactualdpi ;
extern char *iname, *nextstring ;
extern char *paperfmt ;
#ifndef KPATHSEA
extern char *headerpath ;
extern char *figpath ;
#endif
extern char errbuf[] ;
extern shalfword linepos ;
extern struct header_list *ps_fonts_used ;
extern char banner[], banner2[] ;
extern int gargc ;
extern char **gargv ;
extern struct papsiz *papsizes ;
extern integer hpapersize, vpapersize ;
char preamblecomment[256] ; /* usually "TeX output ..." */
#ifdef HPS
extern Boolean noprocset, HPS_FLAG ;
#endif
/*
 *   We need a few statics to take care of things.
 */
static integer rhh, rvv ;
static int instring ;
static Boolean lastspecial = 1 ;
static shalfword d ;
static Boolean popened = 0 ;
int lastfont ; /* exported to dospecial to fix rotate.tex problem */
static void chrcmd P1H(char c);        /* just a forward declaration */
static char strbuffer[LINELENGTH + 20], *strbp = strbuffer ;
static struct papsiz *finpapsiz ;
static struct papsiz defpapsiz = {
   0, 40258437L, 52099154L, "letter", ""
} ;
#ifdef CREATIONDATE
#if (!defined(VMS) && !defined(MSDOS) && !(defined(OS2) && defined(_MSC_VER)) && !defined(ATARIST))
 /* VAXC/MSDOS don't like/need this !! */
#include <sys/types.h>
#include <sys/time.h> /* time(), at least on BSD Unix */
#endif
#include <time.h> /* asctime() and localtime(), at least on BSD Unix */
static time_t jobtime;
#endif
/*
 *   This routine copies a file down the pipe.  Search path uses the
 *   header path.
 *
 *   We add code to handle the case of MS-DOS font files.
 *
 *   Format:  80 {01,02} four byte length in littleendian order data
 *   repeated possibly multiple times.
 */
static char *hxdata = "0123456789ABCDEF" ;
static int infigure ;
extern char *infont ;
static char possibleDSCLine[81],
       *dscLinePointer = possibleDSCLine, *dscLineEnd = possibleDSCLine + 80 ;
void
copyfile P1C(char *, s)
{
   FILE *f = NULL ;
   int c, prevc = '\n' ;
   long len ;
   /* begin DOS EPS code */
   int doseps = 0;
   unsigned long dosepsbegin, dosepsend = 0;
   int removingBytes = 0 ;
   char *scanForEnd = 0 ;
   /* end DOS EPS code */
#ifdef VMCMS
   register char *lastdirsep ;
   register char *trunc_s ;
   trunc_s = s ;
#endif
#ifdef MVSXA
   register char *lastdirsep ;
   register char *trunc_s ;
   trunc_s = s ;
#endif
   dscLinePointer = possibleDSCLine ;

   switch (infigure) {
   case 1:
/*
 *   Look in headerpath too, just in case.  This allows common header
 *   or figure files to be installed in the .../ps directory.
 */
      f = search(figpath, s, READBIN) ;
      if (f == 0)
         f = search(headerpath, s, READBIN) ;
#ifdef VMCMS
      lastdirsep = strrchr(s, '/') ;
      if ( NULL != lastdirsep ) trunc_s = lastdirsep + 1 ;
      (void)sprintf(errbuf,
   "Couldn't find figure file %s with CMS name %s; continuing", s, trunc_s) ;
#else
#ifdef MVSXA
      lastdirsep = strrchr(s, '/') ;
      if ( NULL != lastdirsep ) trunc_s = lastdirsep + 1 ;
      (void)sprintf(errbuf,
    "Couldn't find figure file %s with MVS name %s; continuing", s, trunc_s) ;
#else
      (void)sprintf(errbuf, "Could not find figure file %s; continuing", s) ;
#endif
#endif
      break ;
#ifndef VMCMS
#ifndef MVSXA
#ifndef VMS
#if !defined(MSDOS) || defined(__DJGPP__)
#ifndef ATARIST
#ifndef __THINK__
   case 2:
#ifdef SECURE
      (void)sprintf(errbuf, "<%s>: Tick filename execution disabled", s) ;
#else
#ifdef OS2
      if (_osmode == OS2_MODE) {
#endif
      if (secure == 0) {
         (void)sprintf(errbuf, "Execution of  <%s> failed ", s) ;
         f = popen(s, "r") ;
         if (f != 0)
            SET_BINARY(fileno(f)) ;
	}
	else {
      (void)sprintf(errbuf,"Secure mode is %d so execute <%s> will not run", secure,s) ;
	}
#ifdef OS2
      }
#endif
#endif
      break;
#endif
#endif
#endif
#endif
#endif
#endif
   default:
      f = search(headerpath, s, READBIN) ;
      (void)sprintf(errbuf, "! Could not find header file %s", s) ;
      break ;
   }
   if (f==NULL)
      error(errbuf) ;
   else {
      if (! quiet) {
         if (strlen(s) + prettycolumn > STDOUTSIZE) {
            fprintf(stderr, "\n") ;
            prettycolumn = 0 ;
         }
#ifdef VMCMS
         (void)fprintf(stderr, "<%s>", trunc_s) ;
#else
#ifdef MVSXA
         (void)fprintf(stderr, "<%s>", trunc_s) ;
#else
         (void)fprintf(stderr, "<%s>", s) ;
#endif
#endif
         (void)fflush(stderr) ;
         prettycolumn += 2 + strlen(s) ;
      }
      if (linepos != 0)
         (void)putc('\n', bitfile) ;
/*
 *   Suggested by Andrew Trevorrow; don't ever BeginFont a file ending in .enc
 */
      if (infont && strstr(s,".enc"))
         infont = 0 ;
      if (! disablecomments) {
         if (infigure)
            (void)fprintf(bitfile, "%%%%BeginDocument: %s\n", s) ;
         else if (infont)
            (void)fprintf(bitfile, "%%%%BeginFont: %s\n", infont) ;
#ifdef HPS
         else if (noprocset) {}
#endif
         else
            (void)fprintf(bitfile, "%%%%BeginProcSet: %s 0 0\n", s) ;
      }
      c = getc(f) ;
      if (c == 0x80) {
         while (1) {
            c = getc(f) ;
            switch(c) {
case 1:
case 2:
               len = getc(f) ;
               len += getc(f) * 256L ;
               len += getc(f) * 65536L ;
               len += getc(f) * 256L * 65536 ;
               if (c == 1) {
                  while (len > 0) {
                     c = getc(f) ;
                     if (c == EOF) {
                        error("premature EOF in MS-DOS font file") ;
                        len = 0 ;
                     } else {
		        if (c == '\r') { /* Mac- or DOS-style text file */
                           (void)putc('\n', bitfile) ;
			   if ((c = getc(f)) == '\n') /* DOS-style text */
			      len--; /* consume, but don't generate NL */
			   else
			      ungetc(c, f);
			}
                        else
                           (void)putc(c, bitfile) ;
                        len-- ;
                     }
                  }
               } else {
                  putc('\n', bitfile) ;
                  prevc = 0 ;
                  while (len > 0) {
                     c = getc(f) ;
                     if (c == EOF) {
                        error("premature EOF in MS-DOS font file") ;
                        len = 0 ;
                     } else {
                        (void)putc(hxdata[c >> 4], bitfile) ;
                        (void)putc(hxdata[c & 15], bitfile) ;
                        len-- ;
                        prevc += 2 ;
                        if (prevc >= 76) {
                           putc('\n', bitfile) ;
                           prevc = 0 ;
                        }
                     }
                  }
               }
               break ;
case 3:
               goto msdosdone ;
default:
               error("saw type other than 1, 2, or 3 in MS-DOS font file") ;
               break ;
            }
            c = getc(f) ;
            if (c == EOF)
               break ;
            if (c != 0x80) {
               error("saw non-MSDOS header in MSDOS font file") ;
               break ;
            }
         }
msdosdone:
         prevc = 0 ;
      } else {
/* begin DOS EPS code */
         if (c == 'E'+0x80) {
            if ((getc(f)=='P'+0x80) && (getc(f)=='S'+0x80)
	                            && (getc(f)=='F'+0x80)) {
               doseps = 1;
               dosepsbegin = getc(f) ;
               dosepsbegin += getc(f) * 256L ;
               dosepsbegin += getc(f) * 65536L ;
               dosepsbegin += getc(f) * 256L * 65536 ;
               dosepsend = getc(f) ;
               dosepsend += getc(f) * 256L ;
               dosepsend += getc(f) * 65536L ;
               dosepsend += getc(f) * 256L * 65536 ;
               fseek(f, dosepsbegin, 0);
               c = getc(f);
               dosepsend-- ;
            }
            else {
               rewind(f);
               c = getc(f);
            }
         }
/* end DOS EPS code */
         if (c != EOF) {
            while (1) {
               if (c == '\n') { /* end or beginning of line; check DSC */
                  *dscLinePointer = 0 ; /* make sure we terminate!
                                         * might be a new empty line! */
                  if (strncmp(possibleDSCLine, "%%BeginBinary:", 14) == 0 ||
                      strncmp(possibleDSCLine, "%%BeginData:", 12) == 0) {
                     integer size = 0 ;
                     char *p = possibleDSCLine ;
                     *dscLinePointer = 0 ;
                     *dscLineEnd = 0 ;
                     if (scanForEnd == 0 && removecomments)
                        (void)fputs(possibleDSCLine, bitfile) ;
                     scanForEnd = 0 ;
                     while (*p != ':')
                        p++ ;
                     p++ ;
                     while (*p && *p <= ' ')
                        p++ ;
                     if ('0' > *p || *p > '9') {
                        /*
                         *   No byte count!  We need to scan for end binary
                         *   or end data, and hope we get it right.  Really
                         *   the file is malformed.
                         */
                        scanForEnd = "Yes" ;
                     }
                     while ('0' <= *p && *p <= '9') {
                        size = size * 10 + *p - '0' ;
                        p++ ;
                     }
                     while (*p && *p <= ' ')
                        p++ ;
                     if (*p == 'h' || *p == 'H')
                        /*
                         *   Illustrator 8 and 9 have bogus byte counts
                         *   for hex data.  But if it is hex, we assume
                         *   that it is safe to use ASCII scanning, so
                         *   we do so.
                         */
                        scanForEnd = "Yes" ;
                     while (*p > ' ') /* ignore Hex/Binary/ASCII */
                        p++ ;
                     while (*p && *p <= ' ')
                        p++ ;
                     (void)putc(c, bitfile) ;
                     if (c == '\r') { /* maybe we have a DOS-style text file */
                        c = getc(f);
                        if (c == '\n') {
                           (void)putc(c, bitfile);
                           dosepsend-- ;
                        } else
                           ungetc(c, f);
                     }
                     if (scanForEnd != 0) {
                        if (strncmp(possibleDSCLine, "%%BeginBinary", 13) == 0)
                           scanForEnd = "%%EndBinary" ;
                        else
                           scanForEnd = "%%EndData" ;
                     }
                     if (scanForEnd == 0) {
                        if (strncmp(p, "lines", 5) != 0 &&
                            strncmp(p, "Lines", 5) != 0) {
                           for (; size>0; size--) {
                              c = getc(f) ;
                              dosepsend-- ;
                              if (c == EOF)
                                 error(
                                 "! premature end of file in binary section") ;
                              (void)putc(c, bitfile) ;
                           }
                        } else {
                           /*
                            *  Count both newlines and returns, and when either
                            *  goes over the count, we are done.
                            */
                           int newlines=0, returns=0 ;
                           while (newlines < size && returns < size) {
                              c = getc(f) ;
                              dosepsend-- ;
                              if (c == EOF)
                                 error(
                                    "! premature end of file in binary section") ;
                              (void)putc(c, bitfile) ;
                              if (c == '\n')
                                 newlines++ ;
                              else if (c == '\r')
                                 returns++ ;
                           }
                           /*
                            *   If we've seen precisely one too few newlines,
                            *   and the next char is a newline, include it too.
                            */
                           if (returns == newlines + 1) {
                              if ((c = getc(f)) == '\n') {
                                 putc(c, bitfile) ;
                                 dosepsend-- ;
                              } else {
                                 ungetc(c, f);
                              }
                           }
                        }
                        c = getc(f) ;
                        dosepsend-- ;
                        if (c == '\n' || c == '\r') {
                           (void)putc(c, bitfile) ;
			   if (c == '\r') { /* DOS-style text file? */
			      c = getc(f);
                              dosepsend-- ;
			      if (c == '\n') {
			         putc(c, bitfile);
			         c = getc(f);
                                 dosepsend-- ;
			      }
			   } else {
			      c = getc(f) ;
                              dosepsend-- ;
                           }
                        }
                        if (c != '%') {
                           /*   try to find %%EndBinary or %%EndData anywhere
                                in the rest of the file, and pretend it
                                worked; this works around various Illustrator
                                bugs.   -tgr, 14 June 2003                  */
                           char *m1 = "%%EndData" ;
                           char *m2 = "%%EndBinary" ;
                           char *p1 = m1 ;
                           char *p2 = m2 ;
                           error(
               " expected to see %%EndBinary at end of data; struggling on") ;
                           while (1) {
                              (void)putc(c, bitfile) ;
                              if (c == '\r' || c == '\n') {
			         if (c == '\r') { /* DOS-style text file? */
			            c = getc(f);
			            if (c != '\n')
				       ungetc(c, f);
                                    else
                                       dosepsend-- ;
			         }
                                 break ;
			      }
                              c = getc(f) ;
                              dosepsend-- ;
                              if (c == EOF)
                                 error(
                                 "! premature end of file in binary section") ;
 /*
  *   By the way, this code can be fooled by things like %%%EndBinary
  *   or even %%EndBi%%EndBinary, but this isn't valid DSC anyway.
  *   This comment is mainly here to prevent anyone else from emulating
  *   this code when doing stream-based substring matching.
  */
                              if (c == *p1) {
                                 p1++ ;
                                 if (*p1 == 0)
                                    break ;
                              } else {
                                 p1 = m1 ;
                              }
                              if (c == *p2) {
                                 p2++ ;
                                 if (*p2 == 0)
                                    break ;
                              } else {
                                 p2 = m2 ;
                              }
                           }
                        }
                        while (1) {
                           (void)putc(c, bitfile) ;
                           if (c == '\r' || c == '\n') {
			      if (c == '\r') { /* DOS-style text file? */
			         c = getc(f);
			         if (c != '\n')
				    ungetc(c, f);
                                 else
                                    dosepsend-- ;
			      }
                              break ;
			   }
                           c = getc(f) ;
                           dosepsend-- ;
                           if (c == EOF)
                              error(
                                 "! premature end of file in binary section") ;
                        }
                        c = getc(f) ;
                        dosepsend-- ;
                     }
                  } else if (scanForEnd && strncmp(possibleDSCLine, scanForEnd,
                                                   strlen(scanForEnd))==0) {
                     scanForEnd = 0 ;
                  }
                  dscLinePointer = possibleDSCLine ;
               } else if (dscLinePointer < dscLineEnd) {
                  *dscLinePointer++ = c ;
                  if (removecomments && scanForEnd == 0 &&
                      c == '%' && dscLinePointer == possibleDSCLine + 1) {
                     /* % is first char */
                     c = getc(f) ;
                     if (c == '%' || c == '!')
                        removingBytes = 1 ;
                     if (c != EOF)
                        ungetc(c, f) ;
                     c = '%' ;
                  }
               }
#ifdef VMCMS
               if (c != 0x37 ) {
#else
#ifdef MVSXA
               if (c != 0x37 ) {
#else
               if (c != 4) {
#endif
#endif
                  if (!removingBytes)
                     (void)putc(c, bitfile) ;
               }
               prevc = c ;
/* begin DOS EPS code */
               if (doseps && (dosepsend <= 0))
                  break;      /* stop at end of DOS EPS PostScript section */
/* end DOS EPS code */
               c = getc(f) ;
               dosepsend-- ;
               if (c == EOF)
                  break ;
               else if (c == '\r') {
		  c = getc(f);
		  if (c == '\n') { /* DOS-style text file? */
		     if (!removingBytes) (void)putc('\r', bitfile);
                     dosepsend-- ;
		  } else
		     ungetc(c, f);
                  c = '\n' ;
	       }
               if (prevc == '\n')
                  removingBytes = 0 ;
            }
         }
      }
      if (prevc != '\n')
         (void)putc('\n', bitfile) ;
      linepos = 0 ;
#ifndef VMCMS
#ifndef MVSXA
#ifndef VMS
#if !defined(MSDOS) || defined(__DJGPP__)
#ifndef ATARIST
#ifndef __THINK__
      if (infigure == 2)
#ifdef OS2
         {
            if (_osmode == OS2_MODE)
               (void)pclose(f) ;
         }
#else
         (void)pclose(f) ;
#endif
      else
#endif
#endif
#endif
#endif
#endif
#endif
         (void)fclose(f) ;
      if (!disablecomments) {
         if (infigure)
            (void)fprintf(bitfile, "\n%%%%EndDocument\n") ;
         else if (infont)
            (void)fprintf(bitfile, "\n%%%%EndFont\n") ;
#ifdef HPS
         else if (noprocset) {}
#endif
         else
            (void)fprintf(bitfile, "\n%%%%EndProcSet\n") ;
      }
   }
}

/*
 *   For included PostScript graphics, we use the above routine, but
 *   with no fatal error message.
 */
void figcopyfile P2C(char *, s, int, systemtype)
{
   infigure = systemtype ? 2 : 1 ;
   copyfile(s) ;
   infigure = 0 ;
}
/*
 *   This next routine writes out a `special' character.  In this case,
 *   we simply put it out, since any special character terminates the
 *   preceding token.
 */
void
specialout P1C(char, c)
{
   if (linepos >= LINELENGTH) {
      (void)putc('\n', bitfile) ;
      linepos = 0 ;
   }
   (void)putc(c, bitfile) ;
   linepos++ ;
   lastspecial = 1 ;
}

void
stringend P1H(void)
{
   if (linepos + instring >= LINELENGTH - 2) {
      (void)putc('\n', bitfile) ;
      linepos = 0 ;
   }
   (void)putc('(', bitfile) ;
   *strbp = 0 ;
   (void)fputs(strbuffer, bitfile) ;
   (void)putc(')', bitfile) ;
   linepos += instring + 2 ;
   lastspecial = 1 ;
   instring = 0 ;
   strbp = strbuffer ;
}

#ifdef SHIFTLOWCHARS
/* 
 *   moving chars 0-32 and 127 to higher positions
 *   is desirable when using some fonts
 */
int T1Char P1C(int, c)
{
  int tmpchr = c;
  if (shiftlowchars && curfnt->resfont) {
    if ((tmpchr <= 0x20)&&(tmpchr>=0)) {
      if (tmpchr < 0x0A) {
        tmpchr += 0xA1;
      }
      else {
        tmpchr += 0xA3;
      }
    }
    else if (tmpchr == 0x7F) {
      tmpchr = 0xC4;
    }
  }
  if (curfnt->chardesc[tmpchr].flags2 & EXISTS)
    tmpchr = c ;
  return tmpchr;
}
#endif

void
scout P1C(unsigned char, c)   /* string character out */
{
/*
 *   Is there room in the buffer?  LINELENGTH-6 is used because we
 *   need room for (, ), and a possible four-byte string \000, for
 *   instance.  If it is too long, we send out the string.
 */
   if (instring > LINELENGTH-6) {
      stringend() ;
      chrcmd('p') ;
   }
#ifdef SHIFTLOWCHARS
   c=T1Char(c);
#endif
/*  changed next line to hex representation for VMCMS port
   if (c<' ' || c > 126 || c=='%' ) {
*/
   if ( c<0x20 || c>= 0x7F || c==0x25 ) {
      *strbp++ = '\\' ;
      *strbp++ = '0' + ((c >> 6) & 3) ;
      *strbp++ = '0' + ((c >> 3) & 7) ;
      *strbp++ = '0' + (c & 7) ;
      instring += 4 ;
   } else {
#ifdef VMCMS
     c = ascii2ebcdic[c];
#else
#ifdef MVSXA
     c = ascii2ebcdic[c];
#endif
#endif
     if (c == '(' || c == ')' || c == '\\') {
       *strbp++ = '\\' ;
       *strbp++ = c ;
       instring += 2 ;
     } else {
       *strbp++ = c ;
       instring++ ;
     }
   }
}

void
scout2 P1C(int, c)
{
   char s[64] ;

   sprintf(s, "<%04x>p", c) ;
   cmdout(s) ;
}

void
cmdout P1C(char *, s)
{
   int l ;

   /* hack added by dorab */
   if (instring) {
        stringend();
        chrcmd('p');
   }
   l = strlen(s) ;
   if ((! lastspecial && linepos >= LINELENGTH - 20) ||
           linepos + l >= LINELENGTH) {
      (void)putc('\n', bitfile) ;
      linepos = 0 ;
      lastspecial = 1 ;
   } else if (! lastspecial) {
      (void)putc(' ', bitfile) ;
      linepos++ ;
   }
   (void)fputs(s, bitfile) ;
   linepos += l ;
   lastspecial = 0 ;
}


static void
chrcmd P1C(char, c)
{
   if ((! lastspecial && linepos >= LINELENGTH - 20) ||
       linepos + 2 > LINELENGTH) {
      (void)putc('\n', bitfile) ;
      linepos = 0 ;
      lastspecial = 1 ;
   } else if (! lastspecial) {
      (void)putc(' ', bitfile) ;
      linepos++ ;
   }
   (void)putc(c, bitfile) ;
   linepos++ ;
   lastspecial = 0 ;
}

void
floatout P1C(float, n)
{
   char buf[20] ;

   (void)sprintf(buf, "%.2f", n) ;
   cmdout(buf) ;
}

void doubleout P1C(double, n)
{
   char buf[40] ;

   (void)sprintf(buf, "%g", n) ;
   cmdout(buf) ;
}

void
numout P1C(integer, n)
{
   char buf[10] ;

#ifdef SHORTINT
   (void)sprintf(buf, "%ld", n) ;
#else
   (void)sprintf(buf, "%d", n) ;
#endif
   cmdout(buf) ;
}

void
mhexout P2C(register unsigned char *, p,
	    register long, len)
{
   register char *hexchar = hxdata ;
   register int n, k ;

   while (len > 0) {
      if (linepos > LINELENGTH - 2) {
         (void)putc('\n', bitfile) ;
         linepos = 0 ;
      }
      k = (LINELENGTH - linepos) >> 1 ;
      if (k > len)
         k = len ;
      len -= k ;
      linepos += (k << 1) ;
      while (k--) {
         n = *p++ ;
         (void)putc(hexchar[n >> 4], bitfile) ;
         (void)putc(hexchar[n & 15], bitfile) ;
      }
   }
}

void
fontout P1C(int, n)
{
   char buf[6] ;

   if (instring) {
      stringend() ;
      chrcmd('p') ;
   }
   makepsname(buf, n) ;
   cmdout(buf) ;
}

void
hvpos P1H(void)
{
   if (rvv != vv) {
      if (instring) {
         stringend() ;
         numout(hh) ;
         numout(vv) ;
         chrcmd('y') ;
      } else if (rhh != hh) {
         numout(hh) ;
         numout(vv) ;
         chrcmd('a') ;
      } else { /* hard to get this case, but it's there when you need it! */
         numout(vv - rvv) ;
         chrcmd('x') ;
      }
      rvv = vv ;
   } else if (rhh != hh) {
      if (instring) {
         stringend() ;
         if (hh - rhh < 5 && rhh - hh < 5) {
#ifdef VMCMS /*  should replace 'p' in non-VMCMS line as well */
            chrcmd(ascii2ebcdic[(char)(112 + hh - rhh)]) ;
#else
#ifdef MVSXA /*  should replace 'p' in non-MVSXA line as well */
            chrcmd(ascii2ebcdic[(char)(112 + hh - rhh)]) ;
#else
            chrcmd((char)('p' + hh - rhh)) ;
#endif
#endif
         } else if (hh - rhh < d + 5 && rhh - hh < 5 - d) {
#ifdef VMCMS /* should replace 'g' in non-VMCMS line as well  */
            chrcmd(ascii2ebcdic[(char)(103 + hh - rhh - d)]) ;
#else
#ifdef MVSXA /* should replace 'g' in non-MVSXA line as well  */
            chrcmd(ascii2ebcdic[(char)(103 + hh - rhh - d)]) ;
#else
            chrcmd((char)('g' + hh - rhh - d)) ;
#endif
#endif
            d = hh - rhh ;
         } else {
            numout(hh - rhh) ;
            chrcmd('b') ;
            d = hh - rhh ;
         }
      } else {
         numout(hh - rhh) ;
         chrcmd('w') ;
      }
   }
   rhh = hh ;
}

/*
 *   initprinter opens the bitfile and writes the initialization sequence
 *   to it.
 */
void newline P1H(void)
{
   if (linepos != 0) {
      (void)fprintf(bitfile, "\n") ;
      linepos = 0 ;
   }
   lastspecial = 1 ;
}

void
nlcmdout P1C(char *, s)
{
   newline() ;
   cmdout(s) ;
   newline() ;
}
/*
 *   Is the dimension close enough for a match?  We use a quarter inch
 *   as a match; this is 65536*72.27/4 or 1,184,072 scaled points.
 */
static int indelta P1C(integer, i)
{
   if (i < 0)
      i = -i ;
   return (i <= 1184072) ;
}
/*
 *   A case-irrelevant string compare.
 */
int mlower P1C(int, c)
{
   if ('A' <= c && c <= 'Z')
      return c - 'A' + 'a' ;
   else
      return c ;
}
int ncstrcmp P2C(char *, a, char *, b)
{
   while (*a && (*a == *b ||
                       mlower(*a) == mlower(*b)))
      a++, b++ ;
   if (*a == 0 && *b == 0)
      return 0 ;
   else
      return 1 ;
}
/*
 *   Find the paper size.
 */
void findpapersize P1H(void) {
   if (finpapsiz == 0) {
      struct papsiz *ps ;

      if (tryepsf && !landscape) {
         finpapsiz = &defpapsiz ;
         hpapersize = defpapsiz.xsize ;
         vpapersize = defpapsiz.ysize ;
         return ;
      }
      if (cropmarks) {
/*
 *   If user wanted crop marks, we increase the size of the page by
 *   a half inch all around.
 */
         if (hpapersize == 0 || vpapersize == 0) {
            error(
 "warning: -k crop marks wanted, but no paper size specified; using default") ;
            if (landscape) {
               hpapersize = defpapsiz.ysize ;
               vpapersize = defpapsiz.xsize ;
            } else {
               hpapersize = defpapsiz.xsize ;
               vpapersize = defpapsiz.ysize ;
            }
         }
         hpapersize += 2368143L ;
         vpapersize += 2368143L ;
         add_header(CROPHEADER) ;
      }
      if (paperfmt && *paperfmt) {
         for (ps = papsizes; ps; ps = ps->next)
            if (ncstrcmp(paperfmt, ps->name)==0)
               finpapsiz = ps ;
         if (finpapsiz == 0)
            error("no match for papersize") ;
      }
      if (finpapsiz == 0 && hpapersize > 0 && vpapersize > 0) {
         for (ps=papsizes; ps; ps = ps->next) {
            if (indelta(ps->xsize-hpapersize) &&
                indelta(ps->ysize-vpapersize)) {
               landscape = 0 ;
               break ;
            }
         }
         if (ps == 0) {
            for (ps=papsizes; ps; ps = ps->next) {
               if (indelta(ps->ysize-hpapersize) &&
                   indelta(ps->xsize-vpapersize)) {
                  landscape = 1 ;
                  break ;
               }
            }
            if (ps == 0) {
               for (ps=papsizes; ps; ps = ps->next) {
                  if (ps->ysize == 0 && ps->xsize == 0)
                     break ;
               }
               if (ps == 0) {
                  landscape = (hpapersize > vpapersize) ;
                  error(
                    "no match for special paper size found; using default") ;
               }
            }
         }
         finpapsiz = ps ;
      }
      if (finpapsiz == 0) {
         if (papsizes)
            finpapsiz = papsizes ;
         else
            finpapsiz = &defpapsiz ;
/*
 *   But change xsize/ysize to match so bounding box works.
 */
         if (hpapersize && vpapersize) {
            if (landscape) {
               finpapsiz->ysize = hpapersize ;
               finpapsiz->xsize = vpapersize ;
            } else {
               finpapsiz->xsize = hpapersize ;
               finpapsiz->ysize = vpapersize ;
            }
         }
      }
/*
 *   Here, there was no papersize special.  We set the paper size from
 *   the selected paper format.  If the selected paper format has no
 *   sizes, we use the defaults.
 */
      if (hpapersize == 0 || vpapersize == 0) {
         if (finpapsiz->xsize == 0 || finpapsiz->ysize == 0) {
            finpapsiz->xsize = defpapsiz.xsize ;
            finpapsiz->ysize = defpapsiz.ysize ;
         }
         if (landscape) {
            vpapersize = finpapsiz->xsize ;
            hpapersize = finpapsiz->ysize ;
         } else {
            hpapersize = finpapsiz->xsize ;
            vpapersize = finpapsiz->ysize ;
         }
/*
 *   Here, there was a papersize special, but the selected paper
 *   format has 0 0 for sizes.  We set the sizes here so that the
 *   bounding box works.
 */
      } else if (finpapsiz->xsize == 0 || finpapsiz->ysize == 0) {
         finpapsiz->xsize = hpapersize ;
         finpapsiz->ysize = vpapersize ;
/*
 *   Here, the user specified a size with -t, and there was a
 *   papersize special, and its sizes were greater than zero.
 *   We make sure the sizes are okay.  Note that the user must have
 *   specified landscape if this is desired.
 */
      } else if (paperfmt && *paperfmt) {
         if (landscape) {
            if (!indelta(vpapersize - finpapsiz->xsize) ||
                !indelta(hpapersize - finpapsiz->ysize)) {
               if (vpapersize > finpapsiz->xsize ||
                   hpapersize > finpapsiz->ysize)
                  error("warning: -t selected paper may be too small") ;
               else
                  error("note: -t selected paper may be too large") ;
            }
         } else {
            if (!indelta(hpapersize - finpapsiz->xsize) ||
                !indelta(vpapersize - finpapsiz->ysize)) {
               if (hpapersize > finpapsiz->xsize ||
                   vpapersize > finpapsiz->ysize)
                  error("warning: -t selected paper may be too small") ;
               else
                  error("note: -t selected paper may be too large") ;
            }
         }
      }
   }
}
/*
 *   Convert scaled points to PostScript points.  This is the same
 *   as return (i * 72 / (65536 * 72.27)), which is the same as
 *   dividing by 65781.76, but we want to round up.
 */
static int topoints P1C(integer, i)
{
   i += 65780L ;
   return (i / 6578176L)*100 + (i % 6578176) * 100 / 6578176 ;
}
/*
 *   Send out the special paper stuff.  If `hed' is non-zero, only
 *   send out lines starting with `!' else send all other lines out.
 */
void paperspec P2C(char *, s, int, hed)
{
   int sendit ;

   while (*s) {
      s++ ;
      if (*s == '\0')
         return ;
      if (*s == '!') {
         s++ ;
         while (*s == ' ') s++ ;
         sendit = hed ;
      } else
         sendit = ! hed ;
      if (sendit) {
         while (*s && *s != '\n')
            (void)putc(*s++, bitfile) ;
         putc('\n', bitfile) ;
      } else {
         while (*s && *s != '\n')
            s++ ;
      }
   }
}
char *epsftest P1C(integer, bop)
{
   if (tryepsf && paperfmt == 0 && *iname) {
      findbb(bop+44) ;
      return nextstring ;
   }
   return 0 ;
}
static char *isepsf = 0 ;
static int endprologsent ;
void open_output P1H(void) {
   FILE * pf = NULL;
   if (*oname != 0) {
/*
 *   We check to see if the first character is a exclamation
 *   point, and popen if so.
 */
      if (*oname == '!' || *oname == '|') {
#if defined (MSDOS) && !defined (__DJGPP__)
            error("! can't open output pipe") ;
#else
#ifdef VMS
            error("! can't open output pipe") ;
#else
#ifdef VMCMS
            error("! can't open output pipe") ;
#else
#ifdef MVSXA
            error("! can't open output pipe") ;
#else
#ifdef __THINK__
            error("! can't open output pipe") ;
#else
#ifdef ATARIST
            error("! can't open output pipe") ;
#else
#ifdef OS2
         if (_osmode != OS2_MODE) {
            error("! can't open output pipe") ;
         } else {
#endif
#ifdef __DJGPP__
        /* Feature: if they pipe to "lpr" and there's no executable by
           that name anywhere in sight, write to local printer instead.

           We do this up front, before even trying to popen, because on
           MS-DOS popen always succeeds for writing (it only opens a
           temporary file), and by the time we get to actually run the
           (possibly nonexistent) program in pclose, it's too late to
           fall back.

           We don't use kpathsea functions here because they don't
           know about DOS-specific executable extensions, while we
           want to be able to find "lpr.exe", "lpr.com", "lpr.bat" etc.  */
        extern char *__dosexec_find_on_path(const char *,
                                            char **, char *) ;
        extern char **environ ;
        char *p = oname + 1 ;
        char found[FILENAME_MAX] ;

        while (ISSPACE(*p))
          p++ ;
        if (strncmp(p, "lpr", 3) == 0 && (ISSPACE(p[3]) || p[3] == '\0')
            && !__dosexec_find_on_path(oname+1, (char **)0, found)
            && !__dosexec_find_on_path(oname+1, environ, found))
           pf = fopen("PRN", "w") ;
#endif
	 if (pf == NULL && (pf = popen(oname+1, "w")) != NULL)
	    popened = 1;
         if (pf == NULL)
            error("! couldn't open output pipe") ;
	 bitfile = pf;
#ifdef OS2
         }
#endif
#endif
#endif
#endif
#endif
#endif
#endif
      } else {
         if ((bitfile=fopen(oname,"w"))==NULL)
            error("! couldn't open PostScript file") ;
      }
   } else {
      bitfile = stdout ;
   }

   /* Even PostScript output may include binary characters, so switch
      bitfile to binary mode.  */
   if (O_BINARY && !isatty(fileno(bitfile)))
      SET_BINARY(fileno(bitfile)) ;
}
void
initprinter P1C(sectiontype *, sect)
{
   void tell_needed_fonts() ;
   int n = sect->numpages * pagecopies * collatedcopies ;
#ifdef HPS
   if (!HPS_FLAG)
#endif
      open_output() ;

   findpapersize() ;
   if (disablecomments) {
      (void)fprintf(bitfile,
             "%%!PS (but not EPSF; comments have been disabled)\n") ;
      (void)fprintf(stderr, "Warning:  no %%%%Page comments generated.\n") ;
   } else {
      if (multiplesects) {
         (void)fprintf(bitfile,
             "%%!PS (but not EPSF because of memory limits)\n") ;
         (void)fprintf(stderr, "Warning:  no %%%%Page comments generated.\n") ;
      } else {
         isepsf = epsftest(sect->bos) ;
         if (isepsf)
            (void)fprintf(bitfile, "%%!PS-Adobe-2.0 EPSF-2.0\n") ;
         else
            (void)fprintf(bitfile, "%%!PS-Adobe-2.0\n") ;
      }
      if (tryepsf && isepsf == 0)
         error("We tried, but couldn't make it EPSF.") ;
      (void)fprintf(bitfile, "%%%%Creator: %s\n", banner + 8) ;
      if (*iname)
         (void)fprintf(bitfile, "%%%%Title: %s\n", iname) ;
#ifdef CREATIONDATE
      jobtime=time(0);
      (void)fprintf(bitfile, "%%%%CreationDate: %s",
                                 asctime(localtime(&jobtime))) ;
#endif
      if (! isepsf) {
/*
 *   Normally, we wouldn't want to add that second field
 *   indicating that the page order is reversed, as per page
 *   644 of the Red book.  But we have to, for many existing
 *   spoolers.
 */
        (void)fprintf(bitfile, "%%%%Pages: %d%s\n", (sepfiles ? n : totalpages),
                                                    (reverse?" -1":"")) ;
        (void)fprintf(bitfile, "%%%%PageOrder: %sscend\n", reverse?"De":"A");
      }
      if (landscape) {
         fprintf(bitfile, "%%%%Orientation: Landscape\n") ;
         fprintf(bitfile, "%%%%BoundingBox: 0 0 %d %d\n",
              topoints(finpapsiz->xsize), topoints(finpapsiz->ysize)) ;
      } else if (isepsf)
         fprintf(bitfile, "%%%%BoundingBox: %s\n", isepsf) ;
      else
         fprintf(bitfile, "%%%%BoundingBox: 0 0 %d %d\n",
              topoints(finpapsiz->xsize), topoints(finpapsiz->ysize)) ;
      tell_needed_fonts() ;
      paperspec(finpapsiz->specdat, 1) ;
      (void)fprintf(bitfile, "%%%%EndComments\n") ;
   }
   {
      int i, len ;
      char *p ;

/*
 *   Here, too, we have to be careful not to exceed the line length
 *   limitation, if possible.
 */
      (void)fprintf(bitfile, "%%DVIPSWebPage: %s\n", banner2) ;
      (void)fprintf(bitfile, "%%DVIPSCommandLine:") ;
      len = 18 ;
      for (i=0; i<gargc; i++) {
         p = gargv[i] ;
         while (*p > ' ')
            p++ ;
         if (*p)
            len += 2 ;
         len += strlen(gargv[i]) + 1 ;
         if (len > LINELENGTH) {
            (void)fprintf(bitfile, "\n%%+") ;
            len = strlen(gargv[i]) + 3 ;
            if (*p)
               len += 2 ;
         }
         (void)fprintf(bitfile, (*p ? " \"%s\"" : " %s"), gargv[i]) ;
      }
      (void)fprintf(bitfile, "\n%%DVIPSParameters: dpi=%d", actualdpi) ;
      if (actualdpi != vactualdpi)
         (void)fprintf(bitfile, "x%d", vactualdpi) ;
      if (compressed)
         (void)fprintf(bitfile, ", compressed") ;
      if (removecomments)
         (void)fprintf(bitfile, ", comments removed") ;
      (void)fputc('\n', bitfile) ;
   }
#ifdef VMCMS  /* convert preamblecomment to ebcdic so we can read it */
   {
      int i ;
      for ( i=0 ; preamblecomment[i] ; i++ )
          preamblecomment[i] = ascii2ebcdic[preamblecomment[i]] ;
   }
#else
#ifdef MVSXA   /* IBM: MVS/XA */
   {
      int i ;
      for ( i=0 ; preamblecomment[i] ; i++ )
          preamblecomment[i] = ascii2ebcdic[preamblecomment[i]] ;
   }
#endif  /* VMCMS */
#endif
   (void)fprintf(bitfile, "%%DVIPSSource: %s\n", preamblecomment) ;
   linepos = 0 ;
   endprologsent = 0 ;
   if (safetyenclose)
      (void)fprintf(bitfile, "/SafetyEnclosure save def\n") ;
   if (! headers_off)
      send_headers() ;
}
void setup P1H(void) {
   newline() ;
   if (endprologsent == 0 && !disablecomments) {
      (void)fprintf(bitfile, "%%%%EndProlog\n") ;
      (void)fprintf(bitfile, "%%%%BeginSetup\n") ;
      if (vactualdpi == actualdpi)
         (void)fprintf(bitfile, "%%%%Feature: *Resolution %ddpi\n",
                                           actualdpi) ;
      else
         (void)fprintf(bitfile, "%%%%Feature: *Resolution %dx%ddpi\n",
                                           actualdpi, vactualdpi) ;
      if (multiplesects && *(finpapsiz->specdat)) {
         (void)fprintf(bitfile, "TeXDict begin\n") ;
         paperspec(finpapsiz->specdat, 0) ;
         (void)fprintf(bitfile, "end\n") ;
      }
      if (manualfeed)
         (void)fprintf(bitfile, "%%%%Feature: *ManualFeed True\n") ;
#ifdef HPS
      if (!HPS_FLAG)
#endif
      if (multiplesects)
         (void)fprintf(bitfile, "%%%%EndSetup\n") ;
   }
   if (multiplesects && ! disablecomments)
      (void)fprintf(bitfile, "%%DVIPSBeginSection\n") ;
   cmdout("TeXDict") ;
   cmdout("begin") ;
   if (endprologsent || disablecomments || multiplesects == 0) {
      (void)fprintf(bitfile, "\n") ;
      paperspec(finpapsiz->specdat, 0) ;
   }
   if (manualfeed) cmdout("@manualfeed") ;
   if (landscape) cmdout("@landscape") ;
   if (numcopies != 1) {
      numout((integer)numcopies) ;
      cmdout("@copies") ;
   }
   cmdout("end") ;
   if (endprologsent == 0 && !disablecomments) {
      newline() ;
      endprologsent = 1 ;
#ifdef HPS
      if (!HPS_FLAG)
#endif
         if (! multiplesects)
            (void)fprintf(bitfile, "%%%%EndSetup\n") ;
   }
#ifdef HPS
  if (HPS_FLAG) {
    fclose(bitfile) ;
    set_bitfile("body.tmp",0) ;
  }
#endif
}
/*
 *   cleanprinter is the antithesis of the above routine.
 */
void
cleanprinter P1H(void)
{
   (void)fprintf(bitfile, "\n") ;
   (void)fprintf(bitfile, "userdict /end-hook known{end-hook}if\n") ;
   if (safetyenclose)
      (void)fprintf(bitfile, "SafetyEnclosure restore\n") ;
   if (!disablecomments)
      (void)fprintf(bitfile, "%%%%EOF\n") ;
   if (sendcontrolD)
      (void)putc(4, bitfile) ;
   if (ferror(bitfile))
      error("Problems with file writing; probably disk full.") ;
#if !defined(MSDOS) || defined(__DJGPP__)
#ifndef VMS
#ifndef MVSXA
#ifndef VMCMS
#ifndef __THINK__
#ifndef ATARIST
#ifdef OS2
   if (_osmode == OS2_MODE)
#endif
      if (popened)
         (void)pclose(bitfile) ;
#endif
#endif
#endif
#endif
#endif
#endif
   if (popened == 0)
      (void)fclose(bitfile) ;
   bitfile = NULL ;
}

/* this tells dvips that it has no clue where it is. */
static int thispage = 0 ;
static integer rulex, ruley ;
void psflush P1H(void) {
   rulex = ruley = rhh = rvv = -314159265 ;
   lastfont = -1 ;
}
/*
 *   pageinit initializes the output variables.
 */
void
pageinit P1H(void)
{
   psflush() ;
   newline() ;
   thispage++ ;
   if (!disablecomments)
      if (multiplesects)
#ifdef SHORTINT
         (void)fprintf(bitfile, "%%DVIPSSectionPage: %ld\n", pagenum) ;
      else if (! isepsf)
         (void)fprintf(bitfile, "%%%%Page: %ld %d\n", pagenum, thispage) ;
#else
         (void)fprintf(bitfile, "%%DVIPSSectionPage: %d\n", pagenum) ;
      else if (! isepsf)
         (void)fprintf(bitfile, "%%%%Page: %d %d\n", pagenum, thispage) ;
#endif
   linepos = 0 ;
   cmdout("TeXDict") ;
   cmdout("begin") ;
#ifdef HPS
   if (HPS_FLAG) {
      cmdout("HPSdict") ;
      cmdout("begin") ;
   }
#endif
   if (landscape) cmdout("@landscape") ;
   numout((integer)pagenum) ;
   numout((integer)thispage-1) ;
   cmdout("bop") ;
   d = 0 ;
}



/*
 *   This routine ends a page.
 */
void
pageend P1H(void)
{
   if (instring) {
      stringend() ;
      chrcmd('p') ;
   }
   cmdout("eop") ;
   cmdout("end") ;
#ifdef HPS
   if (HPS_FLAG)
      cmdout("end") ;
#endif
}

/*
 *   drawrule draws a rule at the specified position.
 *   It does nothing to save/restore the current position,
 *   or even draw the current string.  (Rules are normally
 *   set below the baseline anyway, so this saves us on
 *   output size almost always.)
 */
void
drawrule P2C(integer, rw, integer, rh)
{
   numout((integer)hh) ;
   numout((integer)vv) ;
   if (rw == rulex && rh == ruley)
      chrcmd('V') ;
   else {
      numout((integer)rw) ;
      numout((integer)rh) ;
      chrcmd('v') ;
      rulex = rw ;
      ruley = rh ;
   }
}

/*
 *   drawchar draws a character at the specified position.
 */
void
drawchar P2C(chardesctype *, c, int, cc)
{
   hvpos() ;
   if (lastfont != curfnt->psname) {
      fontout((int)curfnt->psname) ;
      lastfont = curfnt->psname ;
   }
   if (curfnt->codewidth==1) scout((unsigned char)cc) ;
   else scout2(cc) ;
   rhh = hh + c->pixelwidth ; /* rvv = rv */
}
/*
 *   This routine sends out the document fonts comment.
 */
void tell_needed_fonts P1H(void) {
   struct header_list *hl = ps_fonts_used ;
   char *q ;
   int roomleft = -1 ;

   if (hl == 0)
      return ;
   while (0 != (q=get_name(&hl))) {
      if ((int)strlen(q) >= roomleft) {
         if (roomleft != -1) {
            fprintf(bitfile, "\n%%%%+") ;
            roomleft = LINELENGTH - 3 ;
         } else {
            fprintf(bitfile, "%%%%DocumentFonts:") ;
            roomleft = LINELENGTH - 16 ;
         }
      }
      fprintf(bitfile, " %s", q) ;
      roomleft -= strlen(q) + 1 ;
   }
   fprintf(bitfile, "\n") ;
}
