Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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