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
 *
3
 * postbgi - BGI (Basic Graphical Instructions) to PostScript translator.
4
 *
5
 * A simple program that translates BGI files into PostScript. Probably only
6
 * useful in Computer Centers that support STARE or PRISM plotters. Most of the
7
 * code was borrowed from the corresponding program that was written for printers
8
 * that understand Impress.
9
 *
10
 * Extending the original program to handle PRISM jobs was not trivial. Graphics
11
 * packages that support PRISM occasionally use BGI commands that I ignored in the
12
 * STARE implementation. Subroutines, color requests, patterns (for filling), and
13
 * filled trapeziods were the most important omissions. All are now implemented,
14
 * and at present only repeats, filled slices, and raster rectangles are missing.
15
 *
16
 * Pattern filling results were not always predictable or even good, unless the
17
 * halftone screen definitions were changed and scaling was adjusted so one pixel
18
 * in user space mapped into an integral number of device space pixels. Doing that
19
 * makes the resulting PostScript output device dependent, but was often necessary.
20
 * I've added two booleans to the PostScript prologue (fixscreen and scaletodevice)
21
 * that control what's done. By default both are false (check postbgi.ps) but can
22
 * be set to true on the command line using the -P option or by hand by changing
23
 * the definitions in the prologue. A command line that would set fixscreen and
24
 * scaletodevice true would look like,
25
 *
26
 *	postbgi -P"/fixscreen true" -P"/scaletodevice true" file >file.ps
27
 *
28
 * Several other approaches are available if you want to have your spooler handle
29
 * STARE and PRISM jobs differently. A boolean called prism is defined in the
30
 * prologue (postbgi.ps) and if it's set to true PostScript procedure setup will
31
 * set fixscreen and scaletodevice to true before anything important is done. That
32
 * means the following command line,
33
 *
34
 *	postbgi -P"/prism true" file >file.ps
35
 *
36
 * accomplishes the same things as the last example. Two different prologue files,
37
 * one for STARE jobs and the other for PRISM, could be used and the spooler could
38
 * point postbgi to the appropriate one using the -L option. In that case the only
39
 * important difference in the two prologues would be the definition of prism. The
40
 * prologue used for PRISM jobs would have prism set to true, while the STARE
41
 * prologue would have it set to false.
42
 *
43
 * Also included is code that ties lines to device space coordinates. What you get
44
 * is a consistent line thickness, but placement of lines won't be exact. It's a
45
 * trade-off that should be right for most jobs. Everything is implemented in the
46
 * prologue (postbgi.ps) and nothing will be done if the linewidth is zero or if
47
 * the boolean fixlinewidth (again in postbgi.ps) is false. Once again the -P
48
 * option can be used to set fixlinewidth to whatever you choose.
49
 *
50
 * BGI supports color mixing but PostScript doesn't. BGI files that expect to mix
51
 * colors won't print properly. PostScript's fill operator overlays whatever has
52
 * already been put down. Implementing color mixing would have been a terribly
53
 * difficult job - not worth the effort!
54
 *
55
 * The PostScript prologue is copied from *prologue before any of the input files
56
 * are translated. The program expects that the following PostScript procedures
57
 * are defined in that file:
58
 *
59
 *	setup
60
 *
61
 *	  mark ... setup -
62
 *
63
 *	    Handles special initialization stuff that depends on how the program
64
 *	    was called. Expects to find a mark followed by key/value pairs on the
65
 *	    stack. The def operator is applied to each pair up to the mark, then
66
 *	    the default state is set up.
67
 *
68
 *	pagesetup
69
 *
70
 *	  page pagesetup -
71
 *
72
 *	    Does whatever is needed to set things up for the next page. Expects
73
 *	    to find the current page number on the stack.
74
 *
75
 *	v
76
 *
77
 *	  dx1 dy1 ... dxn dyn x y v -
78
 *
79
 *	    Draws the vector described by the numbers on the stack. The top two
80
 *	    numbers are the coordinates of the starting point. The rest of the
81
 *	    numbers are relative displacements from the preceeding point.
82
 *
83
 *	pp
84
 *
85
 *	  x1 y1 ... xn yn string pp -
86
 *
87
 *	    Prints string, which is always a single character, at the points
88
 *	    represented by the rest of the numbers on the stack.
89
 *
90
 *	R
91
 *
92
 *	  n deltax deltay x y R -
93
 *
94
 *	    Creates a rectangular path with its lower left corner at (x, y) and
95
 *	    sides of length deltax and deltay. The resulting path is stroked if
96
 *	    n is 0 and filled otherwise.
97
 *
98
 *	T
99
 *
100
 *	  dx3 dy3 dx2 dy2 dx1 dy1 x y T -
101
 *
102
 *	    Fills a trapezoid starting at (x, y) and having relative displacements
103
 *	    given by the (dx, dy) pairs.
104
 *
105
 *	t
106
 *
107
 *	  angle x y string t -
108
 *
109
 *	    Prints string starting at (x, y) using an orientation of angle degrees.
110
 *	    The PostScript procedure can handle any angle, but BGI files will only
111
 *	    request 0 or 90 degrees. Text printed at any other orientation will be
112
 *	    vector generated.
113
 *
114
 *	p
115
 *
116
 *	  x y p -
117
 *
118
 *	    Called to mark the point (x, y). It fills a small circle, that right
119
 *	    now has a constant radius. This stuff could probably be much more
120
 *	    efficient?
121
 *
122
 *	l
123
 *
124
 *	  array l -
125
 *
126
 *	    Sets the line drawing mode according to the description given in
127
 *	    array. The arrays that describe the different line styles are declared
128
 *	    in STYLES (file posttek.h), although it would be better to have them
129
 *	    defined in the prologue.
130
 *
131
 *	c
132
 *
133
 *	  red green blue c -
134
 *
135
 *	    Sets the current PostScript RGB color using setrgbcolor. Also used for
136
 *	    selecting appropriate patterns as colors.
137
 *
138
 *	f
139
 *
140
 *	  bgisize f -
141
 *
142
 *	    Changes the size of the font that's used to print text. bgisize is a
143
 *	    grid separation in a 5 by 7 array in which characters are assumed to
144
 *	    be built.
145
 *
146
 *	done
147
 *
148
 *	  done
149
 *
150
 *	    Makes sure the last page is printed. Only needed when we're printing
151
 *	    more than one page on each sheet of paper.
152
 *
153
 * The default line width is zero, which forces lines to be one pixel wide. That
154
 * works well for 'write to black' engines but won't be right for 'write to white'
155
 * engines. The line width can be changed using the -w option, or you can change
156
 * the initialization of linewidth in the prologue. Code in the prologue supports
157
 * the generation of uniform width lines when linewidth is non-zero and boolean
158
 * fixlinewidth is true.
159
 *
160
 * Many default values, like the magnification and orientation, are defined in 
161
 * the prologue, which is where they belong. If they're changed (by options), an
162
 * appropriate definition is made after the prologue is added to the output file.
163
 * The -P option passes arbitrary PostScript through to the output file. Among
164
 * other things it can be used to set (or change) values that can't be accessed by
165
 * other options.
166
 *
167
 */
168
 
169
#include <stdio.h>
170
#include <sys/types.h>
171
#include <fcntl.h>
172
#include <signal.h>
173
#include <math.h>
174
#include <ctype.h>
175
#ifdef plan9
176
#define	isascii(c)	((unsigned char)(c)<=0177)
177
#endif
178
 
179
#include "comments.h"			/* PostScript file structuring comments */
180
#include "gen.h"			/* general purpose definitions */
181
#include "path.h"			/* for the prologue */
182
#include "ext.h"			/* external variable declarations */
183
#include "postbgi.h"			/* a few definitions just used here */
184
 
185
char	*optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
186
 
187
char	*prologue = POSTBGI;		/* default PostScript prologue */
188
char	*formfile = FORMFILE;		/* stuff for multiple pages per sheet */
189
 
190
int	formsperpage = 1;		/* page images on each piece of paper */
191
int	copies = 1;			/* and this many copies of each sheet */
192
 
193
char	*styles[] = STYLES;		/* descriptions of line styles */
194
 
195
int	hpos = 0;			/* current horizontal */
196
int	vpos = 0;			/* and vertical position */
197
 
198
int	bgisize = BGISIZE;		/* just the character grid spacing */
199
int	linespace;			/* distance between lines of text */
200
 
201
int	bgimode;			/* character or graph mode */
202
 
203
int	in_subr = FALSE;		/* currently defining a subroutine */
204
int	in_global = FALSE;		/* to save space with subroutine defs */
205
int	subr_id = 0;			/* defining this subroutine */
206
int	shpos = 0;			/* starting horizontal */
207
int	svpos = 0;			/* and vertical positions - subroutines */
208
Disp	displacement[64];		/* dx and dy after a subroutine call */
209
 
210
Fontmap	fontmap[] = FONTMAP;		/* for translating font names */
211
char	*fontname = "Courier";		/* use this PostScript font */
212
 
213
int	page = 0;			/* page we're working on */
214
int	printed = 0;			/* printed this many pages */
215
 
216
FILE	*fp_in = stdin;			/* read from this file */
217
FILE	*fp_out = NULL;			/* and write stuff here */
218
FILE	*fp_acct = NULL;		/* for accounting data */
219
 
220
/*****************************************************************************/
221
 
222
main(agc, agv)
223
 
224
    int		agc;
225
    char	*agv[];
226
 
227
{
228
 
229
/*
230
 *
231
 * A program that converts BGI (Basic Graphical Instructions) files generated by
232
 * packages like GRAFPAC and DISSPLA into PostScript. It does an adequate job but
233
 * is far from perfect. A few things still haven't been implemented (eg. repeats
234
 * and raster rectangles), but what's here should be good enough for most of our
235
 * STARE and PRISM jobs. Color mixing (in PRISM jobs) won't work on PostScript
236
 * printers, and there's no chance I'll implement it!
237
 *
238
 */
239
 
240
    argc = agc;				/* global so everyone can use them */
241
    argv = agv;
242
 
243
    prog_name = argv[0];		/* just for error messages */
244
 
245
    init_signals();			/* set up interrupt handling */
246
    header();				/* PostScript header comments */
247
    options();				/* command line options */
248
    setup();				/* for PostScript */
249
    arguments();			/* followed by each input file */
250
    done();				/* print the last page etc. */
251
    account();				/* job accounting data */
252
 
253
    exit(x_stat);			/* everything probably went OK */
254
 
255
}   /* End of main */
256
 
257
/*****************************************************************************/
258
 
259
init_signals()
260
 
261
{
262
 
263
/*
264
 *
265
 * Make sure we handle interrupts.
266
 *
267
 */
268
 
269
    if ( signal(SIGINT, interrupt) == SIG_IGN )  {
270
	signal(SIGINT, SIG_IGN);
271
	signal(SIGQUIT, SIG_IGN);
272
	signal(SIGHUP, SIG_IGN);
273
    } else {
274
	signal(SIGHUP, interrupt);
275
	signal(SIGQUIT, interrupt);
276
    }   /* End else */
277
 
278
    signal(SIGTERM, interrupt);
279
 
280
}   /* End of init_signals */
281
 
282
/*****************************************************************************/
283
 
284
header()
285
 
286
{
287
 
288
    int		ch;			/* return value from getopt() */
289
    int		old_optind = optind;	/* for restoring optind - should be 1 */
290
 
291
/*
292
 *
293
 * Scans the option list looking for things, like the prologue file, that we need
294
 * right away but could be changed from the default. Doing things this way is an
295
 * attempt to conform to Adobe's latest file structuring conventions. In particular
296
 * they now say there should be nothing executed in the prologue, and they have
297
 * added two new comments that delimit global initialization calls. Once we know
298
 * where things really are we write out the job header, follow it by the prologue,
299
 * and then add the ENDPROLOG and BEGINSETUP comments.
300
 *
301
 */
302
 
303
    while ( (ch = getopt(argc, argv, optnames)) != EOF )
304
	if ( ch == 'L' )
305
	    prologue = optarg;
306
	else if ( ch == '?' )
307
	    error(FATAL, "");
308
 
309
    optind = old_optind;		/* get ready for option scanning */
310
 
311
    fprintf(stdout, "%s", CONFORMING);
312
    fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
313
    fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
314
    fprintf(stdout, "%s %s\n", PAGES, ATEND);
315
    fprintf(stdout, "%s", ENDCOMMENTS);
316
 
317
    if ( cat(prologue) == FALSE )
318
	error(FATAL, "can't read %s", prologue);
319
 
320
    fprintf(stdout, "%s", ENDPROLOG);
321
    fprintf(stdout, "%s", BEGINSETUP);
322
    fprintf(stdout, "mark\n");
323
 
324
}   /* End of header */
325
 
326
/*****************************************************************************/
327
 
328
options()
329
 
330
{
331
 
332
    int		ch;			/* option name - from getopt() */
333
 
334
/*
335
 *
336
 * Reads and processes the command line options.
337
 *
338
 */
339
 
340
    while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
341
	switch ( ch )  {
342
	    case 'a':			/* aspect ratio */
343
		    fprintf(stdout, "/aspectratio %s def\n", optarg);
344
		    break;
345
 
346
	    case 'c':			/* copies */
347
		    copies = atoi(optarg);
348
		    fprintf(stdout, "/#copies %s def\n", optarg);
349
		    break;
350
 
351
	    case 'f':			/* new font */
352
		    fontname = get_font(optarg);
353
		    fprintf(stdout, "/font /%s def\n", fontname);
354
		    break;
355
 
356
	    case 'm':			/* magnification */
357
		    fprintf(stdout, "/magnification %s def\n", optarg);
358
		    break;
359
 
360
	    case 'n':			/* forms per page */
361
		    formsperpage = atoi(optarg);
362
		    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
363
		    fprintf(stdout, "/formsperpage %s def\n", optarg);
364
		    break;
365
 
366
	    case 'o':			/* output page list */
367
		    out_list(optarg);
368
		    break;
369
 
370
	    case 'p':			/* landscape or portrait mode */
371
		    if ( *optarg == 'l' )
372
			fprintf(stdout, "/landscape true def\n");
373
		    else fprintf(stdout, "/landscape false def\n");
374
		    break;
375
 
376
	    case 'w':			/* line width */
377
		    fprintf(stdout, "/linewidth %s def\n", optarg);
378
		    break;
379
 
380
	    case 'x':			/* shift horizontally */
381
		    fprintf(stdout, "/xoffset %s def\n", optarg);
382
		    break;
383
 
384
	    case 'y':			/* and vertically on the page */
385
		    fprintf(stdout, "/yoffset %s def\n", optarg);
386
		    break;
387
 
388
	    case 'A':			/* force job accounting */
389
	    case 'J':
390
		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
391
		    	error(FATAL, "can't open accounting file %s", optarg);
392
		    break;
393
 
394
	    case 'C':			/* copy file straight to output */
395
		    if ( cat(optarg) == FALSE )
396
			error(FATAL, "can't read %s", optarg);
397
		    break;
398
 
399
	    case 'E':			/* text font encoding */
400
		    fontencoding = optarg;
401
		    break;
402
 
403
	    case 'L':			/* Postscript prologue file */
404
		    prologue = optarg;
405
		    break;
406
 
407
	    case 'P':			/* PostScript pass through */
408
		    fprintf(stdout, "%s\n", optarg);
409
		    break;
410
 
411
	    case 'R':			/* special global or page level request */
412
		    saverequest(optarg);
413
		    break;
414
 
415
	    case 'D':			/* debug flag */
416
		    debug = ON;
417
		    break;
418
 
419
	    case 'I':			/* ignore FATAL errors */
420
		    ignore = ON;
421
		    break;
422
 
423
	    case '?':			/* don't know the option */
424
		    error(FATAL, "");
425
		    break;
426
 
427
	    default:			/* don't know what to do for ch */
428
		    error(FATAL, "missing case for option %c", ch);
429
		    break;
430
	}   /* End switch */
431
    }	/* End while */
432
 
433
    argc -= optind;			/* get ready for non-option args */
434
    argv += optind;
435
 
436
}   /* End of options */
437
 
438
/*****************************************************************************/
439
 
440
char *get_font(name)
441
 
442
    char	*name;			/* name the user asked for */
443
 
444
{
445
 
446
    int		i;			/* for looking through fontmap[] */
447
 
448
/*
449
 *
450
 * Called from options() to map a user's font name into a legal PostScript name.
451
 * If the lookup fails *name is returned to the caller. That should let you choose
452
 * any PostScript font.
453
 *
454
 */
455
 
456
    for ( i = 0; fontmap[i].name != NULL; i++ )
457
	if ( strcmp(name, fontmap[i].name) == 0 )
458
	    return(fontmap[i].val);
459
 
460
    return(name);
461
 
462
}   /* End of get_font */
463
 
464
/*****************************************************************************/
465
 
466
setup()
467
 
468
{
469
 
470
/*
471
 *
472
 * Handles things that must be done after the options are read but before the
473
 * input files are processed.
474
 *
475
 */
476
 
477
    writerequest(0, stdout);		/* global requests eg. manual feed */
478
    setencoding(fontencoding);
479
    fprintf(stdout, "setup\n");
480
 
481
    if ( formsperpage > 1 )  {
482
	if ( cat(formfile) == FALSE )
483
	    error(FATAL, "can't read %s", formfile);
484
	fprintf(stdout, "%d setupforms\n", formsperpage);
485
    }	/* End if */
486
 
487
    fprintf(stdout, "%s", ENDSETUP);
488
 
489
}   /* End of setup */
490
 
491
/*****************************************************************************/
492
 
493
arguments()
494
 
495
{
496
 
497
/*
498
 *
499
 * Makes sure all the non-option command line options are processed. If we get
500
 * here and there aren't any arguments left, or if '-' is one of the input files
501
 * we'll process stdin.
502
 *
503
 */
504
 
505
    if ( argc < 1 )
506
	conv();
507
    else
508
	while ( argc > 0 )  {
509
	    if ( strcmp(*argv, "-") == 0 )
510
		fp_in = stdin;
511
	    else if ( (fp_in = fopen(*argv, "r")) == NULL )
512
		error(FATAL, "can't open %s", *argv);
513
	    conv();
514
	    if ( fp_in != stdin )
515
		fclose(fp_in);
516
	    argc--;
517
	    argv++;
518
	}   /* End while */
519
 
520
}   /* End of arguments */
521
 
522
/*****************************************************************************/
523
 
524
done()
525
 
526
{
527
 
528
/*
529
 *
530
 * Finished with the last input file, so mark the end of the pages, make sure the
531
 * last page is printed, and restore the initial environment.
532
 *
533
 */
534
 
535
    fprintf(stdout, "%s", TRAILER);
536
    fprintf(stdout, "done\n");
537
    fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname);
538
    fprintf(stdout, "%s %d\n", PAGES, printed);
539
 
540
}   /* End of done */
541
 
542
/*****************************************************************************/
543
 
544
account()
545
 
546
{
547
 
548
/*
549
 *
550
 * Writes an accounting record to *fp_acct, provided it's not NULL.
551
 *
552
 */
553
 
554
    if ( fp_acct != NULL )
555
	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
556
 
557
}   /* End of account */
558
 
559
/*****************************************************************************/
560
 
561
conv()
562
 
563
{
564
 
565
    int		ch;			/* next input character */
566
 
567
/*
568
 *
569
 * Controls the conversion of BGI files into PostScript. Not everything has been
570
 * implemented, but what's been done should be good enough for our purposes.
571
 *
572
 */
573
 
574
    redirect(-1);			/* get ready for the first page */
575
    bgimode = 0;
576
    formfeed();
577
 
578
    while ( (ch = get_char()) != EOF )  {
579
	switch ( ch )  {
580
		case BRCHAR:			/* rotated character mode */
581
			    bgimode = ch;
582
			    text(90);
583
			    break;
584
 
585
		case BCHAR:			/* graphical character mode */
586
			    bgimode = ch;
587
			    text(0);
588
			    break;
589
 
590
		case BGRAPH:			/* graphical master mode */
591
			    bgimode = ch;
592
			    break;
593
 
594
		case BSUB:			/* subroutine definition */
595
			    subr_def();
596
			    break;
597
 
598
		case BRET:			/* end of subroutine */
599
			    subr_end();
600
			    break;
601
 
602
		case BCALL:			/* subroutine call */
603
			    subr_call();
604
			    break;
605
 
606
		case BEND:			/* end display - page */
607
			    formfeed();
608
			    break;
609
 
610
		case BERASE:			/* erase - shouldn't be used */
611
			    error(FATAL, "BGI erase opcode obsolete");
612
			    break;
613
 
614
		case BREP:			/* repeat */
615
			    error(FATAL, "Repeat not implemented");
616
			    repeat();
617
			    break;
618
 
619
		case BSETX:			/* new x coordinate */
620
			    hgoto(get_int(0));
621
			    break;
622
 
623
		case BSETY:			/* new y coordinate */
624
			    vgoto(get_int(0));
625
			    break;
626
 
627
		case BSETXY:			/* new x and y coordinates */
628
			    hgoto(get_int(0));
629
			    vgoto(get_int(0));
630
			    break;
631
 
632
		case BINTEN:			/* mark the current point */
633
			    fprintf(fp_out, "%d %d p\n", hpos, vpos);
634
			    break;
635
 
636
		case BVISX:			/* visible x */
637
			    vector(X_COORD, VISIBLE);
638
			    break;
639
 
640
		case BINVISX:			/* invisible x */
641
			    vector(X_COORD, INVISIBLE);
642
			    break;
643
 
644
		case BVISY:			/* visible y */
645
			    vector(Y_COORD, VISIBLE);
646
			    break;
647
 
648
		case BINVISY:			/* invisible y */
649
			    vector(Y_COORD, INVISIBLE);
650
			    break;
651
 
652
		case BVEC:			/* arbitrary vector */
653
			    vector(LONGVECTOR, VISIBLE);
654
			    break;
655
 
656
		case BSVEC:			/* short vector */
657
			    vector(SHORTVECTOR, VISIBLE);
658
			    break;
659
 
660
		case BRECT:			/* draw rectangle */
661
			    rectangle(OUTLINE);
662
			    break;
663
 
664
		case BPOINT1:			/* point plot 1 */
665
		case BPOINT:			/* point plot 2 */
666
			    point_plot(ch, get_char());
667
			    break;
668
 
669
		case BLINE:			/* line plot */
670
			    line_plot();
671
			    break;
672
 
673
		case BLTY:			/* line type */
674
			    fprintf(fp_out, "%s l\n", styles[get_data()]);
675
			    break;
676
 
677
		case BARC:			/* circular arc */
678
			    arc(OUTLINE);
679
			    break;
680
 
681
		case BFARC:			/* filled circle */
682
			    arc(FILL);
683
			    break;
684
 
685
		case BFRECT:			/* filled rectangle */
686
			    rectangle(FILL);
687
			    break;
688
 
689
		case BRASRECT:			/* raster rectangle */
690
			    error(FATAL, "Raster Rectangle not implemented");
691
			    break;
692
 
693
		case BCOL:			/* select color */
694
			    set_color(get_data());
695
			    break;
696
 
697
		case BFTRAPH:			/* filled trapezoid */
698
			    trapezoid();
699
			    break;
700
 
701
		case BPAT:			/* pattern for area filling */
702
			    pattern();
703
			    break;
704
 
705
		case BCSZ:			/* change BGI character 'size' */
706
			    setsize(get_data());
707
			    break;
708
 
709
		case BNOISE:			/* from bad file format */
710
			    break;
711
 
712
		default:			/* don't recognize the code */
713
			    error(FATAL, "bad BGI command %d (0%o)", ch, ch);
714
			    break;
715
	}   /* End switch */
716
 
717
	if ( debug == ON )
718
	    fprintf(stderr, "\n");
719
    }	/* End while */
720
 
721
    formfeed();					/* in case BEND was missing */
722
 
723
}   /* End of conv */
724
 
725
/*****************************************************************************/
726
 
727
hgoto(n)
728
 
729
    int		n;			/* new horizontal position */
730
 
731
{
732
 
733
/*
734
 *
735
 * Sets the current BGI horizontal position to n.
736
 *
737
 */
738
 
739
    hpos = n;
740
 
741
}   /* End of hgoto */
742
 
743
/*****************************************************************************/
744
 
745
vgoto(n)
746
 
747
    int		n;			/* move to this vertical position */
748
 
749
{
750
 
751
/*
752
 *
753
 * Sets the absolute vertical position to n.
754
 * 
755
 */
756
 
757
    vpos = n;
758
 
759
}   /* End of vgoto */
760
 
761
/*****************************************************************************/
762
 
763
setsize(n)
764
 
765
    int		n;			/* BGI size - just a grid separation */
766
 
767
{
768
 
769
/*
770
 *
771
 * Called when we're supposed to change the BGI character size to n. The BGI
772
 * size is the grid separation in a 5 by 7 array in which characters are assumed
773
 * to be built.
774
 *
775
 */
776
 
777
    bgisize = n;
778
    linespace = LINESPACE(bgisize);
779
 
780
    fprintf(fp_out, "%d f\n", bgisize);
781
 
782
    if ( debug == ON )
783
	fprintf(stderr, "BGI size = %d\n", n);
784
 
785
}   /* End of setsize */
786
 
787
/*****************************************************************************/
788
 
789
repeat()
790
 
791
{
792
 
793
    int		count;			/* repeat this many times */
794
    int		ch;			/* next input character */
795
 
796
/*
797
 *
798
 * Haven't implemented repeats, although it wouldn't be difficult. Apparently it's
799
 * not used by any graphics packages that generate BGI.
800
 *
801
 */
802
 
803
    count = get_int();			/* get the repeat count */
804
 
805
    while ( (ch = get_char()) != EOF  &&  ch != BENDR ) ;
806
 
807
}   /* End of repeat */
808
 
809
/*****************************************************************************/
810
 
811
text(angle)
812
 
813
    int		angle;			/* either 0 or 90 degrees */
814
 
815
{
816
 
817
    int		ch;			/* next character from file *fp_in */
818
 
819
/*
820
 *
821
 * Called from conv() after we've entered one of the graphical character modes.
822
 * Characters are read from the input file and printed until the next mode change
823
 * opcode is found (or until EOF). angle will be 90 for rotated character mode
824
 * and 0 otherwise.
825
 *
826
 *
827
 */
828
 
829
    fprintf(fp_out, "%d %d %d(", angle, hpos, vpos);
830
 
831
    while ( (ch = get_char()) != EOF )  {
832
	if ( ch == BGRAPH || ch == BCHAR || ch == BRCHAR )  {
833
	    ungetc(ch, fp_in);
834
	    position--;
835
	    break;
836
	}   /* End if */
837
 
838
	switch ( ch )  {
839
	    case '\012':
840
		vgoto(vpos - linespace);
841
 
842
	    case '\015':
843
		hgoto(0);
844
		fprintf(fp_out, ")t\n%d %d %d(", angle, hpos, vpos);
845
		break;
846
 
847
	    case '(':
848
	    case ')':
849
	    case '\\':
850
		putc('\\', fp_out);
851
 
852
	    default:
853
		if ( isascii(ch) && isprint(ch) )
854
		    putc(ch, fp_out);
855
		else fprintf(fp_out, "\\%.3o", ch & 0377);
856
		break;
857
	}   /* End switch */
858
    }	/* End while */
859
 
860
    fprintf(fp_out, ") t\n");
861
 
862
}   /* End of text */
863
 
864
/*****************************************************************************/
865
 
866
formfeed()
867
 
868
{
869
 
870
    int		ch;			/* repeat count for this page */
871
 
872
/*
873
 *
874
 * Does whatever is needed to print the last page and get ready for the next one.
875
 * It's called, from conv(), after a BEND code is processed. I'm ignoring the
876
 * copy count that's expected to follow each page.
877
 *
878
 */
879
 
880
    if ( bgimode == BGRAPH && (ch = get_char()) != EOF  &&  ! (ch & MSB) )  {
881
	ungetc(ch, fp_in);
882
	position--;
883
    }	/* End if */
884
 
885
    if ( fp_out == stdout )		/* count the last page */
886
	printed++;
887
 
888
    fprintf(fp_out, "cleartomark\n");
889
    fprintf(fp_out, "showpage\n");
890
    fprintf(fp_out, "saveobj restore\n");
891
    fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
892
 
893
    while ( (ch = get_char()) == 0 ) ;	/* skip any NULL characters */
894
    ungetc(ch, fp_in);
895
    position--;
896
 
897
    if ( ungetc(getc(fp_in), fp_in) == EOF )
898
	redirect(-1);
899
    else redirect(++page);
900
 
901
    fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
902
    fprintf(fp_out, "/saveobj save def\n");
903
    fprintf(fp_out, "mark\n");
904
    writerequest(printed+1, fp_out);
905
    fprintf(fp_out, "%d pagesetup\n", printed+1);
906
 
907
    setsize(bgisize);
908
    hpos = vpos = 0;
909
 
910
}    /* End of formfeed */
911
 
912
/*****************************************************************************/
913
 
914
subr_def()
915
 
916
{
917
 
918
/*
919
 *
920
 * Starts a subroutine definition. All subroutines are defined as PostScript
921
 * procedures that begin with the character S and end with the subroutine's id
922
 * (a number between 0 and 63 - I guess). The primary, and perhaps only use of
923
 * subroutines is in special color plots produced by several graphics libraries,
924
 * and even there it's not all that common. I've also chosen not to worry about
925
 * nested subroutine definitions - that would certainly be overkill!
926
 *
927
 * All subroutines set up their own (translated) coordinate system, do their work
928
 * in that system, and restore things when they exit. To make everything work
929
 * properly we save the current point (in shpos and svpos), set our position to
930
 * (0, 0), and restore things at the end of the subroutine definition. That means
931
 * hpos and vpos measure the relative displacement after a subroutine returns, and
932
 * we save those values in the displacement[] array. The displacements are used
933
 * (in subr_call()) to properly adjust our position after each subroutine call,
934
 * and all subroutines are called with the current x and y coordinates on top of
935
 * the stack.
936
 *
937
 */
938
 
939
    if ( in_subr == TRUE )		/* a nested subroutine definition?? */
940
	error(FATAL, "can't handle nested subroutine definitions");
941
 
942
    if ( (subr_id = get_data()) == EOF )
943
	error(FATAL, "missing subroutine identifier");
944
 
945
    if ( in_global == FALSE )  {	/* just used to reduce file size some */
946
	fprintf(fp_out, "cleartomark\n");
947
	fprintf(fp_out, "saveobj restore\n");
948
	fprintf(fp_out, "%s", BEGINGLOBAL);
949
	in_global = TRUE;
950
    }	/* End if */
951
 
952
    fprintf(fp_out, "/S%d {\n", subr_id);
953
    fprintf(fp_out, "gsave translate\n");
954
 
955
    shpos = hpos;			/* save our current position */
956
    svpos = vpos;
957
 
958
    hgoto(0);				/* start at the origin */
959
    vgoto(0);
960
 
961
    in_subr = TRUE;			/* in a subroutine definition */
962
 
963
}   /* End of subr_def */
964
 
965
/*****************************************************************************/
966
 
967
subr_end()
968
 
969
{
970
 
971
    int		ch;			/* for looking at next opcode */
972
 
973
/*
974
 *
975
 * Handles stuff needed at the end of each subroutine. Want to remember the change
976
 * in horizontal and vertical positions for each subroutine so we can adjust our
977
 * position after each call - just in case. The current position was set to (0, 0)
978
 * before we started the subroutine definition, so when we get here hpos and vpos
979
 * are the relative displacements after the subroutine is called. They're saved in
980
 * the displacement[] array and used to adjust the current position when we return
981
 * from a subroutine.
982
 *
983
 */
984
 
985
    if ( in_subr == FALSE )		/* not in a subroutine definition?? */
986
	error(FATAL, "subroutine end without corresponding start");
987
 
988
    fprintf(fp_out, "grestore\n");
989
    fprintf(fp_out, "} def\n");
990
 
991
    if ( in_global == TRUE && (ch = get_char()) != BSUB )  {
992
	fprintf(fp_out, "%s", ENDGLOBAL);
993
	fprintf(fp_out, "/saveobj save def\n");
994
	fprintf(fp_out, "mark\n");
995
	in_global = FALSE;
996
    }	/* End if */
997
 
998
    ungetc(ch, fp_in);			/* put back the next opcode */
999
 
1000
    displacement[subr_id].dx = hpos;
1001
    displacement[subr_id].dy = vpos;
1002
 
1003
    hgoto(shpos);			/* back to where we started */
1004
    vgoto(svpos);
1005
 
1006
    in_subr = FALSE;			/* done with the definition */
1007
 
1008
}   /* End of subr_end */
1009
 
1010
/*****************************************************************************/
1011
 
1012
subr_call()
1013
 
1014
{
1015
 
1016
    int		ch;			/* next byte from *fp_in */
1017
    int		id;			/* subroutine id if ch wasn't an opcode */
1018
 
1019
/*
1020
 *
1021
 * Handles subroutine calls. Everything that follows the BCALL opcode (up to the
1022
 * next opcode) is taken as a subroutine identifier - thus the loop that generates
1023
 * the subroutine calls.
1024
 *
1025
 */
1026
 
1027
    while ( (ch = get_char()) != EOF && (ch & MSB) )  {
1028
	id = ch & DMASK;
1029
	fprintf(fp_out, "%d %d S%d\n", hpos, vpos, id);
1030
 
1031
	hgoto(hpos + displacement[id].dx);	/* adjust our position */
1032
	vgoto(vpos + displacement[id].dy);
1033
    }	/* End while */
1034
 
1035
    ungetc(ch, fp_in);
1036
 
1037
}   /* End of subr_call */
1038
 
1039
/*****************************************************************************/
1040
 
1041
vector(var, mode)
1042
 
1043
    int		var;			/* coordinate that varies next? */
1044
    int		mode;			/* VISIBLE or INVISIBLE vectors */
1045
 
1046
{
1047
 
1048
    int		ch;			/* next character from *fp_in */
1049
    int		x, y;			/* line drawn to this point */
1050
    int		count = 0;		/* number of points so far */
1051
 
1052
/*
1053
 *
1054
 * Handles plotting of all types of BGI vectors. If it's a manhattan vector var
1055
 * specifies which coordinate will be changed by the next number in the input
1056
 * file.
1057
 *
1058
 */
1059
 
1060
    x = hpos;				/* set up the first point */
1061
    y = vpos;
1062
 
1063
    while ( (ch = get_char()) != EOF  &&  ch & MSB )  {
1064
	if ( var == X_COORD )		/* next length is change in x */
1065
	    x += get_int(ch);
1066
	else if ( var == Y_COORD )	/* it's the change in y */
1067
	    y += get_int(ch);
1068
	else if ( var == LONGVECTOR )  {	/* long vector */
1069
	    x += get_int(ch);
1070
	    y += get_int(0);
1071
	} else {			/* must be a short vector */
1072
	    x += ((ch & MSBMAG) * ((ch & SGNB) ? -1 : 1));
1073
	    y += (((ch = get_data()) & MSBMAG) * ((ch & SGNB) ? -1 : 1));
1074
	}   /* End else */
1075
 
1076
	if ( mode == VISIBLE )  {	/* draw the line segment */
1077
	    fprintf(fp_out, "%d %d\n", hpos - x, vpos - y);
1078
	    count++;
1079
	}   /* End if */
1080
 
1081
	hgoto(x);			/* adjust the current BGI position */
1082
	vgoto(y);
1083
 
1084
	if ( var == X_COORD )		/* vertical length comes next */
1085
	    var = Y_COORD;
1086
	else if ( var == Y_COORD )	/* change horizontal next */
1087
	    var = X_COORD;
1088
    }	/* End while */
1089
 
1090
    if ( count > 0 )
1091
	fprintf(fp_out, "%d %d v\n", hpos, vpos);
1092
 
1093
    ungetc(ch, fp_in);			/* it wasn't part of the vector */
1094
    position--;
1095
 
1096
}   /* End of vector */
1097
 
1098
/*****************************************************************************/
1099
 
1100
rectangle(mode)
1101
 
1102
    int		mode;			/* FILL or OUTLINE the rectangle */
1103
 
1104
{
1105
 
1106
    int		deltax;			/* displacement for horizontal side */
1107
    int		deltay;			/* same but for vertical sides */
1108
 
1109
/*
1110
 *
1111
 * Draws a rectangle and either outlines or fills it, depending on the value of
1112
 * mode. Would be clearer, and perhaps better, if {stroke} or {fill} were put on
1113
 * the stack instead of 0 or 1. R could then define the path and just do an exec
1114
 * to fill or stroke it.
1115
 *
1116
 */
1117
 
1118
    deltax = get_int(0);		/* get the height and width */
1119
    deltay = get_int(0);
1120
 
1121
    if ( mode == OUTLINE )
1122
	fprintf(fp_out, "0 %d %d %d %d R\n", deltax, deltay, hpos, vpos);
1123
    else fprintf(fp_out, "1 %d %d %d %d R\n", deltax, deltay, hpos, vpos);
1124
 
1125
}   /* End of rectangle */
1126
 
1127
/*****************************************************************************/
1128
 
1129
trapezoid()
1130
 
1131
{
1132
 
1133
    int		kind;			/* which sides are parallel */
1134
    int		d[6];			/* true displacements - depends on kind */
1135
 
1136
/*
1137
 *
1138
 * Handles filled trapeziods. A data byte of 0101 following the opcode means the
1139
 * horizontal sides are parallel, 0102 means the vertical sides are parallel.
1140
 * Filling is handled by eofill so we don't need to get things in the right order.
1141
 *
1142
 */
1143
 
1144
    kind = get_data();
1145
 
1146
    d[0] = get_int(0);
1147
    d[1] = 0;
1148
    d[2] = get_int(0);
1149
    d[3] = get_int(0);
1150
    d[4] = get_int(0);
1151
    d[5] = 0;
1152
 
1153
    if ( kind == 2 )  {			/* parallel sides are vertical */
1154
	d[1] = d[0];
1155
	d[0] = 0;
1156
	d[5] = d[4];
1157
	d[4] = 0;
1158
    }	/* End if */
1159
 
1160
    fprintf(fp_out, "%d %d %d %d %d %d %d %d T\n", d[4], d[5], d[2], d[3], d[0], d[1], hpos, vpos);
1161
 
1162
}   /* End of trapezoid */
1163
 
1164
/*****************************************************************************/
1165
 
1166
point_plot(mode, ch)
1167
 
1168
    int		mode;			/* plotting mode BPOINT or BPOINT1 */
1169
    int		ch;			/* will be placed at the points */
1170
 
1171
{
1172
 
1173
    int		c;			/* next character from input file */
1174
    int		x, y;			/* ch gets put here next */
1175
    int		deltax;			/* x increment for BPOINT1 mode */
1176
 
1177
/*
1178
 *
1179
 * The two point plot modes are used to place a character at selected points. The
1180
 * difference in the two modes, namely BPOINT and BPOINT1, is the way we get the
1181
 * coordinates of the next point. In BPOINT1 the two bytes immediately following
1182
 * ch select a constant horizontal change, while both coordinates are given for
1183
 * all points in BPOINT mode.
1184
 *
1185
 */
1186
 
1187
    if ( mode == BPOINT1 )  {		/* first integer is change in x */
1188
	deltax = get_int(0);
1189
	x = hpos - deltax;
1190
    }	/* End if */
1191
 
1192
    while ( (c = get_char()) != EOF  &&  (c & MSB) )  {
1193
	if ( mode == BPOINT1 )  {	/* only read y coordinate */
1194
	    y = get_int(c);
1195
	    x += deltax;
1196
	} else {			/* get new x and y from input file */
1197
	    x = get_int(c);
1198
	    y = get_int(0);
1199
	}   /* End else */
1200
 
1201
	hgoto(x);			/* adjust BGI position */
1202
	vgoto(y);
1203
 
1204
	fprintf(fp_out, "%d %d\n", hpos, vpos);
1205
    }	/* End while */
1206
 
1207
    putc('(', fp_out);
1208
 
1209
    switch ( ch )  {
1210
	case '(':
1211
	case ')':
1212
	case '\\':
1213
		putc('\\', fp_out);
1214
 
1215
	default:
1216
		putc(ch, fp_out);
1217
    }	/* End switch */
1218
 
1219
    fprintf(fp_out, ")pp\n");
1220
 
1221
    ungetc(c, fp_in);			/* it wasn't part of the point plot */
1222
    position--;
1223
 
1224
}   /* End of point_plot */
1225
 
1226
/*****************************************************************************/
1227
 
1228
line_plot()
1229
 
1230
{
1231
 
1232
    int		c;			/* next input character from fp_in */
1233
    int		deltax;			/* change in x coordinate */
1234
    int		x0, y0;			/* starting point for next segment */
1235
    int		x1, y1;			/* endpoint of the line */
1236
    int		count = 0;		/* number of points so far */
1237
 
1238
/*
1239
 *
1240
 * Essentially the same format as BPOINT1, except that in this case we connect
1241
 * pairs of points by line segments.
1242
 *
1243
 */
1244
 
1245
    deltax = get_int(0);		/* again the change in x is first */
1246
 
1247
    x1 = hpos;				/* so it works first time through */
1248
    y1 = get_int(0);
1249
 
1250
    while ( (c = get_char()) != EOF  &&  (c & MSB) )  {
1251
	x0 = x1;			/* line starts here */
1252
	y0 = y1;
1253
 
1254
	x1 += deltax;			/* and ends at this point */
1255
    	y1 = get_int(c);
1256
 
1257
	fprintf(fp_out, "%d %d\n", -deltax, y0 - y1);
1258
	count++;
1259
    }	/* End while */
1260
 
1261
    hgoto(x1);				/* adjust current BGI position */
1262
    vgoto(y1);
1263
 
1264
    if ( count > 0 )
1265
	fprintf(fp_out, "%d %d v\n", hpos, vpos);
1266
 
1267
    ungetc(c, fp_in);			/* wasn't part of the line */
1268
    position--;
1269
 
1270
}   /* End of line_plot */
1271
 
1272
/*****************************************************************************/
1273
 
1274
arc(mode)
1275
 
1276
    int		mode;			/* FILL or OUTLINE the path */
1277
 
1278
{
1279
 
1280
    int		dx1, dy1;		/* displacements for first point */
1281
    int		dx2, dy2;		/* same for the second point */
1282
    int		radius;			/* of the arc */
1283
    int		angle1, angle2;		/* starting and ending angles */
1284
 
1285
/*
1286
 *
1287
 * Called whenever we need to draw an arc. I'm ignoring filled slices for now.
1288
 *
1289
 */
1290
 
1291
    dx1 = get_int(0);			/* displacements relative to center */
1292
    dy1 = get_int(0);
1293
    dx2 = get_int(0);
1294
    dy2 = get_int(0);
1295
 
1296
    radius = get_int(0);		/* and the radius */
1297
 
1298
    if ( radius == 0 )			/* nothing to do */
1299
	return;
1300
 
1301
    angle1 = (atan2((double) dy1, (double) dx1) * 360) / (2 * PI) + .5;
1302
    angle2 = (atan2((double) dy2, (double) dx2) * 360) / (2 * PI) + .5;
1303
 
1304
    fprintf(fp_out, "%d %d %d %d %d arcn stroke\n", hpos, vpos, radius, angle1, angle2);
1305
 
1306
}   /* End of arc */
1307
 
1308
/*****************************************************************************/
1309
 
1310
pattern()
1311
 
1312
{
1313
 
1314
    double	red = 0;		/* color components */
1315
    double	green = 0;
1316
    double	blue = 0;
1317
    int		kind;			/* corse or fine pattern */
1318
    int		val;			/* next color data byte */
1319
    int		i;			/* loop index */
1320
 
1321
/*
1322
 *
1323
 * Handles patterns by setting the current color based of the values assigned to
1324
 * the next four data bytes. BGI supports two kinds of patterns (fine or coarse)
1325
 * but I'm handling each in the same way - for now. In a fine pattern the four
1326
 * data bytes assign a color to four individual pixels (upperleft first) while
1327
 * in a coarse pattern the four colors are assigned to groups of four pixels,
1328
 * for a total of 16. Again the first color goes to the group in the upper left
1329
 * corner. The byte immediately following the BPAT opcode selects fine (040) or
1330
 * coarse (041) patterns. The PostScript RGB color is assigned by averaging the
1331
 * RED, GREEN, and BLUE components assigned to the four pixels (or groups of
1332
 * pixels). Acceptable results, but there's no distinction between fine and
1333
 * coarse patterns.
1334
 *
1335
 */
1336
 
1337
    if ( (kind = get_char()) == EOF )
1338
	error(FATAL, "bad pattern command");
1339
 
1340
    for ( i = 0; i < 4; i++ )  {
1341
	val = get_data();
1342
	red += get_color(val, RED);
1343
	green += get_color(val, GREEN);
1344
	blue += get_color(val, BLUE);
1345
    }	/* End for */
1346
 
1347
    fprintf(fp_out, "%g %g %g c\n", red/4, green/4, blue/4);
1348
 
1349
}   /* End of pattern */
1350
 
1351
/*****************************************************************************/
1352
 
1353
get_color(val, component)
1354
 
1355
    int		val;			/* color data byte */
1356
    int		component;		/* RED, GREEN, or BLUE component */
1357
 
1358
{
1359
 
1360
 
1361
    int		primary;		/* color mixing mode - bits 2 to 4 */
1362
    int		plane;			/* primary color plane - bits 5 to 7 */
1363
    unsigned	rgbcolor;		/* PostScript expects an RGB triple */
1364
 
1365
/*
1366
 *
1367
 * Picks the requested color component (RED, GREEN, or BLUE) from val and returns
1368
 * the result to the caller. BGI works with Cyan, Yellow, and Magenta so the one's
1369
 * complement stuff (following the exclusive or'ing) recovers the RED, BLUE, and
1370
 * GREEN components that PostScript's setrgbcolor operator needs. The PostScript
1371
 * interpreter in the ColorScript 100 has a setcmycolor operator, but it's not
1372
 * generally available so I've decided to stick with setrgbcolor.
1373
 *
1374
 */
1375
 
1376
    primary = (val >> 3) & 07;
1377
    plane = val & 07;
1378
    rgbcolor = (~(primary ^ plane)) & 07;
1379
 
1380
    if ( debug == ON )
1381
	fprintf(stderr, "val = %o, primary = %o, plane = %o, rgbcolor = %o\n",
1382
		val, primary, plane, rgbcolor);
1383
 
1384
    switch ( component )  {
1385
	case RED:
1386
		return(rgbcolor>>2);
1387
 
1388
	case GREEN:
1389
		return(rgbcolor&01);
1390
 
1391
	case BLUE:
1392
		return((rgbcolor>>1)&01);
1393
 
1394
	default:
1395
		error(FATAL, "unknown color component");
1396
		return(0);
1397
    }	/* End switch */
1398
 
1399
}   /* End of get_color */
1400
 
1401
/*****************************************************************************/
1402
 
1403
set_color(val)
1404
 
1405
    int		val;			/* color data byte */
1406
 
1407
{
1408
 
1409
/*
1410
 *
1411
 * Arranges to have the color set to the value requested in the BGI data byte val.
1412
 *
1413
 */
1414
 
1415
    fprintf(fp_out, "%d %d %d c\n", get_color(val, RED), get_color(val, GREEN), get_color(val, BLUE));
1416
 
1417
}   /* End of set_color */
1418
 
1419
/*****************************************************************************/
1420
 
1421
get_int(highbyte)
1422
 
1423
    int		highbyte;		/* already read this byte */
1424
 
1425
{
1426
 
1427
    int		lowbyte;		/* this and highbyte make the int */
1428
 
1429
/*
1430
 *
1431
 * Figures out the value on the integer (sign magnitude form) that's next in the
1432
 * input file. If highbyte is nonzero we'll use it and the next byte to build the
1433
 * integer, otherwise two bytes are read from fp_in.
1434
 *
1435
 */
1436
 
1437
 
1438
    if ( highbyte == 0 )		/* need to read the first byte */
1439
	highbyte = get_data();
1440
 
1441
    lowbyte = get_data();		/* always need the second byte */
1442
 
1443
    return(highbyte & SGNB ? -MAG(highbyte, lowbyte) : MAG(highbyte, lowbyte));
1444
 
1445
}   /* End of get_int */
1446
 
1447
/*****************************************************************************/
1448
 
1449
get_data()
1450
 
1451
{
1452
 
1453
    int		val;			/* data value returned to caller */
1454
 
1455
/*
1456
 *
1457
 * Called when we expect to find a single data character in the input file. The
1458
 * data bit is turned off and the resulting value is returned to the caller.
1459
 *
1460
 */
1461
 
1462
    if ( (val = get_char()) == EOF  ||  ! (val & MSB) )
1463
	error(FATAL, "missing data value");
1464
 
1465
    return(val & DMASK);
1466
 
1467
}   /* End of get_data */
1468
 
1469
/*****************************************************************************/
1470
 
1471
get_char()
1472
 
1473
{
1474
 
1475
    int		ch;			/* character we just read */
1476
 
1477
/*
1478
 *
1479
 * Reads the next character from file *fp_in and returns the value to the caller.
1480
 * This routine isn't really needed, but we may want to deal directly with some
1481
 * screwball file formats so I thought it would probably be a good idea to isolate
1482
 * all the input in one routine that could be easily changed.
1483
 *
1484
 */
1485
 
1486
    if ( (ch = getc(fp_in)) != EOF )  {
1487
	position++;
1488
	ch &= CHMASK;
1489
    }	/* End if */
1490
 
1491
    if ( debug == ON )
1492
	fprintf(stderr, "%o ", ch);
1493
 
1494
    return(ch);
1495
 
1496
}   /* End of get_char */
1497
 
1498
/*****************************************************************************/
1499
 
1500
redirect(pg)
1501
 
1502
    int		pg;			/* next page we're printing */
1503
 
1504
{
1505
 
1506
    static FILE	*fp_null = NULL;	/* if output is turned off */
1507
 
1508
/*
1509
 *
1510
 * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
1511
 * otherwise output goes to stdout.
1512
 *
1513
 */
1514
 
1515
    if ( pg >= 0 && in_olist(pg) == ON )
1516
	fp_out = stdout;
1517
    else if ( (fp_out = fp_null) == NULL )
1518
	fp_out = fp_null = fopen("/dev/null", "w");
1519
 
1520
}   /* End of redirect */
1521
 
1522
/*****************************************************************************/
1523