postdaisy.c 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  1. /*
  2. *
  3. * postdaisy - PostScript translator for Diablo 1640 files.
  4. *
  5. * A program that translates Diablo 1640 files into PostScript. Absolutely nothing
  6. * is guaranteed. Quite a few things haven't been implemented, and what's been
  7. * done isn't well tested. Most of the documentation used to write this program
  8. * was taken from the 'Diablo Emulator' section of a recent Imagen manual.
  9. *
  10. * Some of document comments that are generated may not be right. Most of the test
  11. * files I used produced a trailing blank page. I've put a check in formfeed() that
  12. * won't print the last page if it doesn't contain any text, but PAGES comments may
  13. * not be right. The DOCUMENTFONTS comment will also be wrong if auto underline or
  14. * bold printing have been turned on by escape commands.
  15. *
  16. * The brute force approach used to implement horizontal and vertical tabs leaves
  17. * much to be desired, and may not work for very small initial hmi and vmi values.
  18. * At the very least I should have used malloc() to get space for the two tabstop
  19. * arrays after hmi and vmi are known!
  20. *
  21. * Reverse printing mode hasn't been tested at all, but what's here should be
  22. * close even though it's not efficient.
  23. *
  24. * The PostScript prologue is copied from *prologue before any of the input files
  25. * are translated. The program expects that the following PostScript procedures
  26. * are defined in that file:
  27. *
  28. * setup
  29. *
  30. * mark ... setup -
  31. *
  32. * Handles special initialization stuff that depends on how this program
  33. * was called. Expects to find a mark followed by key/value pairs on the
  34. * stack. The def operator is applied to each pair up to the mark, then
  35. * the default state is set up.
  36. *
  37. * pagesetup
  38. *
  39. * page pagesetup -
  40. *
  41. * Does whatever is needed to set things up for the next page. Expects to
  42. * find the current page number on the stack.
  43. *
  44. * t
  45. *
  46. * mark str1 x1 str2 x2 ... strn xn y hmi t mark
  47. *
  48. * Handles all the text on the stack. Characters in the strings are
  49. * printed using hmi as the character advance, and all strings are at
  50. * vertical position y. Each string is begins at the horizontal position
  51. * that preceeds it.
  52. *
  53. * f
  54. *
  55. * font f -
  56. *
  57. * Use font f, where f is the full PostScript font name. Only used when
  58. * we switch to auto underline (Courier-Italic) or bold (Courier-Bold)
  59. * printing.
  60. *
  61. * done
  62. *
  63. * done
  64. *
  65. * Makes sure the last page is printed. Only needed when we're printing
  66. * more than one page on each sheet of paper.
  67. *
  68. * Many default values, like the magnification and orientation, are defined in
  69. * the prologue, which is where they belong. If they're changed (by options), an
  70. * appropriate definition is made after the prologue is added to the output file.
  71. * The -P option passes arbitrary PostScript through to the output file. Among
  72. * other things it can be used to set (or change) values that can't be accessed by
  73. * other options.
  74. *
  75. */
  76. #include <stdio.h>
  77. #include <signal.h>
  78. #include <ctype.h>
  79. #ifdef plan9
  80. #define isascii(c) ((unsigned char)(c)<=0177)
  81. #endif
  82. #include <sys/types.h>
  83. #include <fcntl.h>
  84. #include "comments.h" /* PostScript file structuring comments */
  85. #include "gen.h" /* general purpose definitions */
  86. #include "path.h" /* for the prologue */
  87. #include "ext.h" /* external variable declarations */
  88. #include "postdaisy.h" /* a few special definitions */
  89. char *optnames = "a:c:f:h:l:m:n:o:p:r:s:v:x:y:A:C:E:J:L:P:DI";
  90. char *prologue = POSTDAISY; /* default PostScript prologue */
  91. char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
  92. int formsperpage = 1; /* page images on each piece of paper */
  93. int copies = 1; /* and this many copies of each sheet */
  94. char htabstops[COLUMNS]; /* horizontal */
  95. char vtabstops[ROWS]; /* and vertical tabs */
  96. int res = RES; /* input file resolution - sort of */
  97. int hmi = HMI; /* horizontal motion index - 1/120 inch */
  98. int vmi = VMI; /* vertical motion index - 1/48 inch */
  99. int ohmi = HMI; /* original hmi */
  100. int ovmi = VMI; /* and vmi - for tabs and char size */
  101. int hpos = 0; /* current horizontal */
  102. int vpos = 0; /* and vertical position */
  103. int lastx = -1; /* printer's last horizontal */
  104. int lasty = -1; /* and vertical position */
  105. int lasthmi = -1; /* hmi for current text strings */
  106. int lastc = -1; /* last printed character */
  107. int prevx = -1; /* at this position */
  108. int leftmargin = LEFTMARGIN; /* page margins */
  109. int rightmargin = RIGHTMARGIN;
  110. int topmargin = TOPMARGIN;
  111. int bottommargin = BOTTOMMARGIN;
  112. int stringcount = 0; /* number of strings on the stack */
  113. int stringstart = 1; /* column where current one starts */
  114. int advance = 1; /* -1 if in backward print mode */
  115. int lfiscr = OFF; /* line feed implies carriage return */
  116. int crislf = OFF; /* carriage return implies line feed */
  117. int linespp = 0; /* lines per page if it's positive */
  118. int markedpage = FALSE; /* helps prevent trailing blank page */
  119. int page = 0; /* page we're working on */
  120. int printed = 0; /* printed this many pages */
  121. Fontmap fontmap[] = FONTMAP; /* for translating font names */
  122. char *fontname = "Courier"; /* use this PostScript font */
  123. int shadowprint = OFF; /* automatic bold printing if ON */
  124. FILE *fp_in; /* read from this file */
  125. FILE *fp_out = stdout; /* and write stuff here */
  126. FILE *fp_acct = NULL; /* for accounting data */
  127. /*****************************************************************************/
  128. main(agc, agv)
  129. int agc;
  130. char *agv[];
  131. {
  132. /*
  133. *
  134. * A simple program that translates Diablo 1640 files into PostScript. Nothing
  135. * is guaranteed - the program not well tested and doesn't implement everything.
  136. *
  137. */
  138. argc = agc; /* other routines may want them */
  139. argv = agv;
  140. prog_name = argv[0]; /* really just for error messages */
  141. init_signals(); /* sets up interrupt handling */
  142. header(); /* PostScript header comments */
  143. options(); /* handle the command line options */
  144. setup(); /* for PostScript */
  145. arguments(); /* followed by each input file */
  146. done(); /* print the last page etc. */
  147. account(); /* job accounting data */
  148. exit(x_stat); /* not much could be wrong */
  149. } /* End of main */
  150. /*****************************************************************************/
  151. init_signals()
  152. {
  153. /*
  154. *
  155. * Makes sure we handle interrupts.
  156. *
  157. */
  158. if ( signal(SIGINT, interrupt) == SIG_IGN ) {
  159. signal(SIGINT, SIG_IGN);
  160. signal(SIGQUIT, SIG_IGN);
  161. signal(SIGHUP, SIG_IGN);
  162. } else {
  163. signal(SIGHUP, interrupt);
  164. signal(SIGQUIT, interrupt);
  165. } /* End else */
  166. signal(SIGTERM, interrupt);
  167. } /* End of init_signals */
  168. /*****************************************************************************/
  169. header()
  170. {
  171. int ch; /* return value from getopt() */
  172. int old_optind = optind; /* for restoring optind - should be 1 */
  173. /*
  174. *
  175. * Scans the option list looking for things, like the prologue file, that we need
  176. * right away but could be changed from the default. Doing things this way is an
  177. * attempt to conform to Adobe's latest file structuring conventions. In particular
  178. * they now say there should be nothing executed in the prologue, and they have
  179. * added two new comments that delimit global initialization calls. Once we know
  180. * where things really are we write out the job header, follow it by the prologue,
  181. * and then add the ENDPROLOG and BEGINSETUP comments.
  182. *
  183. */
  184. while ( (ch = getopt(argc, argv, optnames)) != EOF )
  185. if ( ch == 'L' )
  186. prologue = optarg;
  187. else if ( ch == '?' )
  188. error(FATAL, "");
  189. optind = old_optind; /* get ready for option scanning */
  190. fprintf(stdout, "%s", CONFORMING);
  191. fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
  192. fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
  193. fprintf(stdout, "%s %s\n", PAGES, ATEND);
  194. fprintf(stdout, "%s", ENDCOMMENTS);
  195. if ( cat(prologue) == FALSE )
  196. error(FATAL, "can't read %s", prologue);
  197. if ( DOROUND )
  198. cat(ROUNDPAGE);
  199. fprintf(stdout, "%s", ENDPROLOG);
  200. fprintf(stdout, "%s", BEGINSETUP);
  201. fprintf(stdout, "mark\n");
  202. } /* End of header */
  203. /*****************************************************************************/
  204. options()
  205. {
  206. int ch; /* return value from getopt() */
  207. int n; /* for CR and LF modes */
  208. /*
  209. *
  210. * Reads and processes the command line options. Added the -P option so arbitrary
  211. * PostScript code can be passed through. Expect it could be useful for changing
  212. * definitions in the prologue for which options have not been defined.
  213. *
  214. * Although any PostScript font can be used, things will only work for constant
  215. * width fonts.
  216. *
  217. */
  218. while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
  219. switch ( ch ) {
  220. case 'a': /* aspect ratio */
  221. fprintf(stdout, "/aspectratio %s def\n", optarg);
  222. break;
  223. case 'c': /* copies */
  224. copies = atoi(optarg);
  225. fprintf(stdout, "/#copies %s store\n", optarg);
  226. break;
  227. case 'f': /* use this PostScript font */
  228. fontname = get_font(optarg);
  229. fprintf(stdout, "/font /%s def\n", fontname);
  230. break;
  231. case 'h': /* default character spacing */
  232. ohmi = hmi = atoi(optarg) * HSCALE;
  233. fprintf(stdout, "/hmi %s def\n", optarg);
  234. break;
  235. case 'l': /* lines per page */
  236. linespp = atoi(optarg);
  237. break;
  238. case 'm': /* magnification */
  239. fprintf(stdout, "/magnification %s def\n", optarg);
  240. break;
  241. case 'n': /* forms per page */
  242. formsperpage = atoi(optarg);
  243. fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
  244. fprintf(stdout, "/formsperpage %s def\n", optarg);
  245. break;
  246. case 'o': /* output page list */
  247. out_list(optarg);
  248. break;
  249. case 'p': /* landscape or portrait mode */
  250. if ( *optarg == 'l' )
  251. fprintf(stdout, "/landscape true def\n");
  252. else fprintf(stdout, "/landscape false def\n");
  253. break;
  254. case 'r': /* set CR and LF modes */
  255. n = atoi(optarg);
  256. if ( n & 01 )
  257. lfiscr = ON;
  258. else lfiscr = OFF;
  259. if ( n & 02 )
  260. crislf = ON;
  261. else crislf = OFF;
  262. break;
  263. case 's': /* point size */
  264. fprintf(stdout, "/pointsize %s def\n", optarg);
  265. break;
  266. case 'v': /* default line spacing */
  267. ovmi = vmi = atoi(optarg) * VSCALE;
  268. break;
  269. case 'x': /* shift things horizontally */
  270. fprintf(stdout, "/xoffset %s def\n", optarg);
  271. break;
  272. case 'y': /* and vertically on the page */
  273. fprintf(stdout, "/yoffset %s def\n", optarg);
  274. break;
  275. case 'A': /* force job accounting */
  276. case 'J':
  277. if ( (fp_acct = fopen(optarg, "a")) == NULL )
  278. error(FATAL, "can't open accounting file %s", optarg);
  279. break;
  280. case 'C': /* copy file straight to output */
  281. if ( cat(optarg) == FALSE )
  282. error(FATAL, "can't read %s", optarg);
  283. break;
  284. case 'E': /* text font encoding */
  285. fontencoding = optarg;
  286. break;
  287. case 'L': /* PostScript prologue file */
  288. prologue = optarg;
  289. break;
  290. case 'P': /* PostScript pass through */
  291. fprintf(stdout, "%s\n", optarg);
  292. break;
  293. case 'R': /* special global or page level request */
  294. saverequest(optarg);
  295. break;
  296. case 'D': /* debug flag */
  297. debug = ON;
  298. break;
  299. case 'I': /* ignore FATAL errors */
  300. ignore = ON;
  301. break;
  302. case '?': /* don't understand the option */
  303. error(FATAL, "");
  304. break;
  305. default: /* don't know what to do for ch */
  306. error(FATAL, "missing case for option %c\n", ch);
  307. break;
  308. } /* End switch */
  309. } /* End while */
  310. argc -= optind; /* get ready for non-option args */
  311. argv += optind;
  312. } /* End of options */
  313. /*****************************************************************************/
  314. char *get_font(name)
  315. char *name; /* name the user asked for */
  316. {
  317. int i; /* for looking through fontmap[] */
  318. /*
  319. *
  320. * Called from options() to map a user's font name into a legal PostScript name.
  321. * If the lookup fails *name is returned to the caller. That should let you choose
  322. * any PostScript font, although things will only work well for constant width
  323. * fonts.
  324. *
  325. */
  326. for ( i = 0; fontmap[i].name != NULL; i++ )
  327. if ( strcmp(name, fontmap[i].name) == 0 )
  328. return(fontmap[i].val);
  329. return(name);
  330. } /* End of get_font */
  331. /*****************************************************************************/
  332. setup()
  333. {
  334. /*
  335. *
  336. * Handles things that must be done after the options are read but before the
  337. * input files are processed.
  338. *
  339. */
  340. writerequest(0, stdout); /* global requests eg. manual feed */
  341. setencoding(fontencoding);
  342. fprintf(stdout, "setup\n");
  343. if ( formsperpage > 1 ) {
  344. if ( cat(formfile) == FALSE )
  345. error(FATAL, "can't read %s", formfile);
  346. fprintf(stdout, "%d setupforms\n", formsperpage);
  347. } /* End if */
  348. fprintf(stdout, "%s", ENDSETUP);
  349. } /* End of setup */
  350. /*****************************************************************************/
  351. arguments()
  352. {
  353. /*
  354. *
  355. * Makes sure all the non-option command line arguments are processed. If we get
  356. * here and there aren't any arguments left, or if '-' is one of the input files
  357. * we'll process stdin.
  358. *
  359. */
  360. fp_in = stdin;
  361. if ( argc < 1 )
  362. text();
  363. else { /* at least one argument is left */
  364. while ( argc > 0 ) {
  365. if ( strcmp(*argv, "-") == 0 )
  366. fp_in = stdin;
  367. else if ( (fp_in = fopen(*argv, "r")) == NULL )
  368. error(FATAL, "can't open %s", *argv);
  369. text();
  370. if ( fp_in != stdin )
  371. fclose(fp_in);
  372. argc--;
  373. argv++;
  374. } /* End while */
  375. } /* End else */
  376. } /* End of arguments */
  377. /*****************************************************************************/
  378. done()
  379. {
  380. /*
  381. *
  382. * Finished with all the input files, so mark the end of the pages, make sure the
  383. * last page is printed, and restore the initial environment.
  384. *
  385. */
  386. fprintf(stdout, "%s", TRAILER);
  387. fprintf(stdout, "done\n");
  388. fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
  389. fprintf(stdout, "%s %d\n", PAGES, printed);
  390. } /* End of done */
  391. /*****************************************************************************/
  392. account()
  393. {
  394. /*
  395. *
  396. * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
  397. * is requested using the -A or -J options.
  398. *
  399. */
  400. if ( fp_acct != NULL )
  401. fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
  402. } /* End of account */
  403. /*****************************************************************************/
  404. text()
  405. {
  406. int ch; /* next input character */
  407. /*
  408. *
  409. * Translates the next input file into PostScript. The redirect(-1) call forces
  410. * the initial output to go to /dev/null - so the stuff formfeed() does at the
  411. * end of each page doesn't go to stdout.
  412. *
  413. */
  414. redirect(-1); /* get ready for the first page */
  415. formfeed(); /* force PAGE comment etc. */
  416. inittabs();
  417. while ( (ch = getc(fp_in)) != EOF )
  418. switch ( ch ) {
  419. case '\010': /* backspace */
  420. backspace();
  421. break;
  422. case '\011': /* horizontal tab */
  423. htab();
  424. break;
  425. case '\012': /* new line */
  426. linefeed();
  427. break;
  428. case '\013': /* vertical tab */
  429. vtab();
  430. break;
  431. case '\014': /* form feed */
  432. formfeed();
  433. break;
  434. case '\015': /* carriage return */
  435. carriage();
  436. break;
  437. case '\016': /* extended character set - SO */
  438. break;
  439. case '\017': /* extended character set - SI */
  440. break;
  441. case '\031': /* next char from supplementary set */
  442. break;
  443. case '\033': /* 2 or 3 byte escape sequence */
  444. escape();
  445. break;
  446. default:
  447. oput(ch);
  448. break;
  449. } /* End switch */
  450. formfeed(); /* next file starts on a new page? */
  451. } /* End of text */
  452. /*****************************************************************************/
  453. inittabs()
  454. {
  455. int i; /* loop index */
  456. /*
  457. *
  458. * Initializes the horizontal and vertical tab arrays. The way tabs are handled is
  459. * quite inefficient and may not work for all initial hmi or vmi values.
  460. *
  461. */
  462. for ( i = 0; i < COLUMNS; i++ )
  463. htabstops[i] = ((i % 8) == 0) ? ON : OFF;
  464. for ( i = 0; i < ROWS; i++ )
  465. vtabstops[i] = ((i * ovmi) > BOTTOMMARGIN) ? ON : OFF;
  466. } /* End of inittabs */
  467. /*****************************************************************************/
  468. cleartabs()
  469. {
  470. int i; /* loop index */
  471. /*
  472. *
  473. * Clears all horizontal and vertical tab stops.
  474. *
  475. */
  476. for ( i = 0; i < ROWS; i++ )
  477. htabstops[i] = OFF;
  478. for ( i = 0; i < COLUMNS; i++ )
  479. vtabstops[i] = OFF;
  480. } /* End of cleartabs */
  481. /*****************************************************************************/
  482. formfeed()
  483. {
  484. /*
  485. *
  486. * Called whenever we've finished with the last page and want to get ready for the
  487. * next one. Also used at the beginning and end of each input file, so we have to
  488. * be careful about what's done. I've added a simple test before the showpage that
  489. * should eliminate the extra blank page that was put out at the end of many jobs,
  490. * but the PAGES comments may be wrong.
  491. *
  492. */
  493. if ( fp_out == stdout ) /* count the last page */
  494. printed++;
  495. endline(); /* print the last line */
  496. fprintf(fp_out, "cleartomark\n");
  497. if ( feof(fp_in) == 0 || markedpage == TRUE )
  498. fprintf(fp_out, "showpage\n");
  499. fprintf(fp_out, "saveobj restore\n");
  500. fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
  501. if ( ungetc(getc(fp_in), fp_in) == EOF )
  502. redirect(-1);
  503. else redirect(++page);
  504. fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
  505. fprintf(fp_out, "/saveobj save def\n");
  506. fprintf(fp_out, "mark\n");
  507. writerequest(printed+1, fp_out);
  508. fprintf(fp_out, "%d pagesetup\n", printed+1);
  509. vgoto(topmargin);
  510. hgoto(leftmargin);
  511. markedpage = FALSE;
  512. } /* End of formfeed */
  513. /*****************************************************************************/
  514. linefeed()
  515. {
  516. int line = 0; /* current line - based on ovmi */
  517. /*
  518. *
  519. * Adjust our current vertical position. If we've passed the bottom of the page
  520. * or exceeded the number of lines per page, print it and go to the upper left
  521. * corner of the next page. This routine is also called from carriage() if crislf
  522. * is ON.
  523. *
  524. */
  525. vmot(vmi);
  526. if ( lfiscr == ON )
  527. hgoto(leftmargin);
  528. if ( linespp > 0 ) /* means something so see where we are */
  529. line = vpos / ovmi + 1;
  530. if ( vpos > bottommargin || line > linespp )
  531. formfeed();
  532. } /* End of linefeed */
  533. /*****************************************************************************/
  534. carriage()
  535. {
  536. /*
  537. *
  538. * Handles carriage return character. If crislf is ON we'll generate a line feed
  539. * every time we get a carriage return character.
  540. *
  541. */
  542. if ( shadowprint == ON ) /* back to normal mode */
  543. changefont(fontname);
  544. advance = 1;
  545. shadowprint = OFF;
  546. hgoto(leftmargin);
  547. if ( crislf == ON )
  548. linefeed();
  549. } /* End of carriage */
  550. /*****************************************************************************/
  551. htab()
  552. {
  553. int col; /* 'column' we'll be at next */
  554. int i; /* loop index */
  555. /*
  556. *
  557. * Tries to figure out where the next tab stop is. Wasn't positive about this
  558. * one, since hmi can change. I'll assume columns are determined by the original
  559. * value of hmi. That fixes them on the page, which seems to make more sense than
  560. * letting them float all over the place.
  561. *
  562. */
  563. endline();
  564. col = hpos/ohmi + 1;
  565. for ( i = col; i < ROWS; i++ )
  566. if ( htabstops[i] == ON ) {
  567. col = i;
  568. break;
  569. } /* End if */
  570. hgoto(col * ohmi);
  571. lastx = hpos;
  572. } /* End of htab */
  573. /*****************************************************************************/
  574. vtab()
  575. {
  576. int line; /* line we'll be at next */
  577. int i; /* loop index */
  578. /*
  579. *
  580. * Looks for the next vertical tab stop in the vtabstops[] array and moves to that
  581. * line. If we don't find a tab we'll just move down one line - shouldn't happen.
  582. *
  583. */
  584. endline();
  585. line = vpos/ovmi + 1;
  586. for ( i = line; i < COLUMNS; i++ )
  587. if ( vtabstops[i] == ON ) {
  588. line = i;
  589. break;
  590. } /* End if */
  591. vgoto(line * ovmi);
  592. } /* End of vtab */
  593. /*****************************************************************************/
  594. backspace()
  595. {
  596. /*
  597. *
  598. * Moves backwards a distance equal to the current value of hmi, but don't go
  599. * past the left margin.
  600. *
  601. */
  602. endline();
  603. if ( hpos - leftmargin >= hmi )
  604. hmot(-hmi);
  605. else hgoto(leftmargin); /* maybe just ignore the backspace?? */
  606. lastx = hpos;
  607. } /* End of backspace */
  608. /*****************************************************************************/
  609. escape()
  610. {
  611. int ch; /* control character */
  612. /*
  613. *
  614. * Handles special codes that are expected to follow an escape character. The
  615. * initial escape character is followed by one or two bytes.
  616. *
  617. */
  618. switch ( ch = getc(fp_in) ) {
  619. case 'T': /* top margin */
  620. topmargin = vpos;
  621. break;
  622. case 'L': /* bottom margin */
  623. bottommargin = vpos;
  624. break;
  625. case 'C': /* clear top and bottom margins */
  626. bottommargin = BOTTOMMARGIN;
  627. topmargin = TOPMARGIN;
  628. break;
  629. case '9': /* left margin */
  630. leftmargin = hpos;
  631. break;
  632. case '0': /* right margin */
  633. rightmargin = hpos;
  634. break;
  635. case '1': /* set horizontal tab */
  636. htabstops[hpos/ohmi] = ON;
  637. break;
  638. case '8': /* clear horizontal tab at hpos */
  639. htabstops[hpos/ohmi] = OFF;
  640. break;
  641. case '-': /* set vertical tab */
  642. vtabstops[vpos/ovmi] = ON;
  643. break;
  644. case '2': /* clear all tabs */
  645. cleartabs();
  646. break;
  647. case '\014': /* set lines per page */
  648. linespp = getc(fp_in);
  649. break;
  650. case '\037': /* set hmi to next byte minus 1 */
  651. hmi = HSCALE * (getc(fp_in) - 1);
  652. break;
  653. case 'S': /* reset hmi to default */
  654. hmi = ohmi;
  655. break;
  656. case '\011': /* move to column given by next byte */
  657. hgoto((getc(fp_in)-1) * ohmi);
  658. break;
  659. case '?': /* do carriage return after line feed */
  660. lfiscr = ON;
  661. break;
  662. case '!': /* don't generate carriage return */
  663. lfiscr = OFF;
  664. break;
  665. case '5': /* forward print mode */
  666. advance = 1;
  667. break;
  668. case '6': /* backward print mode */
  669. advance = -1;
  670. break;
  671. case '\036': /* set vmi to next byte minus 1 */
  672. vmi = VSCALE * (getc(fp_in) - 1);
  673. break;
  674. case '\013': /* move to line given by next byte */
  675. vgoto((getc(fp_in)-1) * ovmi);
  676. break;
  677. case 'U': /* positive half line feed */
  678. vmot(vmi/2);
  679. break;
  680. case 'D': /* negative half line feed */
  681. vmot(-vmi/2);
  682. break;
  683. case '\012': /* negative line feed */
  684. vmot(-vmi);
  685. break;
  686. case '\015': /* clear all margins */
  687. bottommargin = BOTTOMMARGIN;
  688. topmargin = TOPMARGIN;
  689. leftmargin = BOTTOMMARGIN;
  690. rightmargin = RIGHTMARGIN;
  691. break;
  692. case 'E': /* auto underscore - use italic font */
  693. changefont("/Courier-Oblique");
  694. break;
  695. case 'R': /* disable auto underscore */
  696. changefont(fontname);
  697. break;
  698. case 'O': /* bold/shadow printing */
  699. case 'W':
  700. changefont("/Courier-Bold");
  701. shadowprint = ON;
  702. break;
  703. case '&': /* disable bold printing */
  704. changefont(fontname);
  705. shadowprint = OFF;
  706. break;
  707. case '/': /* ignored 2 byte escapes */
  708. case '\\':
  709. case '<':
  710. case '>':
  711. case '%':
  712. case '=':
  713. case '.':
  714. case '4':
  715. case 'A':
  716. case 'B':
  717. case 'M':
  718. case 'N':
  719. case 'P':
  720. case 'Q':
  721. case 'X':
  722. case '\010':
  723. break;
  724. case ',': /* ignored 3 byte escapes */
  725. case '\016':
  726. case '\021':
  727. getc(fp_in);
  728. break;
  729. case '3': /* graphics mode - should quit! */
  730. case '7':
  731. case 'G':
  732. case 'V':
  733. case 'Y':
  734. case 'Z':
  735. error(FATAL, "graphics mode is not implemented");
  736. break;
  737. default:
  738. error(FATAL, "missing case for escape o%o\n", ch);
  739. break;
  740. } /* End switch */
  741. } /* End of escape */
  742. /*****************************************************************************/
  743. vmot(n)
  744. int n; /* move this far vertically */
  745. {
  746. /*
  747. *
  748. * Move vertically n units from where we are.
  749. *
  750. */
  751. vpos += n;
  752. } /* End of vmot */
  753. /*****************************************************************************/
  754. vgoto(n)
  755. int n; /* new vertical position */
  756. {
  757. /*
  758. *
  759. * Moves to absolute vertical position n.
  760. *
  761. */
  762. vpos = n;
  763. } /* End of vgoto */
  764. /*****************************************************************************/
  765. hmot(n)
  766. int n; /* move this horizontally */
  767. {
  768. /*
  769. *
  770. * Moves horizontally n units from our current position.
  771. *
  772. */
  773. hpos += n * advance;
  774. if ( hpos < leftmargin )
  775. hpos = leftmargin;
  776. } /* End of hmot */
  777. /*****************************************************************************/
  778. hgoto(n)
  779. int n; /* go to this horizontal position */
  780. {
  781. /*
  782. *
  783. * Moves to absolute horizontal position n.
  784. *
  785. */
  786. hpos = n;
  787. } /* End of hgoto */
  788. /*****************************************************************************/
  789. changefont(name)
  790. char *name;
  791. {
  792. /*
  793. *
  794. * Changes the current font. Used to get in and out of auto underscore and bold
  795. * printing.
  796. *
  797. */
  798. endline();
  799. fprintf(fp_out, "%s f\n", name);
  800. } /* End of changefont */
  801. /*****************************************************************************/
  802. startline()
  803. {
  804. /*
  805. *
  806. * Called whenever we want to be certain we're ready to start pushing characters
  807. * into an open string on the stack. If stringcount is positive we've already
  808. * started, so there's nothing to do. The first string starts in column 1.
  809. *
  810. */
  811. if ( stringcount < 1 ) {
  812. putc('(', fp_out);
  813. stringstart = lastx = hpos;
  814. lasty = vpos;
  815. lasthmi = hmi;
  816. lastc = -1;
  817. prevx = -1;
  818. stringcount = 1;
  819. } /* End if */
  820. } /* End of startline */
  821. /*****************************************************************************/
  822. endline()
  823. {
  824. /*
  825. *
  826. * Generates a call to the PostScript procedure that processes the text on the
  827. * the stack - provided stringcount is positive.
  828. *
  829. */
  830. if ( stringcount > 0 )
  831. fprintf(fp_out, ")%d %d %d t\n", stringstart, lasty, lasthmi);
  832. stringcount = 0;
  833. } /* End of endline */
  834. /*****************************************************************************/
  835. endstring()
  836. {
  837. /*
  838. *
  839. * Takes the string we've been working on and adds it to the output file. Called
  840. * when we need to adjust our horizontal position before starting a new string.
  841. * Also called from endline() when we're done with the current line.
  842. *
  843. */
  844. if ( stringcount > 0 ) {
  845. fprintf(fp_out, ")%d(", stringstart);
  846. lastx = stringstart = hpos;
  847. stringcount++;
  848. } /* End if */
  849. } /* End of endstring */
  850. /*****************************************************************************/
  851. oput(ch)
  852. int ch; /* next output character */
  853. {
  854. /*
  855. *
  856. * Responsible for adding all printing characters from the input file to the
  857. * open string on top of the stack. The only other characters that end up in
  858. * that string are the quotes required for special characters. Reverse printing
  859. * mode hasn't been tested but it should be close. hpos and lastx should disagree
  860. * each time (except after startline() does something), and that should force a
  861. * call to endstring() for every character.
  862. *
  863. */
  864. if ( stringcount > 100 ) /* don't put too much on the stack */
  865. endline();
  866. if ( vpos != lasty )
  867. endline();
  868. if ( advance == -1 ) /* for reverse printing - move first */
  869. hmot(hmi);
  870. startline();
  871. if ( lastc != ch || hpos != prevx ) {
  872. if ( lastx != hpos )
  873. endstring();
  874. if ( isascii(ch) && isprint(ch) ) {
  875. if ( ch == '\\' || ch == '(' || ch == ')' )
  876. putc('\\', fp_out);
  877. putc(ch, fp_out);
  878. } else fprintf(fp_out, "\\%.3o", ch & 0377);
  879. lastc = ch;
  880. prevx = hpos;
  881. lastx += lasthmi;
  882. } /* End if */
  883. if ( advance != -1 )
  884. hmot(hmi);
  885. markedpage = TRUE;
  886. } /* End of oput */
  887. /*****************************************************************************/
  888. redirect(pg)
  889. int pg; /* next page we're printing */
  890. {
  891. static FILE *fp_null = NULL; /* if output is turned off */
  892. /*
  893. *
  894. * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
  895. * otherwise output goes to stdout.
  896. *
  897. */
  898. if ( pg >= 0 && in_olist(pg) == ON )
  899. fp_out = stdout;
  900. else if ( (fp_out = fp_null) == NULL )
  901. fp_out = fp_null = fopen("/dev/null", "w");
  902. } /* End of redirect */
  903. /*****************************************************************************/