|
|
1.1 root 1: /*
2: *
3: * postprint - PostScript translator for ASCII files.
4: *
5: * A simple program that translates ASCII files into PostScript. All it really
6: * does is expand tabs and backspaces, handle character quoting, print text lines,
7: * and control when pages are started based on the requested number of lines per
8: * page.
9: *
10: * The PostScript prologue is copied from *prologue before any of the input files
11: * are translated. The program expects that the following procedures are defined
12: * in that file:
13: *
14: * setup
15: *
16: * mark ... setup -
17: *
18: * Handles special initialization stuff that depends on how the program
19: * was called. Expects to find a mark followed by key/value pairs on the
20: * stack. The def operator is applied to each pair up to the mark, then
21: * the default state is set up.
22: *
23: * pagesetup
24: *
25: * page pagesetup -
26: *
27: * Does whatever is needed to set things up for the next page. Expects
28: * to find the current page number on the stack.
29: *
30: * l
31: *
32: * string l -
33: *
34: * Prints string starting in the first column and then goes to the next
35: * line.
36: *
37: * L
38: *
39: * mark string column string column ... L mark
40: *
41: * Prints each string on the stack starting at the horizontal position
42: * selected by column. Used when tabs and spaces can be sufficiently well
43: * compressed to make the printer overhead worthwhile. Always used when
44: * we have to back up.
45: *
46: * done
47: *
48: * done
49: *
50: * Makes sure the last page is printed. Only needed when we're printing
51: * more than one page on each sheet of paper.
52: *
53: * Almost everything has been changed in this version of postprint. The program
54: * is more intelligent, especially about tabs, spaces, and backspacing, and as a
55: * result output files usually print faster. Output files also now conform to
56: * Adobe's file structuring conventions, which is undoubtedly something I should
57: * have done in the first version of the program. If the number of lines per page
58: * is set to 0, which can be done using the -l option, pointsize will be used to
59: * guess a reasonable value. The estimate is based on the values of LINESPP,
60: * POINTSIZE, and pointsize, and assumes LINESPP lines would fit on a page if
61: * we printed in size POINTSIZE. Selecting a point size using the -s option and
62: * adding -l0 to the command line forces the guess to be made.
63: *
64: * Many default values, like the magnification and orientation, are defined in
65: * the prologue, which is where they belong. If they're changed (by options), an
66: * appropriate definition is made after the prologue is added to the output file.
67: * The -P option passes arbitrary PostScript through to the output file. Among
68: * other things it can be used to set (or change) values that can't be accessed by
69: * other options.
70: *
71: */
72:
73: #include <stdio.h>
74: #include <signal.h>
75: #include <ctype.h>
76: #include <fcntl.h>
77:
78: #include "comments.h" /* PostScript file structuring comments */
79: #include "gen.h" /* general purpose definitions */
80: #include "path.h" /* for the prologue */
81: #include "ext.h" /* external variable declarations */
82: #include "postprint.h" /* a few special definitions */
83:
84: char *optnames = "a:c:ef:l:m:n:o:p:r:s:t:x:y:A:C:E:J:L:P:R:DI";
85:
86: char *prologue = POSTPRINT; /* default PostScript prologue */
87: char *formfile = FORMFILE; /* stuff for multiple pages per sheet */
88:
89: int formsperpage = 1; /* page images on each piece of paper */
90: int copies = 1; /* and this many copies of each sheet */
91:
92: int linespp = LINESPP; /* number of lines per page */
93: int pointsize = POINTSIZE; /* in this point size */
94: int tabstops = TABSTOPS; /* tabs set at these columns */
95: int crmode = 0; /* carriage return mode - 0, 1, or 2 */
96: int extended = TRUE; /* use escapes for unprintable chars */
97:
98: int col = 1; /* next character goes in this column */
99: int line = 1; /* on this line */
100:
101: int stringcount = 0; /* number of strings on the stack */
102: int stringstart = 1; /* column where current one starts */
103:
104: Fontmap fontmap[] = FONTMAP; /* for translating font names */
105: char *fontname = "Courier"; /* use this PostScript font */
106:
107: int page = 0; /* page we're working on */
108: int printed = 0; /* printed this many pages */
109:
110: FILE *fp_in = stdin; /* read from this file */
111: FILE *fp_out = stdout; /* and write stuff here */
112: FILE *fp_acct = NULL; /* for accounting data */
113:
114: /*****************************************************************************/
115:
116: main(agc, agv)
117:
118: int agc;
119: char *agv[];
120:
121: {
122:
123: /*
124: *
125: * A simple program that translates ASCII files into PostScript. If there's more
126: * than one input file, each begins on a new page.
127: *
128: */
129:
130: argc = agc; /* other routines may want them */
131: argv = agv;
132:
133: prog_name = argv[0]; /* really just for error messages */
134:
135: init_signals(); /* sets up interrupt handling */
136: header(); /* PostScript header and prologue */
137: options(); /* handle the command line options */
138: setup(); /* for PostScript */
139: arguments(); /* followed by each input file */
140: done(); /* print the last page etc. */
141: account(); /* job accounting data */
142:
143: exit(x_stat); /* not much could be wrong */
144:
145: } /* End of main */
146:
147: /*****************************************************************************/
148:
149: init_signals()
150:
151: {
152:
153: /*
154: *
155: * Makes sure we handle interrupts.
156: *
157: */
158:
159: if ( signal(SIGINT, interrupt) == SIG_IGN ) {
160: signal(SIGINT, SIG_IGN);
161: signal(SIGQUIT, SIG_IGN);
162: signal(SIGHUP, SIG_IGN);
163: } else {
164: signal(SIGHUP, interrupt);
165: signal(SIGQUIT, interrupt);
166: } /* End else */
167:
168: signal(SIGTERM, interrupt);
169:
170: } /* End of init_signals */
171:
172: /*****************************************************************************/
173:
174: header()
175:
176: {
177:
178: int ch; /* return value from getopt() */
179: int old_optind = optind; /* for restoring optind - should be 1 */
180:
181: /*
182: *
183: * Scans the option list looking for things, like the prologue file, that we need
184: * right away but could be changed from the default. Doing things this way is an
185: * attempt to conform to Adobe's latest file structuring conventions. In particular
186: * they now say there should be nothing executed in the prologue, and they have
187: * added two new comments that delimit global initialization calls. Once we know
188: * where things really are we write out the job header, follow it by the prologue,
189: * and then add the ENDPROLOG and BEGINSETUP comments.
190: *
191: */
192:
193: while ( (ch = getopt(argc, argv, optnames)) != EOF )
194: if ( ch == 'L' )
195: prologue = optarg;
196: else if ( ch == '?' )
197: error(FATAL, "");
198:
199: optind = old_optind; /* get ready for option scanning */
200:
201: fprintf(stdout, "%s", CONFORMING);
202: fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
203: fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
204: fprintf(stdout, "%s %s\n", PAGES, ATEND);
205: fprintf(stdout, "%s", ENDCOMMENTS);
206:
207: if ( cat(prologue) == FALSE )
208: error(FATAL, "can't read %s", prologue);
209:
210: if ( DOROUND )
211: cat(ROUNDPAGE);
212:
213: fprintf(stdout, "%s", ENDPROLOG);
214: fprintf(stdout, "%s", BEGINSETUP);
215: fprintf(stdout, "mark\n");
216:
217: } /* End of header */
218:
219: /*****************************************************************************/
220:
221: options()
222:
223: {
224:
225: int ch; /* return value from getopt() */
226:
227: /*
228: *
229: * Reads and processes the command line options. Added the -P option so arbitrary
230: * PostScript code can be passed through. Expect it could be useful for changing
231: * definitions in the prologue for which options have not been defined.
232: *
233: * Although any PostScript font can be used, things will only work well for
234: * constant width fonts.
235: *
236: */
237:
238: while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
239: switch ( ch ) {
240:
241: case 'a': /* aspect ratio */
242: fprintf(stdout, "/aspectratio %s def\n", optarg);
243: break;
244:
245: case 'c': /* copies */
246: copies = atoi(optarg);
247: fprintf(stdout, "/#copies %s store\n", optarg);
248: break;
249:
250: case 'e': /* obsolete - it's now always on */
251: extended = TRUE;
252: break;
253:
254: case 'f': /* use this PostScript font */
255: fontname = get_font(optarg);
256: fprintf(stdout, "/font /%s def\n", fontname);
257: break;
258:
259: case 'l': /* lines per page */
260: linespp = atoi(optarg);
261: break;
262:
263: case 'm': /* magnification */
264: fprintf(stdout, "/magnification %s def\n", optarg);
265: break;
266:
267: case 'n': /* forms per page */
268: formsperpage = atoi(optarg);
269: fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
270: fprintf(stdout, "/formsperpage %s def\n", optarg);
271: break;
272:
273: case 'o': /* output page list */
274: out_list(optarg);
275: break;
276:
277: case 'p': /* landscape or portrait mode */
278: if ( *optarg == 'l' )
279: fprintf(stdout, "/landscape true def\n");
280: else fprintf(stdout, "/landscape false def\n");
281: break;
282:
283: case 'r': /* carriage return mode */
284: crmode = atoi(optarg);
285: break;
286:
287: case 's': /* point size */
288: pointsize = atoi(optarg);
289: fprintf(stdout, "/pointsize %s def\n", optarg);
290: break;
291:
292: case 't': /* tabstops */
293: tabstops = atoi(optarg);
294: break;
295:
296: case 'x': /* shift things horizontally */
297: fprintf(stdout, "/xoffset %s def\n", optarg);
298: break;
299:
300: case 'y': /* and vertically on the page */
301: fprintf(stdout, "/yoffset %s def\n", optarg);
302: break;
303:
304: case 'A': /* force job accounting */
305: case 'J':
306: if ( (fp_acct = fopen(optarg, "a")) == NULL )
307: error(FATAL, "can't open accounting file %s", optarg);
308: break;
309:
310: case 'C': /* copy file straight to output */
311: if ( cat(optarg) == FALSE )
312: error(FATAL, "can't read %s", optarg);
313: break;
314:
315: case 'E': /* text font encoding */
316: fontencoding = optarg;
317: break;
318:
319: case 'L': /* PostScript prologue file */
320: prologue = optarg;
321: break;
322:
323: case 'P': /* PostScript pass through */
324: fprintf(stdout, "%s\n", optarg);
325: break;
326:
327: case 'R': /* special global or page level request */
328: saverequest(optarg);
329: break;
330:
331: case 'D': /* debug flag */
332: debug = ON;
333: break;
334:
335: case 'I': /* ignore FATAL errors */
336: ignore = ON;
337: break;
338:
339: case '?': /* don't understand the option */
340: error(FATAL, "");
341: break;
342:
343: default: /* don't know what to do for ch */
344: error(FATAL, "missing case for option %c\n", ch);
345: break;
346: } /* End switch */
347: } /* End while */
348:
349: argc -= optind; /* get ready for non-option args */
350: argv += optind;
351:
352: } /* End of options */
353:
354: /*****************************************************************************/
355:
356: char *get_font(name)
357:
358: char *name; /* name the user asked for */
359:
360: {
361:
362: int i; /* for looking through fontmap[] */
363:
364: /*
365: *
366: * Called from options() to map a user's font name into a legal PostScript name.
367: * If the lookup fails *name is returned to the caller. That should let you choose
368: * any PostScript font, although things will only work well for constant width
369: * fonts.
370: *
371: */
372:
373: for ( i = 0; fontmap[i].name != NULL; i++ )
374: if ( strcmp(name, fontmap[i].name) == 0 )
375: return(fontmap[i].val);
376:
377: return(name);
378:
379: } /* End of get_font */
380:
381: /*****************************************************************************/
382:
383: setup()
384:
385: {
386:
387: /*
388: *
389: * Handles things that must be done after the options are read but before the
390: * input files are processed. linespp (lines per page) can be set using the -l
391: * option. If it's not positive we calculate a reasonable value using the
392: * requested point size - assuming LINESPP lines fit on a page in point size
393: * POINTSIZE.
394: *
395: */
396:
397: writerequest(0, stdout); /* global requests eg. manual feed */
398: setencoding(fontencoding);
399: fprintf(stdout, "setup\n");
400:
401: if ( formsperpage > 1 ) {
402: if ( cat(formfile) == FALSE )
403: error(FATAL, "can't read %s", formfile);
404: fprintf(stdout, "%d setupforms\n", formsperpage);
405: } /* End if */
406:
407: fprintf(stdout, "%s", ENDSETUP);
408:
409: if ( linespp <= 0 )
410: linespp = LINESPP * POINTSIZE / pointsize;
411:
412: } /* End of setup */
413:
414: /*****************************************************************************/
415:
416: arguments()
417:
418: {
419:
420: /*
421: *
422: * Makes sure all the non-option command line arguments are processed. If we get
423: * here and there aren't any arguments left, or if '-' is one of the input files
424: * we'll translate stdin.
425: *
426: */
427:
428: if ( argc < 1 )
429: text();
430: else { /* at least one argument is left */
431: while ( argc > 0 ) {
432: if ( strcmp(*argv, "-") == 0 )
433: fp_in = stdin;
434: else if ( (fp_in = fopen(*argv, "r")) == NULL )
435: error(FATAL, "can't open %s", *argv);
436: text();
437: if ( fp_in != stdin )
438: fclose(fp_in);
439: argc--;
440: argv++;
441: } /* End while */
442: } /* End else */
443:
444: } /* End of arguments */
445:
446: /*****************************************************************************/
447:
448: done()
449:
450: {
451:
452: /*
453: *
454: * Finished with all the input files, so mark the end of the pages with a TRAILER
455: * comment, make sure the last page prints, and add things like the PAGES comment
456: * that can only be determined after all the input files have been read.
457: *
458: */
459:
460: fprintf(stdout, "%s", TRAILER);
461: fprintf(stdout, "done\n");
462: fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
463: fprintf(stdout, "%s %d\n", PAGES, printed);
464:
465: } /* End of done */
466:
467: /*****************************************************************************/
468:
469: account()
470:
471: {
472:
473: /*
474: *
475: * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
476: * requested using the -A or -J options.
477: *
478: */
479:
480: if ( fp_acct != NULL )
481: fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
482:
483: } /* End of account */
484:
485: /*****************************************************************************/
486:
487: text()
488:
489: {
490:
491: int ch; /* next input character */
492:
493: /*
494: *
495: * Translates *fp_in into PostScript. Intercepts space, tab, backspace, newline,
496: * return, and formfeed. Everything else goes to oput(), which handles quoting
497: * (if needed) and escapes for nonascii characters if extended is TRUE. The
498: * redirect(-1) call forces the initial output to go to /dev/null - so stuff
499: * that formfeed() does at the end of each page goes to /dev/null rather than
500: * the real output file.
501: *
502: */
503:
504: redirect(-1); /* get ready for the first page */
505: formfeed(); /* force PAGE comment etc. */
506:
507: while ( (ch = getc(fp_in)) != EOF )
508: switch ( ch ) {
509: case '\n':
510: newline();
511: break;
512:
513: case '\t':
514: case '\b':
515: case ' ':
516: spaces(ch);
517: break;
518:
519: case '\014':
520: formfeed();
521: break;
522:
523: case '\r':
524: if ( crmode == 1 )
525: spaces(ch);
526: else if ( crmode == 2 )
527: newline();
528: break;
529:
530: default:
531: oput(ch);
532: break;
533: } /* End switch */
534:
535: formfeed(); /* next file starts on a new page? */
536:
537: } /* End of text */
538:
539: /*****************************************************************************/
540:
541: formfeed()
542:
543: {
544:
545: /*
546: *
547: * Called whenever we've finished with the last page and want to get ready for the
548: * next one. Also used at the beginning and end of each input file, so we have to
549: * be careful about what's done. The first time through (up to the redirect() call)
550: * output goes to /dev/null.
551: *
552: * Adobe now recommends that the showpage operator occur after the page level
553: * restore so it can be easily redefined to have side-effects in the printer's VM.
554: * Although it seems reasonable I haven't implemented it, because it makes other
555: * things, like selectively setting manual feed or choosing an alternate paper
556: * tray, clumsy - at least on a per page basis.
557: *
558: */
559:
560: if ( fp_out == stdout ) /* count the last page */
561: printed++;
562:
563: endline(); /* print the last line */
564:
565: fprintf(fp_out, "cleartomark\n");
566: fprintf(fp_out, "showpage\n");
567: fprintf(fp_out, "saveobj restore\n");
568: fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
569:
570: if ( ungetc(getc(fp_in), fp_in) == EOF )
571: redirect(-1);
572: else redirect(++page);
573:
574: fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
575: fprintf(fp_out, "/saveobj save def\n");
576: fprintf(fp_out, "mark\n");
577: writerequest(printed+1, fp_out);
578: fprintf(fp_out, "%d pagesetup\n", printed+1);
579:
580: line = 1;
581:
582: } /* End of formfeed */
583:
584: /*****************************************************************************/
585:
586: newline()
587:
588: {
589:
590: /*
591: *
592: * Called when we've read a newline character. The call to startline() ensures
593: * that at least an empty string is on the stack.
594: *
595: */
596:
597: startline();
598: endline(); /* print the current line */
599:
600: if ( ++line > linespp ) /* done with this page */
601: formfeed();
602:
603: } /* End of newline */
604:
605: /*****************************************************************************/
606:
607: spaces(ch)
608:
609: int ch; /* next input character */
610:
611: {
612:
613: int endcol; /* ending column */
614: int i; /* final distance - in spaces */
615:
616: /*
617: *
618: * Counts consecutive spaces, tabs, and backspaces and figures out where the next
619: * string should start. Once that's been done we try to choose an efficient way
620: * to output the required number of spaces. The choice is between using procedure
621: * l with a single string on the stack and L with several string and column pairs.
622: * We usually break even, in terms of the size of the output file, if we need four
623: * consecutive spaces. More means using L decreases the size of the file. For now
624: * if there are less than 6 consecutive spaces we just add them to the current
625: * string, otherwise we end that string, follow it by its starting position, and
626: * begin a new one that starts at endcol. Backspacing is always handled this way.
627: *
628: */
629:
630: startline(); /* so col makes sense */
631: endcol = col;
632:
633: do {
634: if ( ch == ' ' )
635: endcol++;
636: else if ( ch == '\t' )
637: endcol += tabstops - ((endcol - 1) % tabstops);
638: else if ( ch == '\b' )
639: endcol--;
640: else if ( ch == '\r' )
641: endcol = 1;
642: else break;
643: } while ( ch = getc(fp_in) ); /* if ch is 0 we'd quit anyway */
644:
645: ungetc(ch, fp_in); /* wasn't a space, tab, or backspace */
646:
647: if ( endcol < 1 ) /* can't move past left edge */
648: endcol = 1;
649:
650: if ( (i = endcol - col) >= 0 && i < 6 )
651: for ( ; i > 0; i-- )
652: oput((int)' ');
653: else {
654: fprintf(fp_out, ")%d(", stringstart-1);
655: stringcount++;
656: col = stringstart = endcol;
657: } /* End else */
658:
659: } /* End of spaces */
660:
661: /*****************************************************************************/
662:
663: startline()
664:
665: {
666:
667: /*
668: *
669: * Called whenever we want to be certain we're ready to start pushing characters
670: * into an open string on the stack. If stringcount is positive we've already
671: * started, so there's nothing to do. The first string starts in column 1.
672: *
673: */
674:
675: if ( stringcount < 1 ) {
676: putc('(', fp_out);
677: stringstart = col = 1;
678: stringcount = 1;
679: } /* End if */
680:
681: } /* End of startline */
682:
683: /*****************************************************************************/
684:
685: endline()
686:
687: {
688:
689: /*
690: *
691: * Generates a call to the PostScript procedure that processes all the text on
692: * the stack - provided stringcount is positive. If one string is on the stack
693: * the fast procedure (ie. l) is used to print the line, otherwise the slower
694: * one that processes string and column pairs is used.
695: *
696: */
697:
698: if ( stringcount == 1 )
699: fprintf(fp_out, ")l\n");
700: else if ( stringcount > 1 )
701: fprintf(fp_out, ")%d L\n", stringstart-1);
702:
703: stringcount = 0;
704:
705: } /* End of endline */
706:
707: /*****************************************************************************/
708:
709: oput(ch)
710:
711: int ch; /* next output character */
712:
713: {
714:
715: /*
716: *
717: * Responsible for adding all printing characters from the input file to the
718: * open string on top of the stack.
719: *
720: */
721:
722: if ( isascii(ch) && isprint(ch) ) {
723: startline();
724: if ( ch == '(' || ch == ')' || ch == '\\' )
725: putc('\\', fp_out);
726: putc(ch, fp_out);
727: col++;
728: } else if ( extended == TRUE ) {
729: startline();
730: fprintf(fp_out, "\\%.3o", ch & 0377);
731: col++;
732: } /* End if */
733:
734: } /* End of oput */
735:
736: /*****************************************************************************/
737:
738: redirect(pg)
739:
740: int pg; /* next page we're printing */
741:
742: {
743:
744: static FILE *fp_null = NULL; /* if output is turned off */
745:
746: /*
747: *
748: * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
749: * otherwise output goes to stdout.
750: *
751: */
752:
753: if ( pg >= 0 && in_olist(pg) == ON )
754: fp_out = stdout;
755: else if ( (fp_out = fp_null) == NULL )
756: fp_out = fp_null = fopen("/dev/null", "w");
757:
758: } /* End of redirect */
759:
760: /*****************************************************************************/
761:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.