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