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