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