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  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 
34 /*
35  *
36  * dpost - troff post-processor for PostScript printers.
37  *
38  * A program that translates output generated by the device independent troff
39  * into PostScript. Much was borrowed from dimpress and dps (formally dlzw),
40  * and even though the code has been changed, credit has to be given to Richard
41  * Flood for his early work on the PostScript driver.
42  *
43  * Among the most interesting new features are color support (see devcntrl() and
44  * file color.c) and code to handle complex paths pieced together using any of the
45  * standard drawing commands (see devcntrl() and file draw.c). Reverse video mode
46  * has also been included as a special case of the color support. Two encoding
47  * schemes based on widthshow are also new additions. The safe one is obtained when
48  * you set encoding to 2 (eg. using the -e2 option). The slightly faster method
49  * is obtained by setting encoding to 3 (eg. using the -e3 option), although it's
50  * not recommended. Rounding errors in character widths can accumulate and become
51  * quite noticeable by the time you get to the right margin. More often than not
52  * you end up getting a ragged right margin.
53  *
54  * The program handles files formatted for any device, although the best and
55  * most efficient output is generated when the font and description files match
56  * PostScript's resident fonts. Device emulation is relatively expensive, and
57  * can produce output files that are more than twice the size of the input files.
58  * In most cases output files will be smaller than input files, perhaps by up to
59  * 40 percent, although the results you get depend on what you're doing and the
60  * text encoding you're using. You'll get the worst results if you're emulating
61  * another device, using special bitmap characters, like the logo, or doing lots
62  * of vertical motion or drawing.
63  *
64  * PostScript fonts don't support all of troff's characters, so some have to
65  * be built by special PostScript procedures. Those routines can be found in
66  * *fontdir/devpost/charlib, and are only used when we try to print a character
67  * that has been assigned a code less than 32. Definitions are only made the
68  * first time each character is used. Subsequent requests to print the character
69  * only generate a call to the PostScript procedure that's been copied to the
70  * output file. For example you'll find a file called sq in directory
71  * *fontdir/devpost/charlib. It defines a PostScript procedure called build_sq
72  * that's called whenever we need to print a square. Special characters that
73  * have been assigned a code of 2 are expected to come in two pieces. The
74  * definition part and bitmap part (or whatever). The definition is only made
75  * once, but the contents of the character's .map file are copied to the output
76  * file each time, immediately after charlib() generates the call to the
77  * PostScript procedure (build_?? ) that builds the character. That's typically
78  * how logos built from bitmaps would be handled.
79  *
80  * Several different methods can be used to encode lines of text. What's done
81  * depends on the value assigned to encoding. Print time should decrease as
82  * encoding increases (up to MAXENCODING). Setting encoding to 0, which should
83  * probably be the default, produces output essentially identical to the original
84  * version of dpost. It's the slowest but most stable method of encoding lines of
85  * text, and won't be bothered by rounding errors in the font width tables that
86  * could become noticeable by the time you get to the end of a line. Other schemes
87  * seem to work, but aren't well tested and are not guaranteed for all possible
88  * jobs. encoding can be changed on the command line using the -e option. Part of
89  * the support for different encoding schemes was to move control of all text
90  * related output to separate routines. It makes dpost work harder, but changing
91  * things is easy. For example adding stuff to support widthshow took less than
92  * an hour.
93  *
94  * According to Adobe's structuring conventions, the output produced by dpost is
95  * still nonconforming. Global definitions that are occasionally made in individual
96  * pages are the primary problem. Among other things they handle downloading host
97  * resident fonts and defining special characters not generally available on
98  * PostScript printers. The approach used here works on a demand basis and violates
99  * page independence. A definition is made once in the first page that needs it
100  * and is bracketed by PostScript code that ensures the definition is exported to
101  * the global environment where it will be available for use by all the pages that
102  * follow.  Simple changes, like downloading definitions the first time they're
103  * used in each page, restores page independence but wouldn't be an efficient
104  * solution. Other approaches are also available, but every one I've considered
105  * sacrifices much in efficiency - just to maintain page independence. I'll leave
106  * things be for now. Global definitions made in individual pages are bracketed
107  * by %%BeginGlobal and %%EndGlobal comments and can easily be pulled out of
108  * individual pages and put in the prologue by utility programs like postreverse.
109  *
110  * I've also added code that handles the DOCUMENTFONTS comment, although it's
111  * only produced for those fonts in directory /usr/lib/font/devpost that have an
112  * associated .name file. The first string in a .name file should be the (long)
113  * PostScript name (eg. Times-Roman in R.name). For now everything else in the
114  * .name file is ignored, although that may also change. You'll find .name files
115  * for all the supported fonts in the devpost source directory, although they may
116  * not be installed in /usr/lib/font/devpost.
117  *
118  * The PostScript prologue is copied from *prologue before any of the input files
119  * are translated. The program expects the following procedures are avaliable:
120  *
121  *	setup
122  *
123  *	  mark ... setup -
124  *
125  *	    Handles special initialization stuff that depends on how the program
126  *	    was called. Expects to find a mark followed by key/value pairs on the
127  *	    stack. The def operator is applied to each pair up to the mark, then
128  *	    the default state is set up. An 'x res' command must preceed the
129  *	    'x init' command!
130  *
131  *	pagesetup
132  *
133  *	  page pagesetup -
134  *
135  *	    Called at the start of each page, immediately after the page level
136  *	    save, to do special initialization on a per page basis. Right now the
137  *	    only argument is the current page number, and actually nothing of any
138  *	    importance is currently done.
139  *
140  *	setdecoding
141  *
142  *	  num setdecoding -
143  *
144  *	    Selects the text decoding procedure (ie. what's assigned to PostScript
145  *	    procedure t) from the decodingdefs array defined in the prologue. num
146  *	    should be the value assigned to variable encoding (in dpost) and will
147  *	    remain constant throughout a job, unless special features, like reverse
148  *	    video printing, are requested. The text encoding scheme can be set on
149  *	    the command line using the -e option. Print time and the size of the
150  *	    output file will usually decrease as the value assigned to encoding
151  *	    increases.
152  *
153  *	f
154  *
155  *	  size font f -
156  *
157  *	    Selects the size and font to be used for character imaging. Font names
158  *	    are defined, in *prologue, so they agree with the one or two character
159  *	    names used by troff.
160  *
161  *	m
162  *
163  *	  x y m -
164  *
165  *	    Moves to point (x, y). Normally only used when the vertical position
166  *	    changes. Horizontal positioning between words (or letters) is handled
167  *	    in procedure t (below).
168  *
169  *	t
170  *
171  *	  mark text t mark
172  *
173  *	    Processes everything on the stack, up to the mark, as a single line
174  *	    of text to be printed at a fixed vertical position. What's put out as
175  *	    text depends on the encoding scheme. Setting encoding to 0 produces
176  *	    output essentially identical to the original version of dpost. In that
177  *	    case everything on the stack, up to a mark, is interpreted (from top
178  *	    down) as an absolute horizontal position and a string to be printed at
179  *	    that point. For example the stack might look like,
180  *
181  *		mark(this)1000(is)1100(an)1200(example)1300 t
182  *
183  *	    Procedure t would go through the stack, up to the mark, adjusting the
184  *	    horizontal position before printing each string. In other encoding
185  *	    schemes, like the one based on widthshow, strings containing several
186  *	    space separated words would appear on the stack, and each one would be
187  *	    preceeded by a number that's expected to be added to the width of a
188  *	    space. For example we might have,
189  *
190  *		mark(an example)30(this is)40 2 1000 2000 t
191  *
192  *	    where (1000, 2000) is where the first string starts and 2 is the repeat
193  *	    count (ie. number of string and space pairs on the stack).
194  *
195  *	w
196  *
197  *	  string x y w -
198  *
199  *	    Prints a single word starting at position (x, y). Only used in the more
200  *	    complicated encoding schemes (eg. the ones based on widthshow).
201  *
202  *	done
203  *
204  *	    Makes sure the last page is printed. Only needed when we're printing
205  *	    more than one page on each sheet of paper.
206  *
207  * The PostScript procedures that support troff's drawing commands have been moved
208  * out of *prologue and put in a separate file (ie. DRAW as defined in path.h).
209  * The procedures are used by the routines in file draw.c, and are copied to the
210  * output file at most once and only when needed. Yet another convenient violation
211  * of page independence. If you don't approve append *drawfile to *prologue and
212  * make sure *drawfile can't be read when DPOST runs.
213  *
214  * Many default values, like the magnification and orientation, are defined in
215  * the prologue, which is where they belong. If they're changed (by options), an
216  * appropriate definition is made after the prologue is added to the output file.
217  * The -P option passes arbitrary PostScript through to the output file. Among
218  * other things it can be used to set (or change) values that can't be accessed by
219  * other options.
220  *
221  *
222  * output language from troff:
223  * all numbers are character strings
224  *
225  * sn	size in points
226  * fn	font as number from 1-n
227  * cx	ascii character x
228  * Cxyz	funny char xyz. terminated by white space
229  * Hn	go to absolute horizontal position n
230  * Vn	go to absolute vertical position n (down is positive)
231  * hn	go n units horizontally (relative)
232  * vn	ditto vertically
233  * nnc	move right nn, then print c (exactly 2 digits!)
234  * 		(this wart is an optimization that shrinks output file size
235  * 		 about 35% and run-time about 15% while preserving ascii-ness)
236  * Dt ...\n	draw operation 't':
237  * 	Dl x y		line from here by x,y
238  * 	Dc d		circle of diameter d with left side here
239  * 	De x y		ellipse of axes x,y with left side here
240  *	Da x1 y1 x2 y2	arc counter-clockwise from current point (x, y) to
241  *			(x + x1 + x2, y + y1 + y2)
242  * 	D~ x y x y ...	wiggly line by x,y then x,y ...
243  * nb a	end of line (information only -- no action needed)
244  * 	b = space before line, a = after
245  * p	new page begins -- set v to 0
246  * #...\n	comment
247  * x ...\n	device control functions:
248  * 	x i	init
249  * 	x T s	name of device is s
250  * 	x r n h v	resolution is n/inch
251  * 		h = min horizontal motion, v = min vert
252  * 	x p	pause (can restart)
253  * 	x s	stop -- done forever
254  * 	x t	generate trailer
255  * 	x f n s	font position n contains font s
256  * 	x H n	set character height to n
257  * 	x S n	set slant to N
258  *
259  * 	Subcommands like "i" are often spelled out like "init".
260  *
261  */
262 
263 
264 #include	<stdio.h>
265 #include	<fcntl.h>
266 #include	<signal.h>
267 #include	<math.h>
268 #include	<ctype.h>
269 #include	<time.h>
270 
271 #include	"comments.h"		/* PostScript file structuring comments */
272 #include	"gen.h"			/* general purpose definitions */
273 #include	"path.h"		/* for the prologue and a few other files */
274 #include	"ext.h"			/* external variable definitions */
275 #include	"dev.h"			/* typesetter and font descriptions */
276 #include	"dpost.h"		/* a few definitions just used here */
277 
278 
279 char		*prologue = DPOST;	/* the basic PostScript prologue */
280 char		*colorfile = COLOR;	/* things needed for color support */
281 char		*drawfile = DRAW;	/* and drawing */
282 char		*formfile = FORMFILE;	/* stuff for multiple pages per sheet */
283 char		*baselinefile = BASELINE;
284 
285 char		*fontdir = FONTDIR;	/* binary device directories found here */
286 char		*hostfontdir = NULL;	/* host resident font directory */
287 
288 int		formsperpage = 1;	/* page images on each piece of paper */
289 int		copies = 1;		/* and this many copies of each sheet */
290 int		picflag = ON;		/* enable/disable picture inclusion */
291 
292 
293 /*
294  *
295  * encoding selects the encoding scheme used to output lines of text. Change it
296  * to something other than 0 at your own risk. The other methods seem to work but
297  * aren't well tested and are not guaranteed. Some special features, like reverse
298  * video, may temporarily change the encoding scheme and reset it to realencoding
299  * when done.
300  *
301  */
302 
303 
304 int		encoding = DFLTENCODING;
305 int		realencoding = DFLTENCODING;
306 int		maxencoding = MAXENCODING;
307 
308 
309 /*
310  *
311  * seenfonts[] keeps track of the fonts we've used, based on internal numbers. It
312  * helps manage host resident fonts and the DOCUMENTFONTS comment, but only works
313  * if all fonts have internal numbers less than MAXINTERNAL. *docfonts counts the
314  * number of font names we've recorded in *temp_file. If it's positive routine
315  * done() adds *temp_file to the output file before quitting.
316  *
317  */
318 
319 
320 char		seenfonts[MAXINTERNAL+1];
321 int		docfonts = 0;
322 
323 
324 /*
325  *
326  * devname[] is the device troff used when the job was formatted, while *realdev
327  * is combined with *fontdir and used to locate the font and device tables that
328  * that control the translation of the input files into PostScript. *realdev can
329  * be changed using the -T option, but if you do you may end up getting garbage.
330  * The character code field must agree with PostScript's font encoding and font
331  * names must be properly mapped into PostScript font names in the prologue.
332  *
333  */
334 
335 
336 char		devname[20] = "";	/* job is formatted for this printer */
337 char		*realdev = DEVNAME;	/* a good description of target printer */
338 
339 
340 /*
341  *
342  * Standard things that come from binary font and description files for *realdev.
343  * Most are initialized in fontinit() or loadfont().
344  *
345  */
346 
347 
348 struct dev	dev;			/* DESC.out starts this way */
349 struct Font	*fontbase[NFONT+1];	/* FONT.out files begin this way */
350 short		*pstab;			/* list of available sizes */
351 int		nsizes = 1;		/* and the number of sizes in that list */
352 int		smnt;			/* index of first special font */
353 int		nchtab;			/* number of special character names */
354 int		fsize;			/* max size of a font files in bytes */
355 int		unitwidth;		/* set to dev.unitwidth */
356 char		*chname;		/* special character strings */
357 short		*chtab;			/* used to locate character names */
358 char		*fitab[NFONT+1];	/* locates char info on each font */
359 char		*widthtab[NFONT+1];	/* character width data for each font */
360 char		*codetab[NFONT+1];	/* and codes to get characters printed */
361 
362 
363 /*
364  *
365  * Special characters missing from standard PostScript fonts are defined by files
366  * in directory *fontdir/devpost/charlib. Files have the same names as the troff
367  * special character names (for now at least) and each one defines a PostScript
368  * procedure that begins with the prefix build_ and ends with the character's
369  * name.
370  *
371  * For example, the routine used to build character \(12, would be build_12.
372  * downloaded[] points to an array, allocated in fontinit(), that keeps track of
373  * the characters that have already been defined - so we only do it once.
374  *
375  */
376 
377 
378 char		*downloaded;		/* nonzero means it's been downloaded */
379 
380 
381 /*
382  *
383  * Variables that keep track of troff's requests. All are set from values in the
384  * input files. nfonts is adjusted in t_fp() as new fonts are mounted.
385  *
386  */
387 
388 
389 int		nfonts = 0;		/* number of font positions */
390 int		size = 1;		/* current size - internal value */
391 int		font = 0;		/* font position we're using now */
392 int		hpos = 0;		/* where troff wants to be - horizontally */
393 int		vpos = 0;		/* same but vertically */
394 float		lastw = 0;		/* width of the last input character */
395 int		lastc = 0;		/* and its name (or index) */
396 
397 int		fontheight = 0;		/* points from x H ... */
398 int		fontslant = 0;		/* angle from x S ... */
399 
400 int		res;			/* resolution assumed in input file */
401 float		widthfac = 1.0;		/* for emulation = res/dev.res */
402 
403 
404 /*
405  *
406  * Remember some of the same things, but this time for the printer. lastend is only
407  * used when we're doing reverse video, and is where the last character on the
408  * current line was printed.
409  *
410  */
411 
412 
413 int		lastsize = -1;		/* last internal size we used */
414 int		lastfont = -1;		/* last font we told printer about */
415 float		lastx = -1;		/* printer's current position */
416 int		lasty = -1;
417 int		lastend;		/* where last character on this line was */
418 
419 
420 /*
421  *
422  * fontname[] keeps track of the mounted fonts. Filled in (by t_fp()) from data
423  * in the binary font files.
424  *
425  */
426 
427 
428 struct  {
429 
430 	char	*name;			/* name of the font loaded here */
431 	int	number;			/* its internal number */
432 
433 } fontname[NFONT+1] = {NULL, 0};
434 
435 
436 /*
437  *
438  * All the special fonts will be mounted after the last legitimate font position.
439  * It helps when we're translating files prepared for devices, like the 202, that
440  * have a different set of special fonts. The set of special fonts needed when
441  * *realdev's tables are used may not get mounted when we're emulating another
442  * device. gotspecial keeps track of whether we've done it yet. seenpage is set
443  * to TRUE after we've seen the first page command in the input file. It controls
444  * what's done in t_font() and is needed because nfonts is no longer set when the
445  * DESC.out file is read, but rather is updated from "x font" commands in the
446  * input files.
447  *
448  */
449 
450 
451 int		gotspecial = FALSE;
452 int		seenpage = FALSE;
453 
454 
455 /*
456  *
457  * The amount of horizontal positioning error we accept controls both the size
458  * of the output file and the appearance of the printed text. It's probably most
459  * important when we're emulating other devices, like the APS-5. The error can be
460  * set using the -S option. It's converted from points to machine units in t_init()
461  * after the resolution is known. rvslop is also set in t_init() and only used to
462  * adjust the width of the box that's drawn around text when we're printing in
463  * reverse video mode.
464  *
465  */
466 
467 
468 float		pointslop = SLOP;	/* horizontal error in points */
469 int		slop;			/* and machine units */
470 int		rvslop;			/* to extend box in reverse video mode */
471 
472 
473 /*
474  *
475  * Characters are accumulated and saved in PostScript strings that are eventually
476  * processed by making a single call to procedure t. textcount counts the number
477  * of individual strings collected but not yet processed, and is primarily used to
478  * make sure PostScript's stack doesn't get too big. When textcount is positive
479  * we've started accumulating strings and need to generate a call to PostScript
480  * procedure t to process the text before anything else (like a font change) is
481  * done.
482  *
483  */
484 
485 
486 int		textcount = 0;		/* strings accumulated so far */
487 int		stringstart = 0;	/* where the next one starts */
488 int		spacecount = 0;		/* spaces seen so far on current line */
489 
490 
491 /*
492  *
493  * Things that can be used by text line encoding schemes that need to read and
494  * remember an entire line before doing any output. The strings that make up the
495  * line can be saved in array strings[] and accessed by fields in line[]. *strptr
496  * points to the next free slot in strings[].
497  *
498  */
499 
500 
501 char		strings[STRINGSPACE];
502 char		*strptr;
503 Line		line[MAXSTACK+3];
504 
505 
506 /*
507  *
508  * When we're emulating another device we may want to map font name requests that
509  * come in as "x font pos name" commands into some other font name before anything
510  * else is done (ie. calling loadfont()). Font names can collide or we may just
511  * want to a mapping that depends on the device troff used to format the input
512  * files. devfontmap points to a structure that's filled in by getdevmap() if the
513  * mapping file /usr/lib/font/dev*realdev/fontmaps/devname exists. mapdevfont()
514  * then uses that table to translate font name requests into something else before
515  * loadfont() gets called.
516  *
517  * fontmap[] provides a simple minded translation that maps an unrecognized font
518  * name (in loadfont()) into another font name that we know will be available. It
519  * doesn't provide the fine control available with *devfontmap, but should be good
520  * enough for most jobs. Both structures are only needed when emulating another
521  * device using *realdev's font tables.
522  *
523  */
524 
525 
526 Devfontmap	*devfontmap = NULL;	/* device level */
527 Fontmap		fontmap[] = FONTMAP;	/* and general mapping tables - emulation */
528 
529 
530 /*
531  *
532  * A few variables that are really only used if we're doing accounting. Designed
533  * for our use at Murray Hill and probably won't suit your needs. Changes should
534  * be easy and can be made in routine account().
535  *
536  */
537 
538 
539 int		printed = 0;		/* charge for this many pages */
540 
541 
542 /*
543  *
544  * Output and accounting file definitions. The PostScript output always goes to
545  * stdout or /dev/null, while the accounting file can be selected using the -A
546  * option.
547  *
548  */
549 
550 
551 FILE		*tf = NULL;		/* PostScript output goes here */
552 FILE		*fp_acct = NULL;	/* accounting stuff written here */
553 
554 
555 /*
556  *
557  * Need the list of valid options in header() and options(), so I've moved the
558  * definition here.
559  *
560  */
561 
562 
563 char		*optnames = "a:c:e:m:n:o:p:tw:x:y:A:C:J:F:H:L:OP:R:S:T:DI";
564 
565 
566 /*
567  *
568  * Very temporary space that can be used to do things like building up pathnames
569  * immediately before opening a file. Contents may not be preserved across calls
570  * to subroutines defined in this file, so it probably should only be used in low
571  * level subroutines like loadfont() or fontinit() and nowhere else.
572  *
573  */
574 
575 
576 char		temp[150];
577 
578 static void account(void);
579 static void addchar(int);
580 static void addoctal(int);
581 static void arguments(void);
582 static void charlib(int);
583 static void conv(FILE *);
584 static void devcntrl(FILE *);
585 static void documentfonts(void);
586 static void done(void);
587 static void endline(void);
588 static void endstring(void);
589 void endtext(void);
590 static void fontinit(void);
591 static void fontprint(int);
592 static void getdevmap(void);
593 static void header(void);
594 void hgoto(int);
595 static void hmot(int);
596 static void init_signals(void);
597 static void loaddefault(void);
598 static void loadfont(int, char *, char *);
599 static void loadspecial(void);
600 static void options(void);
601 static void oput(int);
602 static void put1(int);
603 static void put1s(char *);
604 static void redirect(int);
605 void reset(void);
606 void resetpos(void);
607 static void setfont(int);
608 static void setpaths(char *);
609 static void setsize(int);
610 static void setup(void);
611 static void starttext(void);
612 static void t_charht(int);
613 static void t_fp(int, char *, char *);
614 static void t_init(void);
615 static void t_newline(void);
616 static void t_page(int);
617 static void t_reset(int);
618 void t_sf(void);
619 static void t_slant(int);
620 static void t_trailer(void);
621 void vgoto(int);
622 static void vmot(int);
623 
624 
625 /*****************************************************************************/
626 
627 
628 int
629 main(int agc, char *agv[])
630 {
631 
632 /*
633  *
634  * A program that translates troff output into PostScript. All the input files
635  * must have been formatted for the same device, which doesn't necessarily have to
636  * be *realdev. If there's more than one input file, each begins on a new page.
637  *
638  */
639 
640 
641     argc = agc;				/* global so everyone can use them */
642     argv = agv;
643 
644     prog_name = argv[0];		/* just for error messages */
645 
646     init_signals();			/* sets up interrupt handling */
647     header();				/* PostScript file structuring comments */
648     options();				/* command line options */
649     arguments();			/* translate all the input files */
650     done();				/* add trailing comments etc. */
651     account();				/* job accounting data */
652 
653     return (x_stat);			/* everything probably went OK */
654 
655 }   /* End of main */
656 
657 
658 /*****************************************************************************/
659 
660 
661 static void
662 init_signals(void)
663 {
664     void	interrupt();		/* signal handler */
665 
666 /*
667  *
668  * Make sure we handle interrupts.
669  *
670  */
671 
672 
673     if ( signal(SIGINT, interrupt) == SIG_IGN )  {
674 	signal(SIGINT, SIG_IGN);
675 	signal(SIGQUIT, SIG_IGN);
676 	signal(SIGHUP, SIG_IGN);
677     } else {
678 	signal(SIGHUP, interrupt);
679 	signal(SIGQUIT, interrupt);
680     }   /* End else */
681 
682     signal(SIGTERM, interrupt);
683 
684 }   /* End of init_signals */
685 
686 
687 /*****************************************************************************/
688 
689 static void
690 header(void)
691 {
692 
693 
694     int		ch;			/* return value from getopt() */
695     int		old_optind = optind;	/* for restoring optind - should be 1 */
696 
697 
698 /*
699  *
700  * Scans the option list looking for things, like the prologue file, that we need
701  * right away but could be changed from the default. Doing things this way is an
702  * attempt to conform to Adobe's latest file structuring conventions. In particular
703  * they now say there should be nothing executed in the prologue, and they have
704  * added two new comments that delimit global initialization calls. Once we know
705  * where things really are we write out the job header, follow it by the prologue,
706  * and then add the ENDPROLOG and BEGINSETUP comments.
707  *
708  */
709 
710 
711     while ( (ch = getopt(argc, argv, optnames)) != EOF )
712 	if ( ch == 'L' )
713 	    setpaths(optarg);
714 	else if ( ch == '?' )
715 	    error(FATAL, "");
716 
717     optind = old_optind;		/* get ready for option scanning */
718 
719     fprintf(stdout, "%s", NONCONFORMING);
720     fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
721     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
722     fprintf(stdout, "%s %s\n", PAGES, ATEND);
723     fprintf(stdout, "%s", ENDCOMMENTS);
724 
725     if ( cat(prologue) == FALSE )
726 	error(FATAL, "can't read %s", prologue);
727 
728     fprintf(stdout, "%s", ENDPROLOG);
729     fprintf(stdout, "%s", BEGINSETUP);
730     fprintf(stdout, "mark\n");
731 
732 }   /* End of header */
733 
734 
735 /*****************************************************************************/
736 
737 
738 static void
739 options(void)
740 {
741     int		ch;			/* name returned by getopt() */
742 
743     extern char	*optarg;		/* option argument set by getopt() */
744     extern int	optind;
745 
746 /*
747  *
748  * Reads and processes the command line options. There are, without a doubt, too
749  * many options!
750  *
751  */
752 
753 
754     while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
755 
756 	switch ( ch )  {
757 
758 	    case 'a':			/* aspect ratio */
759 		    fprintf(stdout, "/aspectratio %s def\n", optarg);
760 		    break;
761 
762 	    case 'c':			/* number of copies */
763 		    copies = atoi(optarg);
764 		    fprintf(stdout, "/#copies %s store\n", optarg);
765 		    break;
766 
767 	    case 'e':			/* change the encoding scheme */
768 		    if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING )
769 			encoding = DFLTENCODING;
770 		    realencoding = encoding;
771 		    break;
772 
773 	    case 'm':			/* magnification */
774 		    fprintf(stdout, "/magnification %s def\n", optarg);
775 		    break;
776 
777 	    case 'n':			/* forms per page */
778 		    formsperpage = atoi(optarg);
779 		    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
780 		    fprintf(stdout, "/formsperpage %s def\n", optarg);
781 		    break;
782 
783 	    case 'o':			/* output page list */
784 		    out_list(optarg);
785 		    break;
786 
787 	    case 'p':			/* landscape or portrait mode */
788 		    if ( *optarg == 'l' )
789 			fprintf(stdout, "/landscape true def\n");
790 		    else fprintf(stdout, "/landscape false def\n");
791 		    break;
792 
793 	    case 't':			/* just for compatibility */
794 		    break;
795 
796 	    case 'w':			/* line width for drawing */
797 		    fprintf(stdout, "/linewidth %s def\n", optarg);
798 		    break;
799 
800 	    case 'x':			/* shift horizontally */
801 		    fprintf(stdout, "/xoffset %s def\n", optarg);
802 		    break;
803 
804 	    case 'y':			/* and vertically on the page */
805 		    fprintf(stdout, "/yoffset %s def\n", optarg);
806 		    break;
807 
808 	    case 'A':			/* force job accounting */
809 	    case 'J':
810 		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
811 			error(FATAL, "can't open accounting file %s", optarg);
812 		    break;
813 
814 	    case 'C':			/* copy file to straight to output */
815 		    if ( cat(optarg) == FALSE )
816 			error(FATAL, "can't read %s", optarg);
817 		    break;
818 
819 	    case 'F':			/* font table directory */
820 		    fontdir = optarg;
821 		    break;
822 
823 	    case 'H':			/* host resident font directory */
824 		    hostfontdir = optarg;
825 		    break;
826 
827 	    case 'L':			/* PostScript prologue file */
828 		    setpaths(optarg);	/* already been done in header() */
829 		    break;
830 
831 	    case 'O':			/* turn picture inclusion off */
832 		    picflag = OFF;
833 		    break;
834 
835 	    case 'P':			/* PostScript pass through */
836 		    fprintf(stdout, "%s\n", optarg);
837 		    break;
838 
839 	    case 'R':			/* special global or page level request */
840 		    saverequest(optarg);
841 		    break;
842 
843 	    case 'S':			/* horizontal position error */
844 		    if ( (pointslop = atof(optarg)) < 0 )
845 			pointslop = 0;
846 		    break;
847 
848 	    case 'T':			/* target printer */
849 		    realdev = optarg;
850 		    break;
851 
852 	    case 'D':			/* debug flag */
853 		    debug = ON;
854 		    tf = stdout;
855 		    break;
856 
857 	    case 'I':			/* ignore FATAL errors */
858 		    ignore = ON;
859 		    break;
860 
861 	    case '?':			/* don't know the option */
862 		    error(FATAL, "");
863 		    break;
864 
865 	    default:
866 		    error(FATAL, "missing case for option %c", ch);
867 		    break;
868 
869 	}   /* End switch */
870     }	/* End while */
871 
872     argc -= optind;			/* get ready for non-options args */
873     argv += optind;
874 
875 }   /* End of options */
876 
877 
878 /*****************************************************************************/
879 
880 
881 static void
882 setpaths(char *name)
883     /* string that followed the -L option */
884 {
885     char	*path;			/* start of the pathname */
886 
887 /*
888  *
889  * Extends the -L option to permit run time modification of pathnames that were
890  * fixed or didn't exist in previous versions of dpost. For example, the PostScript
891  * drawing procedures have been moved out of *prologue and put in *drawfile. The
892  * new syntax can be either -Lfile or -Lname:file. If the "name:" prefix is omitted
893  * file will be used as the prologue, otherwise name should be one of "prologue",
894  * "font", "draw", "color", or "form" and is used to select the pointer that gets
895  * set to string "file".
896  *
897  */
898 
899 
900     for ( path = name; *path; path++ )
901 	if ( *path == ':' || *path == ' ' )  {
902 	    while ( *path == ':' || *path == ' ' ) path++;
903 	    break;
904 	}   /* End if */
905 
906     if ( *path == '\0' )		/* didn't find a "name:" prefix */
907 	path = name;
908 
909     if ( path == name || strncmp(name, "prologue", strlen("prologue")) == 0 )
910 	prologue = path;
911     else if ( strncmp(name, "draw", strlen("draw")) == 0 )
912 	drawfile = path;
913     else if ( strncmp(name, "color", strlen("color")) == 0 )
914 	colorfile = path;
915     else if ( strncmp(name, "form", strlen("form")) == 0 )
916 	formfile = path;
917     else if ( strncmp(name, "baseline", strlen("baseline")) == 0 )
918 	baselinefile = path;
919 
920 }   /* End of setpaths */
921 
922 
923 /*****************************************************************************/
924 
925 
926 static void
927 setup(void)
928 {
929 
930 /*
931  * Handles things that must be done after the options are read but before the
932  * input files are processed. Called from t_init() after an "x init" command is
933  * read, because we need the resolution before we can generate the call to the
934  * setup procedure defined in *prologue. Only allowing one call to setup assumes
935  * all the input files have been prepared for the same device.
936  *
937  */
938 
939 
940     writerequest(0, stdout);		/* global requests eg. manual feed */
941     fprintf(stdout, "/resolution %d def\n", res);
942     fprintf(stdout, "setup\n");
943     fprintf(stdout, "%d setdecoding\n", encoding);
944 
945     if ( formsperpage > 1 )  {		/* followed by stuff for multiple pages */
946 	if ( cat(formfile) == FALSE )
947 	    error(FATAL, "can't read %s", formfile);
948 	fprintf(stdout, "%d setupforms\n", formsperpage);
949     }	/* End if */
950 
951     fprintf(stdout, "%s", ENDSETUP);
952 
953 }   /* End of setup */
954 
955 
956 /*****************************************************************************/
957 
958 
959 static void
960 arguments(void)
961 {
962     FILE	*fp;			/* next input file */
963 
964 /*
965  *
966  * Makes sure all the non-option command line arguments are processed. If we get
967  * here and there aren't any arguments left, or if '-' is one of the input files
968  * we'll translate stdin.
969  *
970  */
971 
972 
973     if ( argc < 1 )
974 	conv(stdin);
975     else
976 	while ( argc > 0 ) {
977 	    if ( strcmp(*argv, "-") == 0 )
978 		fp = stdin;
979 	    else if ( (fp = fopen(*argv, "r")) == NULL )
980 		error(FATAL, "can't open %s", *argv);
981 	    conv(fp);
982 	    if ( fp != stdin )
983 		fclose(fp);
984 	    argc--;
985 	    argv++;
986 	}   /* End while */
987 
988 }   /* End of arguments */
989 
990 
991 /*****************************************************************************/
992 
993 
994 static void
995 done(void)
996 {
997 
998 /*
999  *
1000  * Finished with all the input files, so mark the end of the pages with a TRAILER
1001  * comment, make sure the last page prints, and add things like the DOCUMENTFONTS
1002  * and PAGES comments that can only be determined after all the input files have
1003  * been read.
1004  *
1005  */
1006 
1007 
1008     fprintf(stdout, "%s", TRAILER);
1009     fprintf(stdout, "done\n");
1010 
1011     if ( temp_file != NULL )  {
1012 	if ( docfonts > 0 )  {
1013 	    cat(temp_file);
1014 	    putc('\n', stdout);
1015 	}   /* End if */
1016 	unlink(temp_file);
1017     }	/* End if */
1018 
1019     fprintf(stdout, "%s %d\n", PAGES, printed);
1020 
1021 }   /* End of done */
1022 
1023 
1024 /*****************************************************************************/
1025 
1026 
1027 static void
1028 account(void)
1029 {
1030 
1031 /*
1032  *
1033  * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
1034  * requested using the -A or -J options.
1035  *
1036  */
1037 
1038     if ( fp_acct != NULL )
1039 	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
1040 
1041 }   /* End of account */
1042 
1043 
1044 /*****************************************************************************/
1045 
1046 
1047 static void
1048 conv(FILE *fp)
1049     /* next input file */
1050 {
1051     int			c;		/* usually first char in next command */
1052     int			m, n, n1, m1;	/* when we need to read integers */
1053     char		str[50];	/* for special chars and font numbers */
1054 
1055 
1056 /*
1057  *
1058  * Controls the translation of troff's device independent output language into
1059  * PostScript. The call to t_page() that prints the last page is made when we
1060  * exit the loop, but probably belongs in t_trailer().
1061  *
1062  */
1063 
1064 
1065     redirect(-1);			/* only do output after a page command */
1066     lineno = 1;				/* line in current file */
1067 
1068     while ((c = getc(fp)) != EOF)  {
1069 
1070 	switch (c)  {
1071 
1072 	    case '\n':			/* just count this line */
1073 		    lineno++;
1074 		    break;
1075 
1076 	    case ' ':			/* when input is text */
1077 	    case 0:			/* occasional noise creeps in */
1078 		    break;
1079 
1080 	    case '0': case '1': case '2': case '3': case '4':
1081 	    case '5': case '6': case '7': case '8': case '9':
1082 		    /* two motion digits plus a character */
1083 		    hmot((c-'0')*10 + getc(fp)-'0');
1084 		    put1(getc(fp));
1085 		    break;
1086 
1087 	    case 'c':			/* single ascii character */
1088 		    put1(getc(fp));
1089 		    break;
1090 
1091 	    case 'C':			/* special character */
1092 		    fscanf(fp, "%s", str);
1093 		    put1s(str);
1094 		    break;
1095 
1096 	    case 'N':			/* character at position n */
1097 		    fscanf(fp, "%d", &m);
1098 		    endtext();
1099 		    oput(m);
1100 		    break;
1101 
1102 	    case 'D':			/* drawing functions */
1103 		    endtext();
1104 		    getdraw();
1105 		    if ( size != lastsize )
1106 			t_sf();
1107 		    switch ((c=getc(fp))) {
1108 			case 'p':	/* draw a path */
1109 			    while (fscanf(fp, "%d %d", &n, &m) == 2)
1110 				drawline(n, m);
1111 			    lineno++;
1112 			    break;
1113 
1114 			case 'l':	/* draw a line */
1115 			    fscanf(fp, "%d %d %c", &n, &m, &n1);
1116 			    drawline(n, m);
1117 			    break;
1118 
1119 			case 'c':	/* circle */
1120 			    fscanf(fp, "%d", &n);
1121 			    drawcirc(n);
1122 			    break;
1123 
1124 			case 'e':	/* ellipse */
1125 			    fscanf(fp, "%d %d", &m, &n);
1126 			    drawellip(m, n);
1127 			    break;
1128 
1129 			case 'a':	/* counter-clockwise arc */
1130 			case 'A':	/* clockwise arc */
1131 			    fscanf(fp, "%d %d %d %d", &n, &m, &n1, &m1);
1132 			    drawarc(n, m, n1, m1, c);
1133 			    break;
1134 
1135 			case 'q':	/* spline without end points */
1136 			    drawspline(fp, 1);
1137 			    lineno++;
1138 			    break;
1139 
1140 			case '~':	/* wiggly line */
1141 			    drawspline(fp, 2);
1142 			    lineno++;
1143 			    break;
1144 
1145 			default:
1146 			    error(FATAL, "unknown drawing function %c", c);
1147 			    break;
1148 		    }	/* End switch */
1149 		    break;
1150 
1151 	    case 's':			/* use this point size */
1152 		    fscanf(fp, "%d", &n);	/* ignore fractional sizes */
1153 		    setsize(t_size(n));
1154 		    break;
1155 
1156 	    case 'f':			/* use font mounted here */
1157 		    fscanf(fp, "%s", str);
1158 		    setfont(t_font(str));
1159 		    break;
1160 
1161 	    case 'H':			/* absolute horizontal motion */
1162 		    fscanf(fp, "%d", &n);
1163 		    hgoto(n);
1164 		    break;
1165 
1166 	    case 'h':			/* relative horizontal motion */
1167 		    fscanf(fp, "%d", &n);
1168 		    hmot(n);
1169 		    break;
1170 
1171 	    case 'w':			/* word space */
1172 		    break;
1173 
1174 	    case 'V':			/* absolute vertical position */
1175 		    fscanf(fp, "%d", &n);
1176 		    vgoto(n);
1177 		    break;
1178 
1179 	    case 'v':			/* relative vertical motion */
1180 		    fscanf(fp, "%d", &n);
1181 		    vmot(n);
1182 		    break;
1183 
1184 	    case 'p':			/* new page */
1185 		    fscanf(fp, "%d", &n);
1186 		    t_page(n);
1187 		    break;
1188 
1189 	    case 'n':			/* end of line */
1190 		    while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
1191 		    t_newline();
1192 		    lineno++;
1193 		    break;
1194 
1195 	    case '#':			/* comment */
1196 		    while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
1197 		    lineno++;
1198 		    break;
1199 
1200 	    case 'x':			/* device control function */
1201 		    devcntrl(fp);
1202 		    lineno++;
1203 		    break;
1204 
1205 	    default:
1206 		    error(FATAL, "unknown input character %o %c", c, c);
1207 		    done();
1208 
1209 	}   /* End switch */
1210 
1211     }	/* End while */
1212 
1213     t_page(-1);				/* print the last page */
1214     endtext();
1215 
1216 }   /* End of conv */
1217 
1218 
1219 /*****************************************************************************/
1220 
1221 
1222 static void
1223 devcntrl(FILE *fp)
1224     /* current input file */
1225 {
1226 
1227 
1228     char	str[50], buf[256], str1[50];
1229     int		c, n;
1230 
1231 
1232 /*
1233  *
1234  * Called from conv() to process the rest of a device control function. There's
1235  * a whole family of them and they all start with the string "x ", which we've
1236  * already read. The "x X ..." commands are an extensible (and device dependent)
1237  * family that we use here for things like picture inclusion. Unrecognized device
1238  * control commands are ignored.
1239  *
1240  */
1241 
1242 
1243     fscanf(fp, "%s", str);		/* get the control function name */
1244 
1245     switch ( str[0] )  {		/* only the first character counts */
1246 
1247 	case 'i':			/* initialize */
1248 		t_init();
1249 		break;
1250 
1251 	case 'T':			/* device name */
1252 		fscanf(fp, "%s", devname);
1253 		getdevmap();
1254 		strcpy(devname, realdev);
1255 		break;
1256 
1257 	case 't':			/* trailer */
1258 		t_trailer();
1259 		break;
1260 
1261 	case 'p':			/* pause -- can restart */
1262 		t_reset('p');
1263 		break;
1264 
1265 	case 's':			/* stop */
1266 		t_reset('s');
1267 		break;
1268 
1269 	case 'r':			/* resolution assumed when prepared */
1270 		fscanf(fp, "%d", &res);
1271 		break;
1272 
1273 	case 'f':			/* load font in a position */
1274 		fscanf(fp, "%d %s", &n, str);
1275 		fgets(buf, sizeof buf, fp);	/* in case there's a filename */
1276 		ungetc('\n', fp);	/* fgets() goes too far */
1277 		str1[0] = '\0';		/* in case there's nothing to come in */
1278 		sscanf(buf, "%s", str1);
1279 		loadfont(n, mapdevfont(str), str1);
1280 		break;
1281 
1282 	/* these don't belong here... */
1283 	case 'H':			/* char height */
1284 		fscanf(fp, "%d", &n);
1285 		t_charht(n);
1286 		break;
1287 
1288 	case 'S':			/* slant */
1289 		fscanf(fp, "%d", &n);
1290 		t_slant(n);
1291 		break;
1292 
1293 	case 'X':			/* copy through - from troff */
1294 		fscanf(fp, " %[^: \n]:", str);
1295 		fgets(buf, sizeof(buf), fp);
1296 		ungetc('\n', fp);
1297 		if ( strcmp(str, "PI") == 0 || strcmp(str, "PictureInclusion") == 0 )
1298 		    picture(buf);
1299 		else if ( strcmp(str, "InlinePicture") == 0 )
1300 		    inlinepic(fp, buf);
1301 		else if ( strcmp(str, "BeginPath") == 0 )
1302 		    beginpath(buf, FALSE);
1303 		else if ( strcmp(str, "DrawPath") == 0 )
1304 		    drawpath(buf, FALSE);
1305 		else if ( strcmp(str, "BeginObject") == 0 )
1306 		    beginpath(buf, TRUE);
1307 		else if ( strcmp(str, "EndObject") == 0 )
1308 		    drawpath(buf, TRUE);
1309 		else if ( strcmp(str, "NewBaseline") == 0 )
1310 		    newbaseline(buf);
1311 		else if ( strcmp(str, "DrawText") == 0 )
1312 		    drawtext(buf);
1313 		else if ( strcmp(str, "SetText") == 0 )
1314 		    settext(buf);
1315 		else if ( strcmp(str, "SetColor") == 0 )  {
1316 		    newcolor(buf);
1317 		    setcolor();
1318 		} else if ( strcmp(str, "PS") == 0 || strcmp(str, "PostScript") == 0 )  {
1319 		    endtext();
1320 		    /* xymove(hpos, vpos); ul90-22006 */
1321 		    fprintf(tf, "%s", buf);
1322 		}   /* End else */
1323 		break;
1324     }	/* End switch */
1325 
1326     while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
1327 
1328 }   /* End of devcntrl */
1329 
1330 
1331 /*****************************************************************************/
1332 
1333 
1334 static void
1335 fontinit(void)
1336 {
1337     int		fin;			/* for reading the DESC.out file */
1338     char	*filebase;		/* the whole thing goes here */
1339     int		i;			/* loop index */
1340 
1341 
1342 /*
1343  *
1344  * Reads *realdev's DESC.out file and uses what's there to initialize things like
1345  * the list of available point sizes. Old versions of the program used *devname's
1346  * DESC.out file to initialize nfonts, but that meant we needed to have *devname's
1347  * binary font files available for emulation. That restriction has been removed
1348  * and we now set nfonts using the "x font" commands in the input file, so by the
1349  * time we get here all we really need is *realdev. In fact devcntrl() reads the
1350  * device name from the "x T ..." command, but almost immediately replaces it with
1351  * string *realdev so we end up using *realdev's DESC.out file. Later on (in
1352  * t_font()) we mount all of *realdev's special fonts after the last legitimate
1353  * font position, just to be sure device emulation works reasonably well - there's
1354  * no guarantee *devname's special fonts match what's needed when *realdev's tables
1355  * are used.
1356  *
1357  */
1358 
1359 
1360     sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname);
1361     if ( (fin = open(temp, 0)) < 0 )
1362 	error(FATAL, "can't open tables for %s", temp);
1363 
1364     read(fin, &dev, sizeof(struct dev));
1365 
1366     nfonts = 0;				/* was dev.nfonts - now set in t_fp() */
1367     nsizes = dev.nsizes;
1368     nchtab = dev.nchtab;
1369     unitwidth = dev.unitwidth;
1370 
1371     if ( (filebase = malloc(dev.filesize)) == NULL )
1372 	error(FATAL, "no memory for description file");
1373 
1374     read(fin, filebase, dev.filesize);	/* all at once */
1375     close(fin);
1376 
1377     pstab = (short *) filebase;
1378     chtab = pstab + nsizes + 1;
1379     chname = (char *) (chtab + nchtab);
1380     fsize = 3 * 255 + nchtab + 128 - 32 + sizeof(struct Font);
1381 
1382     for ( i = 1; i <= NFONT; i++ )  {	/* so loadfont() knows nothing's there */
1383 	fontbase[i] = NULL;
1384 	widthtab[i] = codetab[i] = fitab[i] = NULL;
1385     }	/* End for */
1386 
1387     if ( (downloaded = (char *) calloc(nchtab + 128, sizeof(char))) == NULL )
1388 	error(FATAL, "no memory");
1389 
1390 }   /* End of fontinit */
1391 
1392 
1393 /*****************************************************************************/
1394 
1395 
1396 static void
1397 loadfont(int n, char *s, char *s1)
1398     /* n - load this font position */
1399     /* s - with the .out file for this font */
1400     /* s1 - taken from here - possibly */
1401 {
1402     int		fin;			/* for reading *s.out file */
1403     int		nw;			/* number of width table entries */
1404 
1405 
1406 /*
1407  *
1408  * Loads font position n with the binary font file for *s.out provided it's not
1409  * already there. If *s1 is NULL or points to the empty string we read files from
1410  * directory *fontdir/dev*devname, otherwise directory *s1 is used. If the first
1411  * open fails we try to map font *s into one we expect will be available, and then
1412  * we try again.
1413  *
1414  */
1415 
1416 
1417     if ( n < 0  ||  n > NFONT )		/* make sure it's a legal position */
1418 	error(FATAL, "illegal fp command %d %s", n, s);
1419 
1420     if ( fontbase[n] != NULL && strcmp(s, fontbase[n]->namefont) == 0 )
1421 	return;
1422 
1423     if ( s1 == NULL || s1[0] == '\0' )
1424 	sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, s);
1425     else sprintf(temp, "%s/%s.out", s1, s);
1426 
1427     if ( (fin = open(temp, 0)) < 0 )  {
1428 	sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, mapfont(s));
1429 	if ( (fin = open(temp, 0)) < 0 )
1430 	    error(FATAL, "can't open font table %s", temp);
1431     }	/* End if */
1432 
1433     if ( fontbase[n] != NULL )		/* something's already there */
1434 	free(fontbase[n]);		/* so release the memory first */
1435 
1436     fontbase[n] = (struct Font *) malloc(fsize);
1437     if ( fontbase[n] == NULL )
1438 	error(FATAL, "Out of space in loadfont %s", s);
1439 
1440     read(fin, fontbase[n], fsize);
1441     close(fin);
1442 
1443     if ( smnt == 0 && fontbase[n]->specfont == 1 )
1444 	smnt = n;
1445 
1446     nw = fontbase[n]->nwfont & BMASK;
1447     widthtab[n] = (char *) fontbase[n] + sizeof(struct Font);
1448     codetab[n] = (char *) widthtab[n] + 2 * nw;
1449     fitab[n] = (char *) widthtab[n] + 3 * nw;
1450 
1451     t_fp(n, fontbase[n]->namefont, fontbase[n]->intname);
1452 
1453     if ( debug == ON )
1454 	fontprint(n);
1455 
1456 }   /* End of loadfont */
1457 
1458 
1459 /*****************************************************************************/
1460 
1461 
1462 static void
1463 loadspecial(void)
1464 {
1465     char	*p;			/* for next binary font file */
1466     int		nw;			/* width entries in next font */
1467     int		i;			/* loop index */
1468 
1469 
1470 /*
1471  *
1472  * Loads all the special fonts after the last legal font position. Mostly used
1473  * for device emulation, but we'll do it no matter what. Needed because there's
1474  * no consistency in special fonts across different devices, and relying on having
1475  * them mounted in the input file doesn't guarantee the whole collection will be
1476  * there. The special fonts are determined and mounted using the copy of the
1477  * DESC.out file that's been read into memory. Initially had this stuff at the
1478  * end of fontinit(), but we now don't know nfonts until much later.
1479  *
1480  */
1481 
1482 
1483     if ( gotspecial == FALSE )
1484 	for ( i = 1, p = chname + dev.lchname; i <= dev.nfonts; i++ )  {
1485 	    nw = *p & BMASK;
1486 	    if ( ((struct Font *) p)->specfont == 1 )
1487 		loadfont(++nfonts, ((struct Font *)p)->namefont, NULL);
1488 	    p += 3 * nw + dev.nchtab + 128 - 32 + sizeof(struct Font);
1489 	}   /* End for */
1490 
1491     gotspecial = TRUE;
1492 
1493 }   /* End of loadspecial */
1494 
1495 
1496 /*****************************************************************************/
1497 char *defaultFonts[] =
1498 	{ "R", "I", "B", "BI", "CW", "H", "HB", "HX", "S1", "S", NULL };
1499 
1500 static void
1501 loaddefault(void)
1502 {
1503   int i;
1504 
1505   for (i = 0; defaultFonts[i] != NULL ; i++)
1506     loadfont(++nfonts, defaultFonts[i], NULL);
1507 }
1508 
1509 
1510 static void
1511 fontprint(int i)
1512     /* font's index in fontbase[] */
1513 {
1514     int		j, n;
1515     char	*p;
1516 
1517 
1518 /*
1519  *
1520  * Debugging routine that dumps data about the font mounted in position i.
1521  *
1522  */
1523 
1524 
1525     fprintf(tf, "font %d:\n", i);
1526 
1527     p = (char *) fontbase[i];
1528     n = fontbase[i]->nwfont & BMASK;
1529 
1530     fprintf(tf, "base=0%o, nchars=%d, spec=%d, name=%s, widtab=0%o, fitab=0%o\n",
1531 	    p, n, fontbase[i]->specfont, fontbase[i]->namefont, widthtab[i], fitab[i]);
1532 
1533     fprintf(tf, "widths:\n");
1534     for ( j = 0; j <= n; j++ )  {
1535 	fprintf(tf, " %2d", widthtab[i][j] & BMASK);
1536 	if ( j % 20 == 19 ) putc('\n', tf);
1537     }	/* End for */
1538 
1539     fprintf(tf, "\ncodetab:\n");
1540     for ( j = 0; j <= n; j++ )  {
1541 	fprintf(tf, " %2d", codetab[i][j] & BMASK);
1542 	if ( j % 20 == 19 ) putc('\n', tf);
1543     }	/* End for */
1544 
1545     fprintf(tf, "\nfitab:\n");
1546     for ( j = 0; j <= dev.nchtab + 128-32; j++ )  {
1547 	fprintf(tf, " %2d", fitab[i][j] & BMASK);
1548 	if ( j % 20 == 19 ) putc('\n', tf);
1549     }	/* End for */
1550 
1551     putc('\n', tf);
1552 
1553 }   /* End of fontprint */
1554 
1555 
1556 /*****************************************************************************/
1557 
1558 
1559 char *
1560 mapfont(char *name)
1561     /* troff wanted this font */
1562 {
1563     int		i;			/* loop index */
1564 
1565 
1566 /*
1567  *
1568  * If loadfont() can't find font *name we map it into something else that should
1569  * be available and return a pointer to the new name. Used mostly for emulating
1570  * devices like the APS-5.
1571  *
1572  */
1573 
1574 
1575     for ( i = 0; fontmap[i].name != NULL; i++ )
1576 	if ( strcmp(name, fontmap[i].name) == 0 )
1577 	    return(fontmap[i].use);
1578 
1579     switch ( *++name )  {
1580 	case 'I':
1581 		return("I");
1582 
1583 	case 'B':
1584 		return("B");
1585 
1586 	case 'X':
1587 		return("BI");
1588 
1589 	default:
1590 		return("R");
1591     }	/* End switch */
1592 
1593 }   /* End of mapfont */
1594 
1595 
1596 /*****************************************************************************/
1597 
1598 
1599 static void
1600 getdevmap(void)
1601 {
1602 
1603 
1604     FILE	*fp;			/* for reading the device fontmap file */
1605     int		i = 0;			/* number of mapping pairs we've read */
1606     int		c;			/* for skipping lines */
1607 
1608 
1609 /*
1610  *
1611  * Looks for the device font mapping file *fontdir/dev*realdev/fontmaps/devname.
1612  * The file, if it exists, should be an ASCII file containing pairs of one or two
1613  * character font names per line. The first name is the font troff will be asking
1614  * for and the second is the one we'll use. Comments are lines that begin with
1615  * a '#' as the first non-white space character on a line. The devfontmap list
1616  * ends with a member that has the empty string in the name field.
1617  *
1618  */
1619 
1620 
1621     sprintf(temp, "%s/dev%s/fontmaps/%s", fontdir, realdev, devname);
1622 
1623     if ( devfontmap == NULL && (fp = fopen(temp, "r")) != NULL )  {
1624 	devfontmap = (Devfontmap *) malloc(10 * sizeof(Devfontmap));
1625 
1626 	while ( fscanf(fp, "%s", temp) != EOF )  {
1627 	    if ( temp[0] != '#' && strlen(temp) < 3 )
1628 		if ( fscanf(fp, "%s", &temp[3]) == 1 && strlen(&temp[3]) < 3 )  {
1629 		    strcpy((devfontmap + i)->name, temp);
1630 		    strcpy((devfontmap + i)->use, &temp[3]);
1631 		    if ( ++i % 10 == 0 )
1632 			devfontmap = (Devfontmap *) realloc(devfontmap, (i + 10) * sizeof(Devfontmap));
1633 		}   /* End if */
1634 	    while ( (c = getc(fp)) != '\n' && c != EOF ) ;
1635 	}   /* End while */
1636 
1637 	(devfontmap + i)->name[0] = '\0';	/* end the list we just read */
1638 	fclose(fp);
1639     }	/* End if */
1640 
1641 }   /* End of getdevmap */
1642 
1643 
1644 /*****************************************************************************/
1645 
1646 
1647 char *
1648 mapdevfont(char *str)
1649 {
1650     int		i;
1651 
1652 
1653 /*
1654  *
1655  * Called immediately before loadfont() after an 'x font' command is recognized.
1656  * Takes the font name that troff asked for, looks it up in the devfontmap list,
1657  * and returns the mapped name to the caller. No mapping is done if the devfontmap
1658  * list is empty or font *str isn't found in the list.
1659  *
1660  */
1661 
1662 
1663     if ( devfontmap != NULL )
1664 	for ( i = 0; (devfontmap + i)->name[0] != '\0'; i++ )
1665 	    if ( strcmp((devfontmap + i)->name, str) == 0 )
1666 		return((devfontmap + i)->use);
1667 
1668     return(str);
1669 
1670 }   /* End of mapdevfont */
1671 
1672 
1673 /*****************************************************************************/
1674 
1675 
1676 void
1677 reset(void)
1678 {
1679 
1680 /*
1681  *
1682  * Resets the variables that keep track of the printer's current position, font,
1683  * and size. Typically used after a restore/save pair (eg. when we finish with a
1684  * page) to make sure we force the printer back into sync (in terms of the font
1685  * and current point) before text is printed.
1686  *
1687  */
1688 
1689 
1690     lastx = -(slop + 1);
1691     lasty = -1;
1692     lastfont = lastsize = -1;
1693 
1694 }   /* End of reset */
1695 
1696 
1697 /*****************************************************************************/
1698 
1699 
1700 void
1701 resetpos(void)
1702 {
1703 
1704 
1705 /*
1706  *
1707  * Resets the variables that keep track of the printer's current position. Used
1708  * when there's a chance we've lost track of the printer's current position or
1709  * done something that may have wiped it out, and we want to force dpost to set
1710  * the printer's position before printing text or whatever. For example stroke or
1711  * fill implicitly do a newpath, and that wipes out the current point, unless the
1712  * calls were bracketed by a gsave/grestore pair.
1713  *
1714  */
1715 
1716 
1717     lastx = -(slop + 1);
1718     lasty = -1;
1719 
1720 }   /* End of resetpos */
1721 
1722 
1723 /*****************************************************************************/
1724 
1725 
1726 static void
1727 t_init(void)
1728 {
1729     static int	initialized = FALSE;	/* only do most things once */
1730 
1731 
1732 /*
1733  *
1734  * Called from devcntrl() after an "x init" command is read. Things only work if
1735  * we've already seen the "x res" command, and much of the stuff, including the
1736  * call to setup, should only be done once. Restricting everything to one call of
1737  * setup (ie. the one in the prologue) means all the input files must have been
1738  * formatted for the same device.
1739  *
1740  */
1741 
1742 
1743     endtext();				/* moved  - for cat'ed troff files */
1744 
1745     if ( initialized == FALSE )  {	/* only do this stuff once per job */
1746 	fontinit();
1747 	gotspecial = FALSE;
1748 	widthfac = (float) res /dev.res;
1749 	slop = pointslop * res / POINTS + .5;
1750 	rvslop = res * .025;
1751 	setup();
1752 	initialized = TRUE;
1753     }	/* End if */
1754 
1755     hpos = vpos = 0;			/* upper left corner */
1756     setsize(t_size(10));		/* start somewhere */
1757     reset();				/* force position and font stuff - later */
1758 
1759 }   /* End of t_init */
1760 
1761 
1762 /*****************************************************************************/
1763 
1764 
1765 static void
1766 t_page(int pg)
1767     /* troff's current page number */
1768 {
1769     static int	lastpg = 0;		/* last one we started - for ENDPAGE */
1770 
1771 
1772 /*
1773  *
1774  * Called whenever we've finished the last page and want to get ready for the
1775  * next one. Also used at the end of each input file, so we have to be careful
1776  * about what's done. The first time through (up to the redirect(pg) call) output
1777  * goes to /dev/null because of the redirect(-1) call made in conv().
1778  *
1779  * Adobe now recommends that the showpage operator occur after the page level
1780  * restore so it can be easily redefined to have side-effects in the printer's VM.
1781  * Although it seems reasonable I haven't implemented it, because it makes other
1782  * things, like selectively setting manual feed or choosing an alternate paper
1783  * tray, clumsy - at least on a per page basis.
1784  *
1785  */
1786 
1787 
1788     if ( tf == stdout )			/* count the last page */
1789 	printed++;
1790 
1791     endtext();				/* print the last line? */
1792 
1793     fprintf(tf, "cleartomark\n");
1794     fprintf(tf, "showpage\n");
1795     fprintf(tf, "restore\n");
1796     fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed);
1797 
1798     redirect(pg);
1799 
1800     fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1);
1801     fprintf(tf, "save\n");
1802     fprintf(tf, "mark\n");
1803     writerequest(printed+1, tf);
1804     fprintf(tf, "%d pagesetup\n", printed+1);
1805     setcolor();
1806 
1807     lastpg = pg;			/* for the next ENDPAGE comment */
1808     hpos = vpos = 0;			/* get ready for the next page */
1809     reset();				/* force position and font stuff - later */
1810 
1811     seenpage = TRUE;
1812 
1813 }   /* End of t_page */
1814 
1815 
1816 /*****************************************************************************/
1817 
1818 
1819 static void
1820 t_newline(void)
1821 {
1822 
1823 
1824 /*
1825  *
1826  * Just finished the last line. All we do is set the horizontal position to 0,
1827  * although even that probably isn't necessary.
1828  *
1829  */
1830 
1831 
1832     hpos = 0;
1833 
1834 }   /* End of t_newline */
1835 
1836 
1837 /*****************************************************************************/
1838 
1839 
1840 int
1841 t_size(int n)
1842     /* convert this to an internal size */
1843 {
1844     int		i;			/* loop index */
1845 
1846 
1847 /*
1848  *
1849  * Converts a point size into an internal size that can be used as an index into
1850  * pstab[]. The internal size is one plus the index of the least upper bound of
1851  * n in pstab[], or nsizes if n is larger than all the listed sizes.
1852  *
1853  */
1854 
1855 
1856     if ( n <= pstab[0] )
1857 	return(1);
1858     else if (n >= pstab[nsizes-1])
1859 	return(nsizes);
1860 
1861     for ( i = 0; n > pstab[i]; i++ ) ;
1862 
1863     return(i+1);
1864 
1865 }   /* End of t_size */
1866 
1867 
1868 /*****************************************************************************/
1869 
1870 
1871 static void
1872 setsize(int n)
1873     /* new internal size */
1874 {
1875 
1876 
1877 /*
1878  *
1879  * Now using internal size n, where pstab[n-1] is the best available approximation
1880  * to the size troff asked for.
1881  *
1882  */
1883 
1884 
1885     size = n;
1886 
1887 }   /* End of setsize */
1888 
1889 
1890 /*****************************************************************************/
1891 
1892 
1893 static void
1894 t_fp(int n, char *s, char *si)
1895     /* n - this position */
1896     /* s - now has this font mounted */
1897     /* si - its internal number */
1898 
1899 
1900 {
1901 
1902 
1903 /*
1904  *
1905  * Updates nfonts and the array that keeps track of the mounted fonts. Called from
1906  * loadfont() after an "x font pos font" command is read, and if pos is larger than
1907  * the current value assigned to nfonts we set gotspecial to FALSE to make sure
1908  * t_font() loads all the special fonts after the last legitimate font position.
1909  *
1910  */
1911 
1912 
1913     fontname[n].name = s;
1914     fontname[n].number = atoi(si);
1915 
1916     if ( n == lastfont )		/* force a call to t_sf() */
1917 	lastfont = -1;
1918 
1919     if ( n > nfonts )  {		/* got more positions */
1920 	nfonts = n;
1921 	gotspecial = FALSE;
1922     }	/* End if */
1923 
1924 }   /* End of t_fp */
1925 
1926 
1927 /*****************************************************************************/
1928 
1929 int
1930 t_font(char *s)
1931     /* use font in this position next */
1932 {
1933     int		n;
1934 
1935 
1936 /*
1937  *
1938  * Converts the string *s into an integer and checks to make sure it's a legal
1939  * font position. Also arranges to mount all the special fonts after the last
1940  * legitimate font (by calling loadspecial()), provided it hasn't already been
1941  * done.
1942  *
1943  */
1944 
1945 
1946     n = atoi(s);
1947 
1948     if ( seenpage == TRUE )  {
1949 	if ( n < 0  ||  n > nfonts )
1950 	    error(FATAL, "illegal font position %d", n);
1951 
1952 	if ( gotspecial == FALSE )
1953 	    loadspecial();
1954     }	/* End if */
1955 
1956     return(n);
1957 
1958 }   /* End of t_font */
1959 
1960 
1961 /*****************************************************************************/
1962 
1963 
1964 static void
1965 setfont(int n)
1966     /* use the font mounted here */
1967 {
1968 
1969 
1970 /*
1971  *
1972  * troff wants to use the font that's been mounted in position n. All we do here
1973  * is update the variable that keeps track of the current position. PostScript
1974  * font changes are handled in t_sf(), and are only generated right before we're
1975  * ready to print or draw something.
1976  *
1977  */
1978 
1979 
1980     if ( n < 0 || n > NFONT )
1981 	error(FATAL, "illegal font %d", n);
1982     if ( fontname[n].name == NULL && fontname[n].number == 0)
1983 	loaddefault();
1984     if ( fontname[n].name == NULL && fontname[n].number == 0)
1985 	error(FATAL,
1986   "font %d not loaded: check 'dpost' input for 'x font %d XXX' before 'f%d'",
1987 		n, n, n);
1988 
1989     font = n;
1990 
1991 }   /* End of setfont */
1992 
1993 
1994 /*****************************************************************************/
1995 
1996 void
1997 t_sf(void)
1998 {
1999     int		fnum;			/* internal font number */
2000 
2001 
2002 /*
2003  *
2004  * Called whenever we need to use a new font or size. Only done right before we
2005  * print a character. The seenfonts[] array keeps track of the fonts we've used.
2006  * Helps manage host resident fonts and the DOCUMENTFONTS comment that's put out
2007  * at the end of the job. The array is indexed by internal number. Only works for
2008  * fonts that have internal numbers less than or equal to MAXINTERNAL.
2009  *
2010  */
2011 
2012 
2013     if ( fontname[font].name == NULL )
2014 	return;
2015 
2016     endtext();
2017 
2018     if ( (fnum = fontname[font].number) > MAXINTERNAL || fnum < 0 )
2019 	fnum = 0;
2020 
2021     if ( fnum > 0 && seenfonts[fnum] == 0 && hostfontdir != NULL )  {
2022 	sprintf(temp, "%s/%s", hostfontdir, fontname[font].name);
2023 	if ( access(temp, 04) == 0 )
2024 	    doglobal(temp);
2025     }	/* End if */
2026 
2027     if ( tf == stdout )  {
2028 	lastfont = font;
2029 	lastsize = size;
2030 	if ( seenfonts[fnum] == 0 )
2031 	    documentfonts();
2032 	seenfonts[fnum] = 1;
2033     }	/* End if */
2034 
2035     fprintf(tf, "%d %s f\n", pstab[size-1], fontname[font].name);
2036 
2037     if ( fontheight != 0 || fontslant != 0 )
2038 	fprintf(tf, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : pstab[size-1]);
2039 
2040 }   /* End of t_sf */
2041 
2042 
2043 /*****************************************************************************/
2044 
2045 
2046 static void
2047 t_charht(int n)
2048     /* use this as the character height */
2049 {
2050 
2051 /*
2052  *
2053  * Remembers the requested height, from 'x H n'. Forces a call to t_sf(), which
2054  * is where the real work is done, by setting lastfont to -1.
2055  *
2056  */
2057 
2058     fontheight = (n == pstab[size-1]) ? 0 : n;
2059     lastfont = -1;
2060 
2061 }   /* End of t_charht */
2062 
2063 
2064 /*****************************************************************************/
2065 
2066 
2067 static void
2068 t_slant(int n)
2069     /* slant characters this many degrees */
2070 {
2071 
2072 /*
2073  *
2074  * Remembers the requested slant, from 'x X n'. Forces a call to t_sf(), which
2075  * is where the real work is done, by setting lastfont to -1.
2076  *
2077  */
2078 
2079     fontslant = n;
2080     lastfont = -1;
2081 
2082 }   /* End of t_slant */
2083 
2084 
2085 /*****************************************************************************/
2086 
2087 
2088 static void
2089 t_reset(int c)
2090     /* pause or restart */
2091 {
2092 
2093 /*
2094  *
2095  * Found an "x stop" or "x pause" command. Although nothing's done here we could
2096  * add code to reset everything so dpost could handle multiple files formatted for
2097  * different devices.
2098  *
2099  */
2100 
2101 
2102 }   /* End of t_reset */
2103 
2104 
2105 /*****************************************************************************/
2106 
2107 
2108 static void
2109 t_trailer(void)
2110 {
2111 
2112 /*
2113  *
2114  * Called after we find an "x trailer" in the input file. Forcing out the last
2115  * page is done at the end of conv(), but probably belongs here.
2116  *
2117  */
2118 
2119 
2120     endtext();
2121 
2122 }   /* End of t_trailer */
2123 
2124 
2125 /*****************************************************************************/
2126 
2127 
2128 void
2129 hgoto(int n)
2130     /* new horizontal position */
2131 {
2132 
2133 
2134 /*
2135  *
2136  * Want to be at this absolute horizontal position next. Actual motion commands
2137  * are generated in oput(), charlib(), and the drawing routines.
2138  *
2139  */
2140 
2141 
2142     hpos = n;
2143 
2144 }   /* End of hgoto */
2145 
2146 
2147 /*****************************************************************************/
2148 
2149 
2150 static void
2151 hmot(int n)
2152     /* move this far horizontally */
2153 {
2154 
2155 /*
2156  *
2157  * Handles relative horizontal motion. troff's current positon, as recorded in
2158  * in hpos, is changed by n units. Usually called right before we're supposed to
2159  * print a character.
2160  *
2161  */
2162 
2163 
2164     hpos += n;
2165 
2166 }   /* End of hmot */
2167 
2168 
2169 /*****************************************************************************/
2170 
2171 
2172 void
2173 vgoto(int n)
2174     /* new vertical position */
2175 {
2176 
2177 /*
2178  *
2179  * Moves vertically in troff's coordinate system to absolute position n.
2180  *
2181  */
2182 
2183 
2184     vpos = n;
2185 
2186 }   /* End of vgoto */
2187 
2188 
2189 /*****************************************************************************/
2190 
2191 
2192 static void
2193 vmot(int n)
2194     /* move this far vertically */
2195 {
2196 
2197 /*
2198  *
2199  * Handles relative vertical motion of n units in troff's coordinate system.
2200  *
2201  */
2202 
2203 
2204     vpos += n;
2205 
2206 }   /* End of vmot */
2207 
2208 
2209 /*****************************************************************************/
2210 
2211 
2212 void
2213 xymove(int x, int y)
2214     /* this is where we want to be */
2215 {
2216 
2217 /*
2218  *
2219  * Makes sure the post-processor and printer agree about the current position.
2220  *
2221  */
2222 
2223 
2224     hgoto(x);
2225     vgoto(y);
2226 
2227     fprintf(tf, "%d %d m\n", hpos, vpos);
2228 
2229     lastx = hpos;
2230     lasty = vpos;
2231 
2232 }   /* End of xymove */
2233 
2234 
2235 /*****************************************************************************/
2236 
2237 
2238 static void
2239 put1s(char *s)
2240     /* find and print this character */
2241 {
2242     static int		i = 0;		/* last one we found - usually */
2243 
2244 /*
2245  *
2246  * *s points to the start of a two character string that represents one of troff's
2247  * special characters. To print it we first look for *s in the chname[] array using
2248  * chtab[i] to find the string representing character i in chname[]. If the lookup
2249  * is successful we add 128 to i and ask put1() to finish printing the character.
2250  * We remember the index where the last character was found because requests to
2251  * print a special character often come in bunches (eg. drawing lines with \(ru).
2252  *
2253  */
2254 
2255 
2256     if ( strcmp(s, &chname[chtab[i]]) != 0 )
2257 	for ( i = 0; i < nchtab; i++ )
2258 	    if ( strcmp(&chname[chtab[i]], s) == 0 )
2259 		break;
2260 
2261     if ( i < nchtab )
2262 	put1(i + 128);
2263     else i = 0;
2264 
2265 }   /* End of put1s */
2266 
2267 
2268 /*****************************************************************************/
2269 
2270 
2271 static void
2272 put1(int c)
2273     /* want to print this character */
2274 {
2275 
2276     int			i;		/* character code from fitab */
2277     int			j;		/* number of fonts we've checked so far */
2278     int			k;		/* font we're currently looking at */
2279     char		*pw;		/* font widthtab and */
2280     char		*p;		/* and codetab where c was found */
2281     int			code;		/* code used to get c printed */
2282     int			ofont;		/* font when we started */
2283 
2284 
2285 /*
2286  *
2287  * Arranges to have character c printed. If c < 128 it's a simple ASCII character,
2288  * otherwise it's a special character. Things done here have to agree with the way
2289  * the font tables were built by makedev, and work as follows. First we subtract
2290  * 32 from c because the tables don't record the non-graphic ASCII characters.
2291  * If fitab[k][c] isn't zero the character is on font k and the value is an index
2292  * that can be used to recover width and character code data from the other two
2293  * tables. If fitab[k][c] is zero the character isn't defined on font k and we
2294  * check the next font, which is found as follows. The current font is the first
2295  * one we check, and it's followed by a circular search of all the remaining fonts
2296  * that starts with the first special font and skips font position 0. If character
2297  * c is found somewhere besides the current font we change to that font and use
2298  * fitab[k][c] to locate missing data in the other two tables. The width of the
2299  * character can be found at widthtab[k][c] while codetab[k][c] is whatever we
2300  * need to tell the printer to have character c printed. lastc records the real
2301  * name of the character because it's lost by the time oput() gets called but
2302  * charlib() may need it.
2303  *
2304  * Took all the debugging stuff out because at least this part of the program is
2305  * reasonably solid.
2306  *
2307  */
2308 
2309 
2310     lastc = c;				/* charlib() needs the name not the code */
2311     if ( (c -= 32) <= 0 )		/* probably never happens */
2312 	return;
2313 
2314     k = ofont = font;
2315 
2316     if ( (i = fitab[k][c] & BMASK) != 0 )  {	/* it's on this font */
2317 	p = codetab[font];
2318 	pw = widthtab[font];
2319     } else if ( smnt > 0 )  {		/* on special (we hope) */
2320 	for ( k=smnt, j=0; j <= nfonts; j++, k = (k+1) % (nfonts+1) )  {
2321 	    if ( k == 0 )  continue;
2322 	    if ( (i = fitab[k][c] & BMASK) != 0 )  {
2323 		p = codetab[k];
2324 		pw = widthtab[k];
2325 		setfont(k);
2326 		break;
2327 	    }	/* End if */
2328 	}   /* End for */
2329     }	/* End else */
2330 
2331     if ( i != 0 && (code = p[i] & BMASK) != 0 )  {
2332 	lastw = widthfac * (((pw[i] & BMASK) * pstab[size-1] + unitwidth/2) / unitwidth);
2333 	oput(code);
2334     }	/* End if */
2335 
2336     if ( font != ofont )
2337 	setfont(ofont);
2338 
2339 }   /* End of put1 */
2340 
2341 
2342 /*****************************************************************************/
2343 
2344 
2345 static void
2346 oput(int c)
2347     /* want to print this character */
2348 {
2349 
2350 /*
2351  *
2352  * Arranges to print the character whose code is c in the current font. All the
2353  * actual positioning is done here, in charlib(), or in the drawing routines.
2354  *
2355  */
2356 
2357 
2358     if ( textcount > MAXSTACK )		/* don't put too much on the stack? */
2359 	endtext();
2360 
2361     if ( font != lastfont || size != lastsize )
2362 	t_sf();
2363 
2364     if ( vpos != lasty )
2365 	endline();
2366 
2367     starttext();
2368 
2369     if ( ABS(hpos - lastx) > slop )
2370 	endstring();
2371 
2372     if ( isascii(c) && isprint(c) )
2373 	switch ( c )  {
2374 	    case '(':
2375 	    case ')':
2376 	    case '\\':
2377 		    addchar('\\');
2378 
2379 	    default:
2380 		    addchar(c);
2381 	}   /* End switch */
2382     else if ( c > 040 )
2383 	addoctal(c);
2384     else charlib(c);
2385 
2386     lastx += lastw;
2387 
2388 }   /* End of oput */
2389 
2390 
2391 /*****************************************************************************/
2392 
2393 
2394 static void
2395 starttext(void)
2396 {
2397 
2398 /*
2399  * Called whenever we want to be sure we're ready to start collecting characters
2400  * for the next call to PostScript procedure t (ie. the one that prints them). If
2401  * textcount is positive we've already started, so there's nothing to do. The more
2402  * complicated encoding schemes save text strings in the strings[] array and need
2403  * detailed information about the strings when they're written to the output file
2404  * in endtext().
2405  *
2406  */
2407 
2408 
2409     if ( textcount < 1 )  {
2410 	switch ( encoding )  {
2411 	    case 0:
2412 	    case 1:
2413 		putc('(', tf);
2414 		break;
2415 
2416 	    case 2:
2417 	    case 3:
2418 		strptr = strings;
2419 		spacecount = 0;
2420 		line[1].str = strptr;
2421 		line[1].dx = 0;
2422 		line[1].spaces = 0;
2423 		line[1].start = hpos;
2424 		line[1].width = 0;
2425 		break;
2426 
2427 	    case MAXENCODING+1:			/* reverse video */
2428 		if ( lastend == -1 )
2429 		    lastend = hpos;
2430 		putc('(', tf);
2431 		break;
2432 
2433 	    case MAXENCODING+2:			/* follow a funny baseline */
2434 		putc('(', tf);
2435 		break;
2436 	}   /* End switch */
2437 	textcount = 1;
2438 	lastx = stringstart = hpos;
2439     }	/* End if */
2440 
2441 }   /* End of starttext */
2442 
2443 
2444 /*****************************************************************************/
2445 
2446 
2447 void
2448 endtext(void)
2449 {
2450 
2451     int		i;			/* loop index */
2452 
2453 
2454 /*
2455  *
2456  * Generates a call to the PostScript procedure that processes all the text we've
2457  * accumulated - provided textcount is positive.
2458  *
2459  */
2460 
2461     if ( textcount > 0 )  {		/* started working on some text */
2462 	switch ( encoding )  {
2463 	    case 0:
2464 		fprintf(tf, ")%d t\n", stringstart);
2465 		break;
2466 
2467 	    case 1:
2468 		fprintf(tf, ")%d %d t\n", stringstart, lasty);
2469 		break;
2470 
2471 	    case 2:
2472 		*strptr = '\0';
2473 		line[textcount].width = lastx - line[textcount].start;
2474 		if ( spacecount != 0 || textcount != 1 )  {
2475 		    for ( i = textcount; i > 0; i-- )
2476 			fprintf(tf, "(%s)%d %d", line[i].str, line[i].spaces, line[i].width);
2477 		    fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty);
2478 		} else fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty);
2479 		break;
2480 
2481 	    case 3:
2482 		*strptr = '\0';
2483 		if ( spacecount != 0 || textcount != 1 )  {
2484 		    for ( i = textcount; i > 0; i-- )
2485 			fprintf(tf, "(%s)%d", line[i].str, line[i].dx);
2486 		    fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty);
2487 		} else fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty);
2488 		break;
2489 
2490 	    case MAXENCODING+1:
2491 		fprintf(tf, ")%d ", stringstart);
2492 		fprintf(tf, "%d %d drawrvbox ", lastend - rvslop, (int)(lastx + .5) + rvslop);
2493 		fprintf(tf, "t\n", stringstart);
2494 		lastend = (lastx + .5) + 2 * rvslop;
2495 		break;
2496 
2497 	    case MAXENCODING+2:
2498 		fprintf(tf, ")%d %d t\n", stringstart, lasty);
2499 		break;
2500 	}   /* End switch */
2501     }	/* End if */
2502 
2503     textcount = 0;
2504 
2505 }   /* End of endtext */
2506 
2507 
2508 /*****************************************************************************/
2509 
2510 
2511 static void
2512 endstring(void)
2513 {
2514     int		dx;
2515 
2516 /*
2517  *
2518  * Horizontal positions are out of sync. End the last open string, adjust the
2519  * printer's position, and start a new string. Assumes we've already started
2520  * accumulating text.
2521  *
2522  */
2523 
2524 
2525     switch ( encoding )  {
2526 	case 0:
2527 	case 1:
2528 	    fprintf(tf, ")%d(", stringstart);
2529 	    textcount++;
2530 	    lastx = stringstart = hpos;
2531 	    break;
2532 
2533 	case 2:
2534 	case 3:
2535 	    dx = hpos - lastx;
2536 	    if ( spacecount++ == 0 )
2537 		line[textcount].dx = dx;
2538 	    if ( line[textcount].dx != dx )  {
2539 		*strptr++ = '\0';
2540 		line[textcount].width = lastx - line[textcount].start;
2541 		line[++textcount].str = strptr;
2542 		*strptr++ = ' ';
2543 		line[textcount].dx = dx;
2544 		line[textcount].start = lastx;
2545 		line[textcount].width = 0;
2546 		line[textcount].spaces = 1;
2547 	    } else {
2548 		*strptr++ = ' ';
2549 		line[textcount].spaces++;
2550 	    }	/* End else */
2551 	    lastx += dx;
2552 	    break;
2553 
2554 	case MAXENCODING+1:
2555 	    fprintf(tf, ")%d(", stringstart);
2556 	    textcount++;
2557 	    lastx = stringstart = hpos;
2558 	    break;
2559 
2560 	case MAXENCODING+2:
2561 	    endtext();
2562 	    starttext();
2563 	    break;
2564 
2565     }	/* End switch */
2566 
2567 }   /* End of endstring */
2568 
2569 
2570 /*****************************************************************************/
2571 
2572 
2573 static void
2574 endline(void)
2575 {
2576 
2577 /*
2578  *
2579  * The vertical position has changed. Dump any accumulated text, then adjust
2580  * the printer's vertical position.
2581  *
2582  */
2583 
2584 
2585     endtext();
2586 
2587     if ( encoding == 0 || encoding == MAXENCODING+1 )
2588 	fprintf(tf, "%d %d m\n", hpos, vpos);
2589 
2590     lastx = stringstart = lastend = hpos;
2591     lasty = vpos;
2592 
2593 }   /* End of endline */
2594 
2595 
2596 /*****************************************************************************/
2597 
2598 
2599 static void
2600 addchar(int c)
2601     /* next character in current string */
2602 {
2603 
2604 /*
2605  *
2606  * Does whatever is needed to add character c to the current string.
2607  *
2608  */
2609 
2610 
2611     switch ( encoding )  {
2612 	case 0:
2613 	case 1:
2614 	    putc(c, tf);
2615 	    break;
2616 
2617 	case 2:
2618 	case 3:
2619 	    *strptr++ = c;
2620 	    break;
2621 
2622 	case MAXENCODING+1:
2623 	case MAXENCODING+2:
2624 	    putc(c, tf);
2625 	    break;
2626     }	/* End switch */
2627 
2628 }   /* End of addchar */
2629 
2630 
2631 /*****************************************************************************/
2632 
2633 
2634 static void
2635 addoctal(int c)
2636     /* add it as an octal escape */
2637 {
2638 
2639 
2640 /*
2641  *
2642  * Adds c to the current string as an octal escape \ddd.
2643  *
2644  */
2645 
2646 
2647     switch ( encoding )  {
2648 	case 0:
2649 	case 1:
2650 	    fprintf(tf, "\\%o", c);
2651 	    break;
2652 
2653 	case 2:
2654 	case 3:
2655 	    sprintf(strptr, "\\%o", c);
2656 	    strptr += strlen(strptr);
2657 	    break;
2658 
2659 	case MAXENCODING+1:
2660 	case MAXENCODING+2:
2661 	    fprintf(tf, "\\%o", c);
2662 	    break;
2663     }	/* End switch */
2664 
2665 }   /* End of addoctal */
2666 
2667 
2668 /*****************************************************************************/
2669 
2670 
2671 static void
2672 charlib(int code)
2673     /* either 1 or 2 */
2674 {
2675     char	*name;			/* name of the character */
2676     char	tname[10];		/* in case it's a single ASCII character */
2677 
2678 
2679 /*
2680  *
2681  * Called from oput() for characters having codes less than 040. Special files
2682  * that define PostScript procedures for certain characters can be found in
2683  * directory *fontdir/devpost/charlib. If there's a file that has the same name as
2684  * the character we're trying to print it's copied to the output file, otherwise
2685  * nothing, except some positioning, is done.
2686  *
2687  * All character definitions are only made once. Subsequent requests to print the
2688  * character generate a call to a procedure that begins with the prefix build_ and
2689  * ends with the character's name. Special characters that are assigned codes
2690  * other than 1 are assumed to have additional data files that should be copied
2691  * to the output file immediately after the build_ call. Those data files should
2692  * end in the suffix .map, and usually will be a hex representation of a bitmap.
2693  *
2694  */
2695 
2696 
2697     endtext();
2698 
2699     if ( lastc < 128 )  {		/* just a simple ASCII character */
2700 	sprintf(tname, "%.3o", lastc);
2701 	name = tname;
2702     } else name = &chname[chtab[lastc - 128]];
2703 
2704     if ( downloaded[lastc] == 0 )  {
2705 	sprintf(temp, "%s/dev%s/charlib/%s", fontdir, realdev, name);
2706 	if ( access(temp, 04) == 0 && doglobal(temp) == TRUE )  {
2707 	    downloaded[lastc] = 1;
2708 	    t_sf();
2709 	}   /* End if */
2710     }	/* End if */
2711 
2712     if ( downloaded[lastc] == 1 )  {
2713 	xymove(hpos, vpos);
2714 	fprintf(tf, "%d build_%s\n", (int) lastw, name);
2715 	if ( code != 1 )  {		/* get the bitmap or whatever */
2716 	    sprintf(temp, "%s/dev%s/charlib/%s.map", fontdir, realdev, name);
2717 	    if ( access(temp, 04) == 0 && tf == stdout )
2718 		cat(temp);
2719 	}   /* End if */
2720 	fprintf(tf, "%d %d m\n", stringstart = hpos + lastw, vpos);
2721     }	/* End if */
2722 
2723 }   /* End of charlib */
2724 
2725 
2726 /*****************************************************************************/
2727 
2728 
2729 int
2730 doglobal(char *name)
2731     /* copy this to the output - globally */
2732 {
2733     int		val = FALSE;		/* returned to the caller */
2734 
2735 
2736 /*
2737  *
2738  * Copies file *name to the output file and brackets it with whatever commands are
2739  * needed to have it exported to the global environment. TRUE is returned if we
2740  * successfully add file *name to the output file.
2741  *
2742  */
2743 
2744 
2745     if ( tf == stdout )  {
2746 	endtext();
2747 	fprintf(tf, "cleartomark restore\n");
2748 	fprintf(tf, "%s", BEGINGLOBAL);
2749 	val = cat(name);
2750 	fprintf(tf, "%s", ENDGLOBAL);
2751 	fprintf(tf, "save mark\n");
2752 	reset();
2753     }	/* End if */
2754 
2755     return(val);
2756 
2757 }   /* End of doglobal */
2758 
2759 
2760 /*****************************************************************************/
2761 
2762 
2763 static void
2764 documentfonts(void)
2765 {
2766     FILE	*fp_in;			/* PostScript font name read from here */
2767     FILE	*fp_out;		/* and added to this file */
2768 
2769 
2770 /*
2771  *
2772  * Whenever a new font is used we try to record the appropriate PostScript font
2773  * name in *temp_file for the DOCUMENTFONTS comment that's put out in done().
2774  * By default PostScript font names are found in /usr/lib/font/devpost. Fonts
2775  * that have a .name file are recorded in *temp_file. The first string in that
2776  * file is expected to be that font's (long) PostScript name.
2777  *
2778  */
2779 
2780 
2781     if ( temp_file == NULL )		/* generate a temp file name */
2782 	if ( (temp_file = tempnam(TEMPDIR, "dpost")) == NULL )
2783 	    return;
2784 
2785     sprintf(temp, "%s/dev%s/%s.name", fontdir, realdev, fontname[font].name);
2786 
2787     if ( (fp_in = fopen(temp, "r")) != NULL )  {
2788 	if ( (fp_out = fopen(temp_file, "a")) != NULL )  {
2789 	    if ( fscanf(fp_in, "%s", temp) == 1 )  {
2790 		if ( docfonts++ == 0 )
2791 		    fprintf(fp_out, "%s", DOCUMENTFONTS);
2792 		else if ( (docfonts - 1) % 8  == 0 )
2793 		    fprintf(fp_out, "\n%s", CONTINUECOMMENT);
2794 		fprintf(fp_out, " %s", temp);
2795 	    }	/* End if */
2796 	    fclose(fp_out);
2797 	}   /* End if */
2798 	fclose(fp_in);
2799     }	/* End if */
2800 
2801 }   /* End of documentfonts */
2802 
2803 
2804 /*****************************************************************************/
2805 
2806 
2807 static void
2808 redirect(int pg)
2809     /* next page we're printing */
2810 {
2811     static FILE	*fp_null = NULL;	/* if output is turned off */
2812 
2813 
2814 /*
2815  *
2816  * If we're not supposed to print page pg, tf will be directed to /dev/null,
2817  * otherwise output goes to stdout.
2818  *
2819  */
2820 
2821 
2822     if ( pg >= 0 && in_olist(pg) == ON )
2823 	tf = stdout;
2824     else if ( (tf = fp_null) == NULL )
2825 	tf = fp_null = fopen("/dev/null", "w");
2826 
2827 }   /* End of redirect */
2828