xref: /illumos-gate/usr/src/cmd/lp/filter/postscript/postprint/postprint.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 /*
25  * Copyright 2002 Sun Microsystems, Inc.  All Rights Reserved
26  * Use is subject to license terms
27  */
28 
29 
30 #ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33  *
34  * postprint - PostScript translator for ASCII files.
35  *
36  * A simple program that translates ASCII files into PostScript. All it really
37  * does is expand tabs and backspaces, handle character quoting, print text lines,
38  * and control when pages are started based on the requested number of lines per
39  * page.
40  *
41  * The PostScript prologue is copied from *prologue before any of the input files
42  * are translated. The program expects that the following procedures are defined
43  * in that file:
44  *
45  *	setup
46  *
47  *	  mark ... setup -
48  *
49  *	    Handles special initialization stuff that depends on how the program
50  *	    was called. Expects to find a mark followed by key/value pairs on the
51  *	    stack. The def operator is applied to each pair up to the mark, then
52  *	    the default state is set up.
53  *
54  *	pagesetup
55  *
56  *	  page pagesetup -
57  *
58  *	    Does whatever is needed to set things up for the next page. Expects
59  *	    to find the current page number on the stack.
60  *
61  *	l
62  *
63  *	  string l -
64  *
65  *	    Prints string starting in the first column and then goes to the next
66  *	    line.
67  *
68  *	L
69  *
70  *	  mark string column string column ... L mark
71  *
72  *	    Prints each string on the stack starting at the horizontal position
73  *	    selected by column. Used when tabs and spaces can be sufficiently well
74  *	    compressed to make the printer overhead worthwhile. Always used when
75  *	    we have to back up.
76  *
77  *	done
78  *
79  *	  done
80  *
81  *	    Makes sure the last page is printed. Only needed when we're printing
82  *	    more than one page on each sheet of paper.
83  *
84  * Almost everything has been changed in this version of postprint. The program
85  * is more intelligent, especially about tabs, spaces, and backspacing, and as a
86  * result output files usually print faster. Output files also now conform to
87  * Adobe's file structuring conventions, which is undoubtedly something I should
88  * have done in the first version of the program. If the number of lines per page
89  * is set to 0, which can be done using the -l option, pointsize will be used to
90  * guess a reasonable value. The estimate is based on the values of LINESPP,
91  * POINTSIZE, and pointsize, and assumes LINESPP lines would fit on a page if
92  * we printed in size POINTSIZE. Selecting a point size using the -s option and
93  * adding -l0 to the command line forces the guess to be made.
94  *
95  * Many default values, like the magnification and orientation, are defined in
96  * the prologue, which is where they belong. If they're changed (by options), an
97  * appropriate definition is made after the prologue is added to the output file.
98  * The -P option passes arbitrary PostScript through to the output file. Among
99  * other things it can be used to set (or change) values that can't be accessed by
100  * other options.
101  *
102  */
103 
104 
105 #include <stdio.h>
106 #include <signal.h>
107 #include <ctype.h>
108 #include <fcntl.h>
109 #include <unistd.h>
110 
111 #include "comments.h"			/* PostScript file structuring comments */
112 #include "gen.h"			/* general purpose definitions */
113 #include "path.h"			/* for the prologue */
114 #include "ext.h"			/* external variable declarations */
115 #include "postprint.h"			/* a few special definitions */
116 
117 
118 char	*optnames = "a:c:e:f:l:m:n:o:p:r:s:t:x:y:A:C:J:L:P:R:DI";
119 
120 char	*prologue = POSTPRINT;		/* default PostScript prologue */
121 char	*formfile = FORMFILE;		/* stuff for multiple pages per sheet */
122 char	*locale = NULL;
123 
124 int	formsperpage = 1;		/* page images on each piece of paper */
125 int	copies = 1;			/* and this many copies of each sheet */
126 
127 int	linespp = LINESPP;		/* number of lines per page */
128 int	pointsize = POINTSIZE;		/* in this point size */
129 int	tabstops = TABSTOPS;		/* tabs set at these columns */
130 int	crmode = 0;			/* carriage return mode - 0, 1, or 2 */
131 
132 int	col = 1;			/* next character goes in this column */
133 int	line = 1;			/* on this line */
134 
135 int	stringcount = 0;		/* number of strings on the stack */
136 int	stringstart = 1;		/* column where current one starts */
137 
138 Fontmap	fontmap[] = FONTMAP;		/* for translating font names */
139 char	*fontname = "Courier";		/* use this PostScript font */
140 
141 int	page = 0;			/* page we're working on */
142 int	printed = 0;			/* printed this many pages */
143 
144 FILE	*fp_in = stdin;			/* read from this file */
145 FILE	*fp_out = stdout;		/* and write stuff here */
146 FILE	*fp_acct = NULL;		/* for accounting data */
147 
148 
149 /*****************************************************************************/
150 
151 
152 main(agc, agv)
153 
154 
155     int		agc;
156     char	*agv[];
157 
158 
159 {
160 
161 
162 /*
163  *
164  * A simple program that translates ASCII files into PostScript. If there's more
165  * than one input file, each begins on a new page.
166  *
167  */
168 
169 
170     argc = agc;				/* other routines may want them */
171     argv = agv;
172 
173     prog_name = argv[0];		/* really just for error messages */
174 
175     init_signals();			/* sets up interrupt handling */
176     header();				/* PostScript header and prologue */
177 /*  options();				/* handle the command line options */
178     setup();				/* for PostScript */
179     arguments();			/* followed by each input file */
180     done();				/* print the last page etc. */
181     account();				/* job accounting data */
182 
183     exit(x_stat);			/* not much could be wrong */
184 
185 }   /* End of main */
186 
187 
188 /*****************************************************************************/
189 
190 
191 init_signals()
192 
193 
194 {
195 
196 
197     void	interrupt();		/* signal handler */
198 
199 
200 /*
201  *
202  * Makes sure we handle interrupts.
203  *
204  */
205 
206 
207     if ( signal(SIGINT, interrupt) == SIG_IGN )  {
208 	signal(SIGINT, SIG_IGN);
209 	signal(SIGQUIT, SIG_IGN);
210 	signal(SIGHUP, SIG_IGN);
211     } else {
212 	signal(SIGHUP, interrupt);
213 	signal(SIGQUIT, interrupt);
214     }   /* End else */
215 
216     signal(SIGTERM, interrupt);
217 
218 }   /* End of init_signals */
219 
220 
221 /*****************************************************************************/
222 
223 
224 header()
225 
226 
227 {
228 
229 
230     int		ch;			/* return value from getopt() */
231     int		old_optind = optind;	/* for restoring optind - should be 1 */
232 
233 
234 /*
235  *
236  * Scans the option list looking for things, like the prologue file, that we need
237  * right away but could be changed from the default. Doing things this way is an
238  * attempt to conform to Adobe's latest file structuring conventions. In particular
239  * they now say there should be nothing executed in the prologue, and they have
240  * added two new comments that delimit global initialization calls. Once we know
241  * where things really are we write out the job header, follow it by the prologue,
242  * and then add the ENDPROLOG and BEGINSETUP comments.
243  *
244  */
245 
246 
247     while ( (ch = getopt(argc, argv, optnames)) != EOF )
248 	if ( ch == 'L' )
249 	    prologue = optarg;
250 	else if ( ch == '?' )
251 	    error(FATAL, "");
252 
253     optind = old_optind;		/* get ready for option scanning */
254 
255     fprintf(stdout, "%s", CONFORMING);
256     fprintf(stdout, "%s %s\n", CREATOR, "%M%");
257     fprintf(stdout, "%s %s\n", VERSION, "%I%");
258     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
259     fprintf(stdout, "%s %s\n", PAGES, ATEND);
260     fprintf(stdout, "%s", ENDCOMMENTS);
261 
262     options();				/* handle the command line options */
263 
264     if ( cat(prologue) == FALSE )
265 	error(FATAL, "can't read %s", prologue);
266 
267     fprintf(stdout, "%s", ENDPROLOG);
268     fprintf(stdout, "%s", BEGINSETUP);
269     fprintf(stdout, "mark\n");
270 
271 }   /* End of header */
272 
273 
274 /*****************************************************************************/
275 
276 
277 options()
278 
279 
280 {
281 
282 
283     int		ch;			/* return value from getopt() */
284     int		euro = 0;
285     extern char *getenv(char *);
286 
287 
288 /*
289  *
290  * Reads and processes the command line options. Added the -P option so arbitrary
291  * PostScript code can be passed through. Expect it could be useful for changing
292  * definitions in the prologue for which options have not been defined.
293  *
294  * Although any PostScript font can be used, things will only work well for
295  * constant width fonts.
296  *
297  */
298 
299     if  (((locale = getenv("LC_MONETARY")) != NULL) ||
300          ((locale = getenv("LANG")) != NULL)) {
301         char *tmp = NULL;
302 
303         /* if there is a locale specific prologue, use it as the default */
304         if ((tmp = calloc(1, strlen(POSTPRINT) + strlen(locale) + 2)) != NULL) {
305             sprintf(tmp, "%s-%s", POSTPRINT, locale);
306             if (access(tmp, R_OK) == 0)
307                     prologue = tmp;
308             else
309                     free(tmp);
310         }
311 
312         /* if the locale has 8859-15 or euro in it, add the symbol to font */
313         if ((strstr(locale, "8859-15") != NULL) ||
314 	    (strstr(locale, "euro") != NULL))
315 		euro = 1;
316     }
317 
318     while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
319 #if defined(DEBUG)
320 	fprintf(stderr, " Opt: %c, arg: %s\n", ch, optarg);
321 #endif
322 	switch ( ch )  {
323 
324 	    case 'a':			/* aspect ratio */
325 		    fprintf(stdout, "/aspectratio %s def\n", optarg);
326 		    break;
327 
328 	    case 'c':			/* copies */
329 		    copies = atoi(optarg);
330 		    fprintf(stdout, "/#copies %s store\n", optarg);
331 		    break;
332 
333 	    case 'e':			/* should we add the euro ? */
334 		    euro = (strcasecmp(optarg, "on") == 0);
335 		    break;
336 
337 	    case 'f':			/* use this PostScript font */
338 		    fontname = get_font(optarg);
339 		    fprintf(stdout, "/font /%s def\n", fontname);
340 		    break;
341 
342 	    case 'l':			/* lines per page */
343 		    linespp = atoi(optarg);
344 		    break;
345 
346 	    case 'm':			/* magnification */
347 		    fprintf(stdout, "/magnification %s def\n", optarg);
348 		    break;
349 
350 	    case 'n':			/* forms per page */
351 		    formsperpage = atoi(optarg);
352 
353 		    if (formsperpage <= 0) {
354 			/* set default value */
355 			formsperpage = 1;
356 		    }
357 
358 		    fprintf(stdout, "/formsperpage %d def\n", formsperpage);
359 
360 		    break;
361 
362 	    case 'o':			/* output page list */
363 		    out_list(optarg);
364 		    break;
365 
366 	    case 'p':			/* landscape or portrait mode */
367 		    if ( *optarg == 'l' )
368 			fprintf(stdout, "/landscape true def\n");
369 		    else fprintf(stdout, "/landscape false def\n");
370 		    break;
371 
372 	    case 'r':			/* carriage return mode */
373 		    crmode = atoi(optarg);
374 		    break;
375 
376 	    case 's':			/* point size */
377 		    pointsize = atoi(optarg);
378 		    fprintf(stdout, "/pointsize %s def\n", optarg);
379 		    break;
380 
381 	    case 't':			/* tabstops */
382 		    tabstops = atoi(optarg);
383 
384 		    if (tabstops <= 0) {
385 			/* set default */
386 			tabstops = TABSTOPS;
387 		    }
388 
389 		    break;
390 
391 	    case 'x':			/* shift things horizontally */
392 		    fprintf(stdout, "/xoffset %s def\n", optarg);
393 		    break;
394 
395 	    case 'y':			/* and vertically on the page */
396 		    fprintf(stdout, "/yoffset %s def\n", optarg);
397 		    break;
398 
399 	    case 'A':			/* force job accounting */
400 	    case 'J':
401 		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
402 			error(FATAL, "can't open accounting file %s", optarg);
403 		    break;
404 
405 	    case 'C':			/* copy file straight to output */
406 		    if ( cat(optarg) == FALSE )
407 			error(FATAL, "can't read %s", optarg);
408 		    break;
409 
410 	    case 'L':			/* PostScript prologue file */
411 		    prologue = optarg;
412 		    break;
413 
414 	    case 'P':			/* PostScript pass through */
415 		    fprintf(stdout, "%s\n", optarg);
416 		    break;
417 
418 	    case 'R':			/* special global or page level request */
419 		    saverequest(optarg);
420 		    break;
421 
422 	    case 'D':			/* debug flag */
423 		    debug = ON;
424 		    break;
425 
426 	    case 'I':			/* ignore FATAL errors */
427 		    ignore = ON;
428 		    break;
429 
430 	    case '?':			/* don't understand the option */
431 		    error(FATAL, "");
432 		    break;
433 
434 	    default:			/* don't know what to do for ch */
435 		    error(FATAL, "missing case for option %c\n", ch);
436 		    break;
437 
438 	}   /* End switch */
439 
440     }   /* End while */
441 
442     if (euro != 0)
443 	fprintf(stdout, "/must-add-euro-to-font true def\n");
444 
445     argc -= optind;			/* get ready for non-option args */
446     argv += optind;
447 
448 }   /* End of options */
449 
450 
451 /*****************************************************************************/
452 
453 
454 char *get_font(name)
455 
456 
457     char	*name;			/* name the user asked for */
458 
459 
460 {
461 
462 
463     int		i;			/* for looking through fontmap[] */
464 
465 
466 /*
467  *
468  * Called from options() to map a user's font name into a legal PostScript name.
469  * If the lookup fails *name is returned to the caller. That should let you choose
470  * any PostScript font, although things will only work well for constant width
471  * fonts.
472  *
473  */
474 
475 
476     for ( i = 0; fontmap[i].name != NULL; i++ )
477 	if ( strcmp(name, fontmap[i].name) == 0 )
478 	    return(fontmap[i].val);
479 
480     return(name);
481 
482 }   /* End of get_font */
483 
484 
485 /*****************************************************************************/
486 
487 
488 setup()
489 
490 
491 {
492 
493 
494 /*
495  *
496  * Handles things that must be done after the options are read but before the
497  * input files are processed. linespp (lines per page) can be set using the -l
498  * option. If it's not positive we calculate a reasonable value using the
499  * requested point size - assuming LINESPP lines fit on a page in point size
500  * POINTSIZE.
501  *
502  */
503 
504 
505     writerequest(0, stdout);		/* global requests eg. manual feed */
506     fprintf(stdout, "setup\n");
507 
508     if ( formsperpage > 1 )  {
509 	if ( cat(formfile) == FALSE )
510 	    error(FATAL, "can't read %s", formfile);
511 	fprintf(stdout, "%d setupforms\n", formsperpage);
512     }	/* End if */
513 
514     fprintf(stdout, "%s", ENDSETUP);
515 
516     if ( linespp <= 0 )
517 	linespp = LINESPP * POINTSIZE / pointsize;
518 
519 }   /* End of setup */
520 
521 
522 /*****************************************************************************/
523 
524 
525 arguments()
526 
527 
528 {
529 
530 
531 /*
532  *
533  * Makes sure all the non-option command line arguments are processed. If we get
534  * here and there aren't any arguments left, or if '-' is one of the input files
535  * we'll translate stdin.
536  *
537  */
538 
539 
540     if ( argc < 1 )
541 	text();
542     else {				/* at least one argument is left */
543 	while ( argc > 0 )  {
544 	    if ( strcmp(*argv, "-") == 0 )
545 		fp_in = stdin;
546 	    else if ( (fp_in = fopen(*argv, "r")) == NULL )
547 		error(FATAL, "can't open %s", *argv);
548 	    text();
549 	    if ( fp_in != stdin )
550 		fclose(fp_in);
551 	    argc--;
552 	    argv++;
553 	}   /* End while */
554     }   /* End else */
555 
556 }   /* End of arguments */
557 
558 
559 /*****************************************************************************/
560 
561 
562 done()
563 
564 
565 {
566 
567 
568 /*
569  *
570  * Finished with all the input files, so mark the end of the pages with a TRAILER
571  * comment, make sure the last page prints, and add things like the PAGES comment
572  * that can only be determined after all the input files have been read.
573  *
574  */
575     if (printed % formsperpage != 0) {	/* pad to ENDPAGE */
576 	while (printed % formsperpage) {
577 	    printed++;
578 
579 	    fprintf(stdout, "save\n");
580 	    fprintf(stdout, "mark\n");
581 	    writerequest(printed, stdout);
582 	    fprintf(stdout, "%d pagesetup\n", printed);
583 
584 	    fprintf(stdout, "cleartomark\n");
585 	    fprintf(stdout, "showpage\n");
586 	    fprintf(stdout, "restore\n");
587 	}
588 	fprintf(stdout, "%s %d %d\n", ENDPAGE, page, printed);
589     }
590 
591     fprintf(stdout, "%s", TRAILER);
592     fprintf(stdout, "done\n");
593     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
594     fprintf(stdout, "%s %d\n", PAGES, printed);
595 
596 }   /* End of done */
597 
598 
599 /*****************************************************************************/
600 
601 
602 account()
603 
604 
605 {
606 
607 
608 /*
609  *
610  * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
611  * requested using the -A or -J options.
612  *
613  */
614 
615 
616     if ( fp_acct != NULL )
617 	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
618 
619 }   /* End of account */
620 
621 
622 /*****************************************************************************/
623 
624 
625 text()
626 
627 
628 {
629 
630 
631     int		ch;			/* next input character */
632 
633 
634 /*
635  *
636  * Translates *fp_in into PostScript. All we do here is handle newlines, tabs,
637  * backspaces, and quoting of special characters. All other unprintable characters
638  * are totally ignored. The redirect(-1) call forces the initial output to go to
639  * /dev/null. It's done to force the stuff that formfeed() does at the end of
640  * each page to /dev/null rather than the real output file.
641  *
642  */
643 
644 
645     redirect(-1);			/* get ready for the first page */
646     formfeed();				/* force PAGE comment etc. */
647 
648     while ( (ch = getc(fp_in)) != EOF )
649 
650 	switch ( ch )  {
651 
652 	    case '\n':
653 		    newline();
654 		    break;
655 
656 	    case '\t':
657 	    case '\b':
658 	    case ' ':
659 		    spaces(ch);
660 		    break;
661 
662 	    case '\014':
663 		    formfeed();
664 		    break;
665 
666 	    case '\r':
667 		    if ( crmode == 1 )
668 			spaces(ch);
669 		    else if ( crmode == 2 )
670 			newline();
671 		    break;
672 
673 	    case '(':
674 	    case ')':
675 	    case '\\':
676 		    startline();
677 		    putc('\\', fp_out);
678 
679 /*
680  *
681  * Fall through to the default case.
682  *
683  */
684 
685 	    default:
686 		    if ( isascii(ch) && isprint(ch) )
687 			oput(ch);
688 		    else {
689 #define isintlprint(ch)	((ch)&0x80)
690 #define isss(ch)	0
691 			if (isintlprint(ch) || isss(ch)) {
692 				startline();
693 				fprintf(fp_out, "\\%03o", 0xFF&ch);
694 				col++;
695 			}
696 		    }
697 		    break;
698 
699 	}   /* End switch */
700 
701     formfeed();				/* next file starts on a new page? */
702 
703 }   /* End of text */
704 
705 
706 /*****************************************************************************/
707 
708 
709 formfeed()
710 
711 
712 {
713 
714 
715 /*
716  *
717  * Called whenever we've finished with the last page and want to get ready for the
718  * next one. Also used at the beginning and end of each input file, so we have to
719  * be careful about what's done. The first time through (up to the redirect() call)
720  * output goes to /dev/null.
721  *
722  * Adobe now recommends that the showpage operator occur after the page level
723  * restore so it can be easily redefined to have side-effects in the printer's VM.
724  * Although it seems reasonable I haven't implemented it, because it makes other
725  * things, like selectively setting manual feed or choosing an alternate paper
726  * tray, clumsy - at least on a per page basis.
727  *
728  */
729 
730 
731     if ( fp_out == stdout )		/* count the last page */
732 	printed++;
733 
734     endline();				/* print the last line */
735 
736     fprintf(fp_out, "cleartomark\n");
737     fprintf(fp_out, "showpage\n");
738     fprintf(fp_out, "restore\n");
739     if (printed % formsperpage == 0)
740 	fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
741 
742     if ( ungetc(getc(fp_in), fp_in) == EOF )
743 	redirect(-1);
744     else redirect(++page);
745 
746     if (printed % formsperpage == 0)
747 	fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
748     fprintf(fp_out, "save\n");
749     fprintf(fp_out, "mark\n");
750     writerequest(printed+1, fp_out);
751     fprintf(fp_out, "%d pagesetup\n", printed+1);
752 
753     line = 1;
754 
755 }   /* End of formfeed */
756 
757 
758 /*****************************************************************************/
759 
760 
761 newline()
762 
763 
764 {
765 
766 
767 /*
768  *
769  * Called when we've read a newline character. The call to startline() ensures
770  * that at least an empty string is on the stack.
771  *
772  */
773 
774 
775     startline();
776     endline();				/* print the current line */
777 
778     if ( ++line > linespp )		/* done with this page */
779 	formfeed();
780 
781 }   /* End of newline */
782 
783 
784 /*****************************************************************************/
785 
786 
787 spaces(ch)
788 
789 
790     int		ch;			/* next input character */
791 
792 
793 {
794 
795 
796     int		endcol;			/* ending column */
797     int		i;			/* final distance - in spaces */
798 
799 
800 /*
801  *
802  * Counts consecutive spaces, tabs, and backspaces and figures out where the next
803  * string should start. Once that's been done we try to choose an efficient way
804  * to output the required number of spaces. The choice is between using procedure
805  * l with a single string on the stack and L with several string and column pairs.
806  * We usually break even, in terms of the size of the output file, if we need four
807  * consecutive spaces. More means using L decreases the size of the file. For now
808  * if there are less than 6 consecutive spaces we just add them to the current
809  * string, otherwise we end that string, follow it by its starting position, and
810  * begin a new one that starts at endcol. Backspacing is always handled this way.
811  *
812  */
813 
814 
815     startline();			/* so col makes sense */
816     endcol = col;
817 
818     do {
819 	if ( ch == ' ' )
820 	    endcol++;
821 	else if ( ch == '\t' )
822 	    endcol += tabstops - ((endcol - 1) % tabstops);
823 	else if ( ch == '\b' )
824 	    endcol--;
825 	else if ( ch == '\r' )
826 	    endcol = 1;
827 	else break;
828     } while ( ch = getc(fp_in) );	/* if ch is 0 we'd quit anyway */
829 
830     ungetc(ch, fp_in);			/* wasn't a space, tab, or backspace */
831 
832     if ( endcol < 1 )			/* can't move past left edge */
833 	endcol = 1;
834 
835     if ( (i = endcol - col) >= 0 && i < 6 )
836 	for ( ; i > 0; i-- )
837 	    oput((int)' ');
838     else {
839 	fprintf(fp_out, ")%d(", stringstart-1);
840 	stringcount++;
841 	col = stringstart = endcol;
842     }	/* End else */
843 
844 }   /* End of spaces */
845 
846 
847 /*****************************************************************************/
848 
849 
850 startline()
851 
852 
853 {
854 
855 
856 /*
857  *
858  * Called whenever we want to be certain we're ready to start pushing characters
859  * into an open string on the stack. If stringcount is positive we've already
860  * started, so there's nothing to do. The first string starts in column 1.
861  *
862  */
863 
864 
865     if ( stringcount < 1 )  {
866 	putc('(', fp_out);
867 	stringstart = col = 1;
868 	stringcount = 1;
869     }	/* End if */
870 
871 }   /* End of startline */
872 
873 
874 /*****************************************************************************/
875 
876 
877 endline()
878 
879 
880 {
881 
882 
883 /*
884  *
885  * Generates a call to the PostScript procedure that processes all the text on
886  * the stack - provided stringcount is positive. If one string is on the stack
887  * the fast procedure (ie. l) is used to print the line, otherwise the slower
888  * one that processes string and column pairs is used.
889  *
890  */
891 
892 
893     if ( stringcount == 1 )
894 	fprintf(fp_out, ")l\n");
895     else if ( stringcount > 1 )
896 	fprintf(fp_out, ")%d L\n", stringstart-1);
897 
898     stringcount = 0;
899 
900 }   /* End of endline */
901 
902 
903 /*****************************************************************************/
904 
905 
906 oput(ch)
907 
908 
909     int		ch;			/* next output character */
910 
911 
912 {
913 
914 
915 /*
916  *
917  * Responsible for adding all printing characters from the input file to the
918  * open string on top of the stack. The only other characters that end up in
919  * that string are the quotes required for special characters. Some simple
920  * changes here and in spaces could make line wrapping possible. Doing a good
921  * job would probably force lots of printer dependent stuff into the program,
922  * so I haven't bothered with it. Could also change the prologue, or perhaps
923  * write a different one, that uses kshow instead of show to display strings.
924  *
925  */
926 
927 
928     startline();
929     putc(ch, fp_out);
930     col++;
931 
932 }   /* End of oput */
933 
934 
935 /*****************************************************************************/
936 
937 
938 redirect(pg)
939 
940 
941     int		pg;			/* next page we're printing */
942 
943 
944 {
945 
946 
947     static FILE	*fp_null = NULL;	/* if output is turned off */
948 
949 
950 /*
951  *
952  * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
953  * otherwise output goes to stdout.
954  *
955  */
956 
957 
958     if ( pg >= 0 && in_olist(pg) == ON )
959 	fp_out = stdout;
960     else if ( (fp_out = fp_null) == NULL )
961 	fp_out = fp_null = fopen("/dev/null", "w");
962 
963 }   /* End of redirect */
964 
965 
966 /*****************************************************************************/
967 
968 
969