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