home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / pub / utils / textps.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  42KB  |  1,177 lines

  1. char *version = "1.01 28-Nov-2002";
  2. char *copyright = "Copyright (C) 1991, 2002, Trustees of Columbia University";
  3. /*
  4.   textps - Convert plain text to Postscript.
  5.  
  6.   DESCRIPTION:
  7.     Converts text files to PostScript Courier-11, 66 lines to the page, 80
  8.     characters to the line (or other selectable dimensions).  Handles
  9.     pagination, tabs, line wrap, overstruck characters (via BS) and
  10.     overstruck lines (via CR).  Swallows and ignores (rather than printing)
  11.     ANSI escape sequences.  If the input file is already Postscript (i.e. if
  12.     its first line starts with "%!"), it is simply copied to the output
  13.     without alteration.  No special effects like page headings, landscape,
  14.     2-up, etc.
  15.  
  16.     Unlike other "enscriptors", textps handles 8-bit character sets.  The
  17.     default file character set is CP437 on PCs, the NeXT character set on the
  18.     NeXT, and ISO 8859-1 Latin Alphabet 1 elsewhere.  CP850, DEC MCS, and
  19.     Apple QuickDraw are also supported.  Override the default character set
  20.     with the -c command-line option.  Shift-In/Shift-Out codes within the text
  21.     are handled, so 8-bit characters can be encoded by ^N<char-128>^O in the
  22.     7-bit environment (e.g. e-mail) and still print correctly.
  23.  
  24.   USAGE:
  25.     textps [ -h ] [ -v ] [ -c charset ] < input > output
  26.  
  27.     The input file character set is translated from the default character set
  28.     or the one given on the command line to ISO Latin Alphabet 1, and the
  29.     result is converted to Level-1 Postscript.  The -h command line option
  30.     prints a help message and exits immediately.  The -v option includes a
  31.     page showing the textps and PostScript version numbers.  The -c option
  32.     specifies the file's character set; charset may be latin1, cp437, cp850,
  33.     decmcs, apple, or next.
  34.  
  35.     UNIX example:   textps < file | lpr
  36.     MS-DOS example: textps < file > prn
  37.  
  38.     Suggestion for use with DOS.  Make a batch file called PSPRINT.BAT:
  39.       @echo off
  40.       textps < %1 > prn
  41.  
  42.     This assumes PRN is a Postscript printer.  Files that are already
  43.     PostScript or PCL, or seem not to be text at all, are passed through
  44.     without alteration.  Works with Novell CAPTURE.  In VMS, make a DCL
  45.     procedure similar to the above batch file.  In UNIX, use this program 
  46.     as a print filter (in /etc/printcap, or alias lpr='textps | lpr').
  47.  
  48.   ERRORS:
  49.     Returns status code of 0 on success, 1 on failure.
  50.     Only fails if it is invoked with invalid command line arguments,
  51.     in which case an error and usage message is printed on stderr.
  52.  
  53.   BUGS:
  54.     Sometimes a spurious blank page is produced.
  55.  
  56.     Not all the characters print on early model laserwriters or other very
  57.     old PostScript printers: broken bar, copyright, trade mark, not sign,
  58.     fractions, Y/y-acute and Icelandic Thorn/thorn and Eth/eth, etc.  This
  59.     is because these characters were not present in early PostScript releases.
  60.  
  61.     8-bit characters are translated into an internal character set, which is
  62.     ISO Latin Alphabet 1 with a few extensions, so any file characters that
  63.     don't don't appear in this character set, such as PC line- and box-
  64.     drawing characters, are approximated with characters like '+', '-', and
  65.     '|'.  Alphabetic or punctuation characters that have no equivalents in
  66.     Latin-1 are shown as '?'.
  67.  
  68.     Because this program was designed to operate as a print filter, it
  69.     must decide for itself whether the input stream is to be converted to
  70.     PostScript or not.  If it decides incorrectly, that would be a bug,
  71.     which should be reported to the author.
  72.  
  73.   TO BUILD:
  74.     Just compile it.  If compiled under DOS with Microsoft C (and probably
  75.     also under Xenix?), the default character set is CP437, on the NeXT
  76.     it's the NeXT character set, otherwise it's Latin-1.
  77.  
  78.     For OS/2, use the Makefile textps.os2 ("make -f textps.os2 <object>").
  79.     See textps.os2 for a list of objects.
  80.  
  81.     For Windows NT and Windows 95, the default character set is CP437.
  82.  
  83.     To build with a particular default character set, include -DCHARSET=x
  84.     on the cc command line, where x = 0 for Latin1, 1 for CP437, 2 for
  85.     CP850, 3 for NeXT, 4 for DEC MCS, 5 for Apple QuickDraw.
  86.  
  87.     To disable ANSI-escape-sequence elimination, add -DNOESCSEQ.
  88.  
  89.   UPDATES:
  90.     0.95 5 Aug 1991 
  91.       Add L procedure to output n blank lines, and don't bother to output
  92.       blank lines after last text on page.
  93.     0.96 6 Aug 1991
  94.       Make sure a file that starts with Ctrl-L still outputs the PostScript
  95.       prolog.
  96.     0.97 8 Aug 1991
  97.       Totally rewrite so we don't have to use backspacefont, which doesn't
  98.       work if used too much.  This also allows backspace overstriking to work
  99.       across line wrap boundaries.
  100.     0.98 1 Oct 1991
  101.       Fix a few translation table entries.
  102.     0.99 7 Oct 1991
  103.       Fix the top and bottom margins so 66th line doesn't sometimes have
  104.       descenders cut off.
  105.     0.995 18 Apr 1993
  106.       Fix output of already-PS files to not contain an unnecessary
  107.       bufferful of blanks.  Add #ifdef for default OS/2 character set.
  108.       Remove compiler complaint about buf in printf.
  109.       Reported by Darrel Hankerson at Auburn University.
  110.     0.996 16 Jan 1994
  111.       From Darrel Hankerson at Auburn University.  Improved OS/2 and MS-DOS
  112.       support: get the current PC code page from the operating system rather
  113.       than using a hardwired default.  New makefile for OS/2 and DOS.
  114.       Print usage() message if stdin isatty().
  115.     0.997 23 Feb 1996
  116.       Added support for Windows NT and Windows 95.
  117.     0.998 21 May 1996
  118.       Added page-length support.
  119.     0.999 10 Oct 1996
  120.       From Dale R. Worley <worley@ariadne.com>.  Clear hpos and maxhpos when
  121.       printing a blank line.  Handle spaces by incrementing hpos, rather than
  122.       inserting them into the line buffer.
  123.     1.00 6 Sep 2002
  124.       Don't convert PCL (first two bytes == <ESC>E) to PostScript.
  125.     1.01 27 Nov 2002
  126.       Include <ESC>% as a PCL identifier.
  127.  
  128.   Author: Frank da Cruz, Columbia University (fdc@columbia.edu), July 1991.
  129.   Acks:   For help with reencoding bugs: Bur Davis, Adobe.
  130.           For OS/2 and DOS improvements, Darrel Hankerson, Auburn University.
  131.  
  132.   Copyright (C) 1991, 2002,
  133.   Trustees of Columbia University in the City of New York.  This software
  134.   may not be, in whole or in part, licensed or sold for profit as a software
  135.   product itself, nor may it be included in or distributed with commercial
  136.   products or otherwise distributed by commercial enterprises or consultants
  137.   to their clients or customers without a license from the Kermit Project,
  138.   Columbia University.  This copyright notice may not be removed, altered,
  139.   or obscured.
  140.  
  141. /* Defines and Includes... */
  142.  
  143. /* For portability, we can't use logical operators in the preprocessor. */
  144. /* Here we define OS2NTDOS if either OS2, MSDOS, or NT is defined, for items */
  145. /* common to OS/2, MS-DOS, and Windows-32 ... */
  146.  
  147. #ifdef __EMX__
  148. #ifndef OS2
  149. #define OS2
  150. #endif /* OS2 */
  151. #endif /* __EMX__ */
  152.  
  153. #ifdef OS2
  154. #ifndef OS2NTDOS
  155. #define OS2NTDOS
  156. #endif /* OS2NTDOS */
  157. #endif /* OS2 */
  158.  
  159. #ifdef NT
  160. #include <windows.h>
  161. #ifndef OS2NTDOS
  162. #define OS2NTDOS
  163. #endif
  164. #endif
  165.  
  166. #ifdef MSDOS
  167. #ifndef OS2NTDOS
  168. #define OS2NTDOS
  169. #endif /* OS2NTDOS */
  170. #endif /* MSDOS */
  171.  
  172. #define NOESCSEQ    /* Needed for PCL */
  173.  
  174. #ifndef NOESCSEQ    /* Whether to swallow ANSI escape/control sequences */
  175. #define ESCSEQ
  176. #endif /* NOESCSEQ */
  177.  
  178. #define WIDTH 80    /* Portrait printer line width, characters */
  179. #define LENGTH 66    /* Portrait printer page length, lines */
  180. #define MAXLENGTH 256
  181.  
  182. /* Character set translations */
  183.  
  184. #define UNK '?'      /* What to translate an untranslatable character into */
  185. #define SP ' '                /* Space character */
  186. #define DEL 0177            /* DEL character */
  187.  
  188. /* Character set symbols */
  189.  
  190. #define LATIN1 0            /* ISO Latin Alphabet 1 */
  191. #define CP437  1            /* IBM code page 437 */
  192. #define CP850  2            /* IBM code page 850 */
  193. #define NEXT   3            /* NeXT character set */
  194. #define DECMCS 4            /* DEC multinational character set */
  195. #define APPLE  5            /* Apple QuickDraw character set */
  196.  
  197. /* Default character set depends on where we're being compiled. */
  198.  
  199. #ifndef CHARSET                /* If default not already defined */
  200. #ifdef NT
  201. #define CHARSET CP437
  202. #else /* NT */
  203. #ifdef OS2                /* See also the Dos call in main() */
  204. #define CHARSET CP850            /* A little more modern for OS/2 */
  205. #else
  206. #ifdef MSDOS                /* Symbol predefined by Microsoft C */
  207. #define CHARSET CP437            /* Default character set for PCs */
  208. #else
  209. #ifdef NeXT                /* Predefined by NeXT compiler */
  210. #define CHARSET NEXT
  211. #else
  212. #define CHARSET LATIN1            /* Default character set for others */
  213. #endif /* NeXT */
  214. #endif /* OS2 */
  215. #endif /* MSDOS */
  216. #endif /* NT */
  217. #endif /* CHARSET */
  218.  
  219. #include <stdio.h>            /* For EOF definition */
  220. #ifdef OS2NTDOS
  221. #include <io.h>                /* For isatty() */
  222. #include <string.h>
  223. #endif /* OS2NTDOS */
  224.  
  225. /*
  226.   Postscript Prolog, to be inserted at the beginning of the output file.
  227.   The %% Comments are to make the file conformant with Adobe's "Postscript 
  228.   File Structuring Conventions", which allow page-oriented operations in
  229.   Postscript previewers, page pickers, etc.
  230. */
  231. char *prolog[] = {            /* Standard prolog */
  232.     "%!PS-Adobe-1.0",            /* Works with Postscript 1.0 */
  233.     "%%Title: oofa",
  234.     "%%DocumentFonts: Courier CourierLatin1", 
  235.     "%%Creator: textps",
  236.     "%%Pages: (atend)",
  237.     "%%EndComments",
  238. /*
  239.   Postscript font reencoding.  The standard encoding does not have the
  240.   characters needed for Latin-1.
  241.  
  242.   Unfortunately, the font reencoding methods listed in the Postscript
  243.   Cookbook simply do not work with the Apple Laserwriter (even though they
  244.   did work with other Postscript devices).  The method described in the
  245.   Adobe PostScript Language Reference Manual (2nd Ed) to change from the
  246.   StandardEncoding vector to the ISOLatin1Encoding vector works only with
  247.   the LaserWriter-II, but not older LaserWriters.
  248.  
  249.   This method, suggested by Bur Davis at Adobe, works around the fact that
  250.   Courier was a "stroke font" in pre-version-47.0 Postscript, in which many of
  251.   the accented characters are composed from other characters (e.g. i-grave =
  252.   dotless i + grave).  It is probably not the most efficient possible solution
  253.   (an iterative method might be better), but it works.
  254. */
  255.     "/CourierLatin1 /Courier findfont dup dup maxlength dict begin",
  256.     "{",
  257.     "    1 index /FID ne { def } { pop pop } ifelse", 
  258.     "} forall",
  259.     "/Encoding exch 1 index get 256 array copy def", 
  260. /*
  261.   The following characters are added at the C1 positions 128-153, for printing
  262.   non-Latin1 character sets such as IBM code pages, DEC MCS, NeXT, etc.  Note
  263.   that we cannot use characters from the Symbol font.  Characters from
  264.   different fonts cannot be mixed.  Anyway, the Symbol font is not fixed-width.
  265. */
  266.     "Encoding 128 /quotesingle put",
  267.     "Encoding 129 /quotedblleft put",
  268.     "Encoding 131 /fi put",
  269.     "Encoding 132 /endash put",
  270.     "Encoding 133 /dagger put",
  271.     "Encoding 134 /periodcentered put",
  272.     "Encoding 135 /bullet put",
  273.     "Encoding 136 /quotesinglbase put",
  274.     "Encoding 137 /quotedblbase put",
  275.     "Encoding 138 /quotedblright put",
  276.     "Encoding 139 /ellipsis put",
  277.     "Encoding 140 /perthousand put",
  278.     "Encoding 141 /dotaccent put",
  279.     "Encoding 142 /hungarumlaut put",
  280.     "Encoding 143 /ogonek put",
  281.     "Encoding 144 /caron put",
  282.     "Encoding 145 /fl put",
  283.     "Encoding 146 /emdash put",
  284.     "Encoding 147 /Lslash put",
  285.     "Encoding 148 /OE put",
  286.     "Encoding 149 /lslash put",
  287.     "Encoding 150 /oe put",
  288.     "Encoding 151 /florin put",
  289.     "Encoding 152 /fraction put",
  290.     "Encoding 153 /daggerdbl put",
  291. /*
  292.   The following six characters are required for pre-47.0 PostScript versions,
  293.   which compose accented Courier characters by putting together the base
  294.   character and the accent.
  295. */
  296.     "Encoding 154 /dotlessi put",
  297.     "Encoding 155 /grave put",
  298.     "Encoding 156 /circumflex put",
  299.     "Encoding 157 /tilde put",
  300.     "Encoding 158 /breve put",
  301.     "Encoding 159 /ring put",
  302. /*
  303.   The remainder follow the normal ISO 8859-1 encoding.
  304. */
  305.     "Encoding 160 /space put",
  306.     "Encoding 161 /exclamdown put",
  307.     "Encoding 162 /cent put", 
  308.     "Encoding 163 /sterling put", 
  309.     "Encoding 164 /currency put", 
  310.     "Encoding 165 /yen put", 
  311.     "Encoding 166 /brokenbar put", 
  312.     "Encoding 167 /section put", 
  313.     "Encoding 168 /dieresis put", 
  314.     "Encoding 169 /copyright put", 
  315.     "Encoding 170 /ordfeminine put", 
  316.     "Encoding 171 /guillemotleft put", 
  317.     "Encoding 172 /logicalnot put", 
  318.     "Encoding 173 /hyphen put", 
  319.     "Encoding 174 /registered put", 
  320.     "Encoding 175 /macron put", 
  321.     "Encoding 176 /degree put", 
  322.     "Encoding 177 /plusminus put", 
  323.     "Encoding 178 /twosuperior put", 
  324.     "Encoding 179 /threesuperior put", 
  325.     "Encoding 180 /acute put", 
  326.     "Encoding 181 /mu put", 
  327.     "Encoding 182 /paragraph put", 
  328.     "Encoding 183 /bullet put", 
  329.     "Encoding 184 /cedilla put", 
  330.     "Encoding 185 /onesuperior put", 
  331.     "Encoding 186 /ordmasculine put", 
  332.     "Encoding 187 /guillemotright put", 
  333.     "Encoding 188 /onequarter put", 
  334.     "Encoding 189 /onehalf put", 
  335.     "Encoding 190 /threequarters put", 
  336.     "Encoding 191 /questiondown put", 
  337.     "Encoding 192 /Agrave put", 
  338.     "Encoding 193 /Aacute put", 
  339.     "Encoding 194 /Acircumflex put", 
  340.     "Encoding 195 /Atilde put", 
  341.     "Encoding 196 /Adieresis put", 
  342.     "Encoding 197 /Aring put", 
  343.     "Encoding 198 /AE put", 
  344.     "Encoding 199 /Ccedilla put", 
  345.     "Encoding 200 /Egrave put", 
  346.     "Encoding 201 /Eacute put", 
  347.     "Encoding 202 /Ecircumflex put", 
  348.     "Encoding 203 /Edieresis put", 
  349.     "Encoding 204 /Igrave put", 
  350.     "Encoding 205 /Iacute put", 
  351.     "Encoding 206 /Icircumflex put", 
  352.     "Encoding 207 /Idieresis put", 
  353.     "Encoding 208 /Eth put", 
  354.     "Encoding 209 /Ntilde put", 
  355.     "Encoding 210 /Ograve put", 
  356.     "Encoding 211 /Oacute put", 
  357.     "Encoding 212 /Ocircumflex put", 
  358.     "Encoding 213 /Otilde put", 
  359.     "Encoding 214 /Odieresis put", 
  360.     "Encoding 215 /multiply put", 
  361.     "Encoding 216 /Oslash put", 
  362.     "Encoding 217 /Ugrave put", 
  363.     "Encoding 218 /Uacute put", 
  364.     "Encoding 219 /Ucircumflex put", 
  365.     "Encoding 220 /Udieresis put", 
  366.     "Encoding 221 /Yacute put", 
  367.     "Encoding 222 /Thorn put", 
  368.     "Encoding 223 /germandbls put", 
  369.     "Encoding 224 /agrave put", 
  370.     "Encoding 225 /aacute put", 
  371.     "Encoding 226 /acircumflex put", 
  372.     "Encoding 227 /atilde put", 
  373.     "Encoding 228 /adieresis put", 
  374.     "Encoding 229 /aring put", 
  375.     "Encoding 230 /ae put", 
  376.     "Encoding 231 /ccedilla put", 
  377.     "Encoding 232 /egrave put", 
  378.     "Encoding 233 /eacute put", 
  379.     "Encoding 234 /ecircumflex put", 
  380.     "Encoding 235 /edieresis put", 
  381.     "Encoding 236 /igrave put", 
  382.     "Encoding 237 /iacute put", 
  383.     "Encoding 238 /icircumflex put", 
  384.     "Encoding 239 /idieresis put", 
  385.     "Encoding 240 /eth put", 
  386.     "Encoding 241 /ntilde put", 
  387.     "Encoding 242 /ograve put", 
  388.     "Encoding 243 /oacute put", 
  389.     "Encoding 244 /ocircumflex put", 
  390.     "Encoding 245 /otilde put", 
  391.     "Encoding 246 /odieresis put", 
  392.     "Encoding 247 /divide put", 
  393.     "Encoding 248 /oslash put", 
  394.     "Encoding 249 /ugrave put", 
  395.     "Encoding 250 /uacute put", 
  396.     "Encoding 251 /ucircumflex put", 
  397.     "Encoding 252 /udieresis put", 
  398.     "Encoding 253 /yacute put", 
  399.     "Encoding 254 /thorn put", 
  400.     "Encoding 255 /ydieresis put", 
  401.     "currentdict end definefont", 
  402. /*
  403.   Set the font and define functions for adding lines and printing pages.
  404. */
  405.     "/CourierLatin1 findfont 11 scalefont setfont",
  406.     "/StartPage{/sv save def 48 765 moveto}def",
  407.     "/ld -11.4 def",            /* Line spacing */
  408.     "/yline 765 def",            /* Position of top line */
  409.     "/U{show",                /* Show line, don't advance */
  410.     "  48 yline moveto}def",
  411.     "/S{show",                /* Show line, move to next line */
  412.     "  /yline yline ld add def",
  413.     "  48 yline moveto}def",
  414.     "/L{ld mul yline add /yline exch def", /* Move down n lines  */
  415.     "  48 yline moveto}def",
  416.     "/EndPage{showpage sv restore}def",
  417.     "%%EndProlog",            /* End of prolog. */
  418.     "%%Page: 1 1",            /* Number the first page. */
  419.     "StartPage",            /* And start it. */
  420.     ""                    /* Empty string = end of array. */
  421. };
  422.  
  423. /*
  424.   Translation tables from local character sets into CourierLatin1.
  425. */
  426.  
  427. /*
  428.   IBM Code Page 437.  Line- and box-drawing characters are simulated with
  429.   dashes, bars, and plus signs.  Black and gray blobs (fill characters)
  430.   are replaced by X's.  Peseta is shown as P.  Greek letters that don't
  431.   exist in CourierLatin1 are shown as ?.  Untranslatable math symbols are
  432.   shown as ?.
  433. */
  434. unsigned char
  435. y43l1[] = {
  436.   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  437.  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  438.  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  439.  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  440.  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
  441.  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
  442.  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  443. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
  444. 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197,
  445. 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 'P', 151,
  446. 225, 237, 243, 250, 241, 209, 170, 186, 191, '+', 172, 189, 188, 161, 171, 187,
  447. 'X', 'X', 'X', '|', '+', '+', '+', '+', '+', '+', '|', '+', '+', '+', '+', '+',
  448. '+', '+', '+', '+', '-', '+', '+', '+', '+', '+', '+', '+', '+', '-', '+', '+',
  449. '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+', 'X', 'X', 'X', 'X', 'X',
  450. UNK, 223, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK, UNK,
  451. UNK, 177, UNK, UNK, UNK, UNK, UNK, UNK, 176, 134, 135, UNK, 'n', 178, 'X', 160
  452. };
  453.  
  454. /*
  455.   IBM Code Page 850.  Line- and box-drawing characters are simulated with 
  456.   dashes, bars, and plus signs.  Black blobs are replaced by X's.
  457. */
  458. unsigned char
  459. y85l1[] = {
  460.   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  461.  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  462.  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  463.  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  464.  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
  465.  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
  466.  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  467. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
  468. 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197,
  469. 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 248, 163, 216, 215, 151,
  470. 225, 237, 243, 250, 241, 209, 170, 186, 191, 174, 172, 189, 188, 161, 171, 187,
  471. 'X', 'X', 'X', '|', '+', 193, 194, 192, 169, '+', '|', '+', '+', 162, 165, '+',
  472. '+', '+', '+', '+', '-', '+', 227, 195, '+', '+', '+', '+', '+', '-', '+', 164,
  473. 240, 208, 202, 203, 200, 154, 205, 206, 207, '+', '+', 'X', 'X', 166, 204, 'X',
  474. 211, 223, 212, 210, 245, 213, 181, 254, 222, 218, 219, 217, 253, 221, 175, 180,
  475. 173, 177, '=', 190, 182, 167, 247, 184, 176, 168, 134, 185, 179, 178, 'X', 160
  476. };
  477.  
  478. /*
  479.   NeXT character set.  Here we have a full translation.
  480. */
  481. unsigned char
  482. ynel1[] = {
  483.   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  484.  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  485.  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  486.  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  487.  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
  488.  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
  489.  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  490. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
  491. 160, 192, 193, 194, 195, 196, 197, 199, 200, 201, 202, 203, 204, 205, 206, 207,
  492. 208, 209, 210, 211, 212, 213, 214, 217, 218, 219, 220, 221, 222, 181, 215, 247,
  493. 169, 161, 162, 163, 152, 165, 151, 167, 164, 128, 129, 171, '<', '>', 131, 145,
  494. 174, 132, 133, 153, 134, 166, 182, 135, 136, 137, 138, 187, 139, 140, 172, 191,
  495. 185,  96, 180,  94, 126, 175, 158, 141, 168, 178, 176, 184, 179, 142, 143, 144,
  496. 146, 177, 188, 189, 190, 224, 225, 226, 227, 228, 229, 231, 232, 233, 234, 235,
  497. 236, 198, 237, 170, 238, 239, 240, 241, 147, 216, 148, 186, 242, 243, 244, 245,
  498. 246, 230, 249, 250, 251, 154, 252, 253, 149, 248, 150, 223, 254, 255, ' ', ' '
  499. };
  500.  
  501. /*
  502.   DEC Multinational Character Set (MCS).
  503. */
  504. unsigned char
  505. ydml1[] = {
  506.   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  507.  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  508.  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  509.  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  510.  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
  511.  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
  512.  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  513. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
  514. 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
  515. 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
  516. 160, 161, 162, 163, ' ', 165, ' ', 167, 164, 169, 170, 171, ' ', ' ', ' ', ' ',
  517. 176, 177, 178, 179, ' ', 181, 182, 134, ' ', 185, 186, 187, 188, 189, ' ', 191,
  518. 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
  519. ' ', 209, 210, 211, 212, 213, 214, 148, 216, 217, 218, 219, 220, 221, ' ', 223,
  520. 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
  521. ' ', 241, 242, 243, 244, 245, 246, 150, 248, 249, 250, 251, 252, 255, ' ', ' ',
  522. };
  523.  
  524. /*
  525.   Apple QuickDraw character set.
  526. */
  527. unsigned char
  528. yaql1[] = {
  529.   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  530.  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  531.  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  532.  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  533.  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
  534.  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
  535.  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
  536. 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
  537. 196, 197, 199, 201, 209, 214, 220, 225, 224, 226, 228, 227, 229, 231, 233, 232,
  538. 234, 235, 237, 236, 238, 239, 241, 243, 242, 244, 246, 245, 250, 249, 251, 252,
  539. 133, 176, 162, 163, 167, 135, 182, 223, 174, 169, UNK, 180, 168, UNK, 198, 216,
  540. UNK, 177, UNK, UNK, 165, 181, UNK, UNK, UNK, UNK, UNK, 170, 186, UNK, 230, 248,
  541. 191, 161, 172, UNK, 151, UNK, UNK, 171, 187, 139, ' ', 193, 195, 213, 148, 150,
  542. 132, 146, 129, 138,  47,  47, 247, UNK, 255, UNK, 152, 164, '<', '>', 131, 145,
  543. 153, 134, 136, 137, 140, 194, 202, 192, 203, 200, 205, 206, 207, 204, 211, 212,
  544. UNK, 210, 218, 219, 217, 154, 156, 157, 175, 158, 141, 176, 184, 142, 143, 144
  545. };
  546.  
  547. /*
  548.   Data structures and functions for parsing and displaying character set name.
  549. */
  550. struct keytab {                /* Keyword table template */
  551.     char *kwd;                /* Pointer to keyword string */
  552.     int kwval;                /* Associated value */
  553. };
  554.  
  555. struct keytab csets[] = {        /* Character sets, alphabetical */
  556.     "apple",   APPLE,            /* Apple QuickDraw */
  557.     "cp437",   CP437,            /* IBM Code Page 437 */
  558.     "cp850",   CP850,            /* IBM Code Page 850 */
  559.     "decmcs",  DECMCS,            /* DEC Multinational Character Set */
  560.     "latin1",  LATIN1,            /* ISO 8859-1 Latin Alphabet 1 */
  561.     "next",    NEXT            /* NeXT character set */
  562. };
  563. int ncsets = (sizeof(csets) / sizeof(struct keytab));
  564.  
  565. int
  566. lower(s) char *s; {            /* Lowercase the string */
  567.     int n = 0;                /* return its length */
  568.     while (*s) {
  569.     if (*s >= 'A' && *s <= 'Z')
  570.       *s += ('A' - 'a');
  571.         s++, n++;
  572.     }
  573.     return(n);
  574. }
  575.  
  576. /* Look up keyword, return value */
  577.  
  578. int
  579. lookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
  580.  
  581.     int i, v, cmdlen;
  582.  
  583. /* Lowercase & get length of target, if it's null return code -3. */
  584.  
  585.     if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
  586.  
  587. /* Not null, look it up */
  588.  
  589.     for (i = 0; i < n-1; i++) {
  590.         if (!strcmp(table[i].kwd,cmd) ||
  591.            ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
  592.              strncmp(table[i+1].kwd,cmd,cmdlen))) {
  593.                 *x = i;
  594.                 return(table[i].kwval);
  595.              }
  596.         if (v) return(-2);
  597.     }   
  598.  
  599. /* Last (or only) element */
  600.  
  601.     if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
  602.         *x = n-1;
  603.         return(table[n-1].kwval);
  604.     } else return(-1);
  605. }
  606.  
  607. /* Look up value, return keyword */
  608.  
  609. char *
  610. getname(x,table,n) int x, n; struct keytab table[]; {
  611.     int i;
  612.     for (i = 0; i < n; i++)
  613.       if (table[i].kwval == x)
  614.     return(table[i].kwd);
  615.     return("");
  616. }
  617.  
  618. /* Global data */
  619.  
  620. int charset = CHARSET,            /* Character set */
  621.   hpos = 0,                /* Character number in line buffer */
  622.   maxhpos = 0,                /* Longest line in buffer */
  623.   page = 0,                /* Page number */
  624.   line = 0,                /* Line number */
  625.   blank = 0,                /* Blank line count */
  626.   pagefull = 0,                /* Flag for page overflow */
  627.   passthru = 0,                /* Flag for file already postscript */
  628.   proflg = 0,                /* Prolog done */
  629.   shift = 0;                /* Shift state */
  630.  
  631. int width = WIDTH;            /* Paper width, characters */
  632. int length = LENGTH;            /* Paper length, characters */
  633.  
  634. int count = 0L;                /* Input char count */
  635. int idflag = 0;                /* File has been ID'd */
  636.  
  637. /* Data structures */
  638.  
  639. /*
  640.   buf is the line buffer.  columns (indexed by hpos) are the characters
  641.   in the line, rows are overstruck lines.  At display time (see addline),
  642.   buf is treated as a 3-dimensional array, with the extra dimension being
  643.   for wraparound.  The total size of the buffer is 80 chars per line times
  644.   66 lines per page times 10 levels of overstriking = 52,800.  This allows
  645.   files that contain absolutely no linefeeds to still print correctly.
  646. */
  647. #define BUFNUM 10            /* Number of overstrike buffers */
  648. #define BUFWID 5280            /* Max characters per line */
  649.  
  650. unsigned char buf[BUFNUM][BUFWID];    /* Line buffers */
  651. unsigned char outbuf[400];        /* Output buffer */
  652. int linesize[BUFNUM];            /* Size of each line in buffer */
  653. int bufs[MAXLENGTH];            /* # overstrike buffers per line */
  654.  
  655. /* Line and page display routines */
  656.  
  657. /* Forward declarations */
  658.  
  659. void addline();                /* Add line to page */
  660. void addchar();                /* Add character to line */
  661. void newpage();                /* New page */
  662. void usage();                /* Usage message */
  663.  
  664. void
  665. clearbuf() {                /* Clear line buffer */
  666.     int i;
  667. /*
  668.   Note: if a loop is used instead of memset, this program runs
  669.   veeeery slooooooowly.
  670. */
  671.     memset(buf,SP,BUFNUM * BUFWID);    /* Clear buffers and counts */
  672.     for (i = 0; i < BUFNUM; linesize[i++] = -1) ;
  673.     for (i = 0; i < length; bufs[i++] = 0) ;
  674.     hpos = 0;                /* Reset line buffer pointer */
  675.     maxhpos = 0;            /* And maximum line length */
  676. }
  677.  
  678. void
  679. doprolog() {                /* Output the PostScript prolog */
  680.     int i;
  681.     for (i = 0; *prolog[i]; i++) {
  682.     printf("%s\n",prolog[i]);
  683.     proflg++;
  684.     }
  685. }
  686.  
  687. void
  688. addchar(c) unsigned char c; {        /* Add character to line buffer */
  689.     int i, m;
  690.     
  691.     if (c < SP || c == DEL) c = SP;    /* ASCII controls become spaces. */
  692.  
  693.     if (c > 127) {            /* 8-bit values are translated */
  694.     switch (charset) {        /* according to character set. */
  695.       case LATIN1:
  696.         if (c > 127 && c < 161)    /* C1 characters are controls */
  697.           c = SP;            /* in Latin-1. */
  698.         break;
  699.       case CP437:  c = y43l1[c]; break;
  700.       case CP850:  c = y85l1[c]; break;
  701.       case NEXT:   c = ynel1[c]; break;
  702.       case DECMCS: c = ydml1[c]; break;
  703.       case APPLE:  c = yaql1[c]; break;
  704.     }
  705.     }
  706.     for (i = 0; i < BUFNUM; i++) {    /* Find first */
  707.     if (hpos > linesize[i]) {    /* available overstrike buffer */
  708.         buf[i][hpos] = c;        /* Deposit character */
  709.         linesize[i] = hpos;        /* Remember size of this buffer. */
  710.         m = hpos / width;        /* Line-wrap segment number. */
  711.         if (i > bufs[m])        /* Highest overstrike buffer used */
  712.           bufs[m] = i;        /* for this line-wrap segment. */
  713.         break;
  714.     }
  715.     }
  716.     if (hpos > maxhpos) maxhpos = hpos;    /* Remember maximum line position. */
  717.     if (++hpos >= BUFWID)        /* Increment line position. */
  718.       addline();            /* If buffer full, dump it. */
  719. }
  720.  
  721. int
  722. istext(c) char c; {            /* Is c a text character? */
  723.     int rc = 1;
  724.     if (c < SP) {
  725.     switch (c) {            /* Printable or */
  726.       case  9:            /* Tab */
  727.       case 10:            /* LF */
  728.       case 11:            /* VT */
  729.       case 12:            /* LF */
  730.       case 13:            /* CR */
  731.         break;
  732.       default:            /* Other control */
  733.         rc = 0;            /* not text */
  734.         break;
  735.     }
  736.     }
  737.     return(rc);
  738. }
  739.  
  740. void
  741. addline() {                /* Add a line to the current page */
  742.     int i, j, k, m, n, y, wraps;
  743.     unsigned char *p, *q, c;
  744.  
  745.     if (line == 0 && page == 1) {    /* First line of file? */
  746.  
  747.     buf[0][hpos] = '\0';        /* Trim trailing blanks */
  748.  
  749.     if (!istext(buf[0])) {        /* Not PostScript, not a text file */
  750.         passthru++;            /* Yes, set this flag and just copy */
  751.         buf[0][hpos] = '\0';    /* Trim trailing blanks */
  752.         printf("%s\n",buf[0]);    /* Send this line to stdout */
  753.         return;
  754.         } else     if (!strncmp(buf[0],"%!",2)) {    /* Already Postscript? */
  755.         passthru++;            /* Yes, set this flag & just copy */
  756.         buf[0][hpos] = '\0';    /* Trim trailing blanks */
  757.         printf("%s\n",buf[0]);    /* Send this line to stdout */
  758.         return;
  759.     } else if (!proflg) {        /* Plain text, print prolog. */
  760.         doprolog();
  761.     }
  762.     }
  763.     if (linesize[0] < 0) {        /* If line is empty, */
  764.     blank++;            /* just count it. */
  765.     clearbuf();            /* Clear buffer reset other counters */
  766.     return;
  767.     }
  768.     if (blank > 0) {            /* Any previous blank lines? */
  769.     if (blank == 1)            /* One */
  770.       printf("()S\n");
  771.     else                /* Many */
  772.       printf("%d L\n",blank);
  773.     }
  774.     line += blank;            /* Count the blank lines */
  775.     blank = 0;                /* Reset blank line counter */
  776.  
  777.     wraps = maxhpos / width;        /* Number of times line will wrap */
  778.     if (wraps > length) wraps = length;    /* (within reason) */
  779.  
  780.     for (k = 0; k <= wraps; k++) {    /* For each wrapped line */
  781.     m = k * width;            /* Starting position in buffer */
  782.     for (i = 0; i <= bufs[k]; i++) { /* For each overstrike buffer */
  783.         y = linesize[i] + 1;    /* Actual character count */
  784.         if (y <= m)            /* Nothing there, next buffer. */
  785.           continue;
  786.         /* Ending position of this wrap region in buffer. */
  787.         n = (y < m + width) ? y : m + width;
  788.         q = outbuf;
  789.         *q++ = '(';            /* Start text arg */
  790.         for (j = m, p = buf[i]+m; j < n; j++) { /* For each character */
  791.         c = *p++;
  792.         if (c == '(' || c == ')' || c =='\\') /* Quote specials */
  793.           *q++ = '\\';        /*  with backslash. */
  794.         if ((int) c < 128)    /* Insert 7-bit character literally */
  795.           *q++ = c;
  796.         else {            /* Insert 8-bit character */
  797.             *q++ = '\\';    /* as octal backslash escape */
  798.             *q++ = (c >> 6) + '0'; /* (this avoids call to sprintf) */
  799.             *q++ = ((c >> 3) & 07) + '0';
  800.             *q++ = (c & 07) + '0';
  801.         }
  802.         }
  803.         *q = '\0';
  804.         printf("%s%s",outbuf, (i == bufs[k]) ? ")S\n" : ")U\n");
  805.     }
  806.     }
  807.     clearbuf();                /* Clear line buffer */
  808.     line += wraps + 1;            /* Increment line number */
  809.     if (line > (length - 1)) {        /* If page is full */
  810.     newpage();            /* print it and start new one */
  811.     pagefull = 1;
  812.     }
  813. }
  814.  
  815. void
  816. newpage() {                /* Print current page, start new one */
  817.     if (pagefull) {            /* If we just overflowed onto a */
  818.     pagefull = 0;            /* new page, but then got a formfeed */
  819.     return;                /* immediately after... */
  820.     }
  821.     if (!proflg)            /* Do prolog if not done already */
  822.       doprolog();            /*  (in case file starts with ^L) */
  823.     if (hpos)                /* Add any partial line */
  824.       addline();
  825.     line = hpos = 0;            /* Reset line, advance page */
  826.     page++;
  827.     printf("EndPage\n%%%%Page: %d %d\nStartPage\n",page,page);
  828.     blank = 0;
  829. }
  830.  
  831. void            /* Show program & PostScript version numbers */
  832. showver() {
  833.     printf("%%!\n/Courier findfont 11 scalefont setfont\n");
  834.     printf("/EndPage{version (PostScript version )\n");
  835.     printf("48 720 moveto show show showpage sv restore}def\n");
  836.     printf("/sv save def 48 740 moveto\n");
  837.     printf("(textps version %s) show\n",version);
  838.     printf("/sv save def 48 700 moveto\n");
  839.     printf("(textps %s) show\n",copyright);
  840.     printf("EndPage\n");
  841. }
  842.  
  843. /*
  844.    gcharset  -  Set the default character set.
  845.  
  846.    Under OS/2, use the DosQueryCp() or DosGetCp() call;
  847.    under MSDOS, use int 21h; on others just return CHARSET.
  848.  
  849.    Note that in an MSC bound program, DosGetCp() will return the
  850.    current code page and no more than one prepared code page.
  851.  
  852.    INT 21 - DOS 3.3+ - GET GLOBAL CODE PAGE TABLE
  853.               AX = 6601h
  854.    Return: CF set on error
  855.               AX = error code (see AH=59h)
  856.            CF clear if successful
  857.               BX = active code page (see AX=6602h)
  858.           DX = system code page
  859.  
  860.    Values for code page:
  861.      437 US (hardware code page on most PCs)
  862.      850 Multilingual (OS/2 default)
  863.  
  864.    The following are similar to CP437, and are treated like CP437:
  865.      857 Turkish         
  866.      860 Portugal
  867.      861 Iceland
  868.      863 Canada (French)
  869.      865 Norway/Denmark
  870.  
  871.    The following are not supported by textps,
  872.    because Courier does not have the needed characters:
  873.      852 Slavic/Latin-2 (DOS 5+)
  874.      862 Hebrew
  875.      866 Cyrillic
  876.      982 Japanese Shift-JIS
  877.      etc etc.
  878. */
  879. int
  880. gcharset() {
  881.  
  882. /* Note that charset=CHARSET is initialized above */
  883.  
  884. #ifdef OS2NTDOS
  885.     int i;
  886. #ifdef NT
  887.    i = GetConsoleCP() ;
  888. #else /* NT */
  889. #ifdef OS2                /* Then get the code page... */
  890. #define INCL_DOSNLS
  891. #include <os2.h>
  892. /*
  893.   Get the current code page and the first two prepared code pages.
  894.   Only the current code page is used in the following.
  895. */
  896. #ifdef __EMX__
  897.     ULONG CpList[3], CpSize, rc;
  898.     rc = DosQueryCp(sizeof(CpList), CpList, &CpSize);
  899. #else /* MSC */
  900.     USHORT CpList[3], CpSize, rc;
  901.     rc = DosGetCp(sizeof(CpList), CpList, &CpSize);
  902. #endif /* __EMX__ */
  903.     i = (int) CpList[0];
  904. #else /* MSDOS */
  905. #include <dos.h>
  906.     union REGS regs;
  907.  
  908.     regs.x.ax = 0x6601;
  909.     intdos(®s, ®s);
  910.     i = regs.x.bx;
  911. #endif /* OS2 */
  912. #endif /* NT */
  913.     switch (i) {
  914.       case 437: /* CP437 */
  915.       case 860: /* Portugal */
  916.       case 857: /* Turkey */
  917.       case 861: /* Iceland */
  918.       case 863: /* Canadian French */
  919.       case 865: /*  Norway and Denmark */
  920.     charset = CP437;
  921.     break;
  922.       case 850: /* Multilingual (West European) code page */
  923.     charset = CP850;
  924.     break;
  925.     }
  926. #endif /* OS2NTDOS */
  927.  
  928.     return (charset);
  929. }
  930.  
  931.  
  932. /* Main program */
  933.  
  934. void
  935. main(argc, argv) int argc; char *argv[]; {
  936.  
  937.     int i, j,                /* Worker ints */
  938.       escape;                /* Flag when Esc seen. */
  939.     unsigned char c = 0;        /* Input character */
  940.     int prev = 0;            /* Previous character */
  941.  
  942.     gcharset();                /* Get the default character set */
  943.     while (--argc > 0) {        /* argv/argc "User interface"... */
  944.     argv++;
  945.     if (**argv == '-') {        /* Look for '-' */
  946.         c = *(*argv+1);        /* Get option letter */
  947.         switch (c) {
  948.           case 'l': case 'L':    /* Paper length */
  949.         argv++, argc--;
  950.         length = atol(*argv);
  951.         break;
  952.           case 'w': case 'W':    /* Paper width */
  953.         argv++, argc--;
  954.         width = atol(*argv);
  955.         break;
  956.           case 'h': case 'H': case '?': /* Help */
  957.         usage(-1);
  958.           case 'c': case 'C':    /* Character set */
  959.         argv++, argc--;
  960.         if (argc < 1) usage(4);
  961.         i = lookup(csets,*argv,ncsets,&j);
  962.         if (i < 0) usage(-i);
  963.         charset = i;
  964.         break;
  965.           case 'v': case 'V':    /* Show version numbers */
  966.         showver();
  967.         break;
  968.           default:
  969.         usage(5);
  970.         }
  971.     } else {            /* Options must begin with '-' */
  972.         usage(0);
  973.     }
  974.     }
  975. #ifdef OS2NTDOS
  976. #ifdef NT
  977.     if (_isatty(stdin->_file))
  978. #else
  979.     if (isatty(fileno(stdin)))
  980. #endif /* NT */
  981.       usage(-1);
  982. #endif /* OS2NTDOS */
  983.     hpos = line = passthru = 0;        /* Initialize these... */ 
  984.     escape = blank = 0;
  985.     page = 1;
  986. /*
  987.   The tests for PCL are a bit loose, but we're not really testing to see if
  988.   it's PC, but rather to see whether the input stream should be converted to
  989.   PostScript.  If it starts with "%!" then it's already PostScript.  If it
  990.   starts with <ESC>E, it's probably PCL.  If it starts with <ESC>%
  991.   (<ESC>%-12345X), it's probably PJL, which could be PCL, POSTSCRIPT, HPIB,
  992.   or who knows what else, but in any case it's already some kind of printer
  993.   language and should not be converted to PostScript.  Unfortunately, PCL's
  994.   ANSI-like escape sequences conflict with this program's ability to print
  995.   files containing ANSI escape sequences, so to support PCL, ESCSEQ must be
  996.   undefined.
  997. */
  998.     while ((i = getchar()) != EOF) {    /* Read a file character */
  999.     prev = c;
  1000.     c = i;                /* Convert to unsigned char */
  1001.     if (!idflag) {
  1002.         count++;
  1003.         if (count == 2) {
  1004.         idflag++;
  1005.         if (prev == '%' && c == '!') { /* Already PostScript? */
  1006.             passthru++;               /* Don't convert */
  1007.             putchar(prev);
  1008. #ifndef ESCSEQ
  1009.         } else if (prev == '\033') {   /* PCL/PJL? */
  1010.             if (c == 'E' || c == '%')  /* Ditto */
  1011.               passthru++;
  1012.             putchar(prev);
  1013. #endif /* ESCSEQ */
  1014.         /*
  1015.           If the file starts with two non-text characters there is
  1016.           no point trying to convert it to PostScript.
  1017.         */
  1018.         } else if (!istext(prev) && !istext(c)) { /* Other non-text? */
  1019.             passthru++;
  1020.             putchar(prev);
  1021.         }
  1022.         }
  1023.     }    
  1024.     if (passthru) {            /* Passing file through? */
  1025.         putchar(c);            /* Just copy the bytes. */
  1026.         continue;
  1027.     }
  1028. #ifdef ESCSEQ
  1029.     if (escape) {            /* Swallow ANSI escape sequences */
  1030.         switch (escape) {
  1031.           case 1:            /* ESC */
  1032.         if (c < 040 || c > 057) /* Not intermediate character */
  1033.           escape = 0;
  1034.         continue;
  1035.           case 2:            /* CSI */
  1036.         if (c < 040 || c > 077)    /* Not parameter or intermediate */
  1037.           escape = 0;
  1038.         continue;
  1039.           default:            /* Bad escape value, */
  1040.         escape = 0;        /* shouldn't happen. */
  1041.         break;
  1042.         }
  1043.     }
  1044. #endif /* ESCSEQ */
  1045.  
  1046.     if (shift && c > 31 && c < 127) 
  1047.       c |= 0200;            /* Handle shift state. */
  1048.  
  1049.     if (pagefull && c != 014)    /* Spurious blank page suppression */
  1050.       pagefull = 0;
  1051.  
  1052.     switch (c) {            /* Handle the input character */
  1053.  
  1054.       case 010:            /* Backspace */
  1055.         hpos--;
  1056.         if (hpos < 0) hpos = 0;
  1057.         continue;
  1058.  
  1059.       case 011:             /* Tab */
  1060.         hpos = (hpos | 7) + 1;
  1061.         continue;
  1062.  
  1063.       case 012:            /* Linefeed */
  1064.         addline();            /* Add the line to the page */
  1065.         continue;
  1066.  
  1067.       case 014:            /* Formfeed */
  1068.         newpage();            /* Print current page */
  1069.         continue;
  1070.  
  1071.       case 015:            /* Carriage return */
  1072.         hpos = 0;            /* Back to left margin */
  1073.         continue;
  1074.  
  1075.       case 016:            /* Shift-Out */
  1076.         shift = 1;            /* Set shift state */
  1077.         continue;
  1078.  
  1079.       case 017:            /* Shift-In */
  1080.         shift = 0;            /* Reset shift state */
  1081.         continue;
  1082.  
  1083. #ifdef ESCSEQ                /* Swallow ANSI escape sequences */
  1084. /*
  1085.   1 = ANSI escape sequence
  1086.   2 = ANSI control sequence
  1087. */
  1088.       case 033:            /* ESC or 7-bit CSI */
  1089.         escape = ((c = getchar()) == 0133) ? 2 : 1;
  1090.         if (c != 033 && c != 0133 && c != 233) /* Not ANSI after all */
  1091.           ungetc(c,stdin);        /* put it back, avoid loops */
  1092.         continue;
  1093.  
  1094.       case 040:            /* Space */
  1095.         hpos++;
  1096.         continue;
  1097.  
  1098.       case 0233:            /* 8-bit CSI */
  1099.         if (charset == LATIN1) {
  1100.         escape = 2;        /* 0233 is graphic char on PC, etc */
  1101.         continue;
  1102.         }                /* Otherwise fall thru & print it */
  1103. #endif /* ESCSEQ */
  1104.  
  1105.       default:
  1106.         addchar(c);
  1107.     }
  1108.     }
  1109.     if (!passthru) {            /* Done. If converting to PS, */
  1110.     if (hpos)            /* if last line was not empty, */
  1111.       addline();            /* add it to the page. */
  1112.     if (page != 1 || line != 0) {    /* Add trailer. */
  1113.         printf("EndPage\n%%%%Trailer\n%%%%Pages: %d\n",page);
  1114.     }
  1115.     }
  1116.     exit(0);                /* Done, return success code. */
  1117. }
  1118.  
  1119. void
  1120. usage(x) int x; {            /* Give usage message and quit. */
  1121.     int i;
  1122.     switch (x) {
  1123.       case 0:
  1124.     fprintf(stderr,"textps: only options, not filenames, allowed;");
  1125.     fprintf(stderr," use standard input.\n");
  1126.     break;
  1127.       case 1:
  1128.     fprintf(stderr,"textps: invalid character set name\n");
  1129.     break;
  1130.       case 2:
  1131.     fprintf(stderr,"textps: ambiguous option\n");
  1132.     break;
  1133.       case 3:
  1134.     fprintf(stderr,"textps: option required after -\n");
  1135.     break;
  1136.       case 4:
  1137.     fprintf(stderr,"textps: option requires an argument\n");
  1138.     break;
  1139.       case 5:
  1140.     fprintf(stderr,"textps: invalid option\n");
  1141.     break;    
  1142.       default:
  1143.     break;
  1144.     }
  1145.     fprintf(stderr,
  1146. "textps converts standard input to PostScript on standard output.\n");
  1147.     fprintf(stderr,
  1148. "If standard input is already in PostScript format, or is PCL or PJL, it\n");
  1149.     fprintf(stderr,
  1150. "is simply copied to standard output.\n\n");
  1151.     fprintf(stderr,
  1152. "usage:  textps -h -v -c charset -l number -w number < infile > outfile\n");
  1153.     fprintf(stderr,"  -h displays this usage message.\n");
  1154.  
  1155.     fprintf(stderr,
  1156. "  -v produces a page showing textps and PostScript version numbers.\n");
  1157.     fprintf(stderr,
  1158. "  -c specifies the file's character set, one of the following:\n");
  1159.     for (i = 0; i < ncsets; i++)
  1160.       fprintf(stderr,"      %s\n",csets[i].kwd);
  1161.     gcharset();
  1162.     fprintf(stderr,"  the default character set is %s.\n",
  1163.         getname(charset,csets,ncsets));
  1164.     fprintf(stderr,"  -l number is paper length in lines (default 66).\n");
  1165.     fprintf(stderr,"  -w number is paper width in characters (default 80).\n");
  1166.     fprintf(stderr,"examples:\n");
  1167.     fprintf(stderr,"  textps < infile > outfile\n");
  1168.     fprintf(stderr,"  textps -v < infile > outfile\n");
  1169.     fprintf(stderr,"  textps -c cp850 < infile > outfile\n");
  1170.     fprintf(stderr,"  textps -v -c next < infile > outfile\n");
  1171.     fprintf(stderr,"  textps < infile | lpr (UNIX)\n");
  1172.     fprintf(stderr,"  textps < infile > prn (DOS)\n");
  1173.     fprintf(stderr,"textps version: %s.\n",version);
  1174.     fprintf(stderr,"%s\n",copyright);
  1175.     exit(1);
  1176. }
  1177.