xref: /original-bsd/local/transcript/src/psdit.c (revision d0e3910b)
1 /*	@(#)psdit.c	1.4 04/17/88	*/
2 #ifndef lint
3 static char Notice[] = "Copyright (c) 1984, 1985 Adobe Systems Incorporated";
4 static char *RCSID = "$Header: psdit.c,v 2.1 85/11/24 11:50:41 shore Rel $";
5 #endif
6 # define XMOD
7 /*
8  * Changed by Edward Wang (edward@ucbarpa.berkeley.edu) to handle graphics,
9  * 17 Feb, 87, with code provided by John Coker (john@renoir.berkeley.edu)
10  * and Peehong Chen (phc@renoir.berkeley.edu).
11  */
12 /* psdit.c
13  *
14  * Copyright (c) 1984, 1985 Adobe Systems Incorporated
15  *
16  * ditroff intermediate file to PostScript translator
17  *
18  * Original Version: Barry Hayes spring/summer 1984
19  * Edit History:
20  * Andrew Shore: Sat Nov 23 20:05:26 1985
21  * End Edit History.
22  *
23  * RCSLOG:
24  * $Log:	psdit.c,v $
25  * Revision 2.1  85/11/24  11:50:41  shore
26  * Product Release 2.0
27  *
28  * Revision 1.8  85/11/23  20:09:44  shore
29  * test for termination of included PostScript was bad
30  *
31  * Revision 1.7  85/11/21  14:23:56  shore
32  * added envget check for PSLIBDIR
33  *
34  * Revision 1.6  85/11/20  00:43:43  shore
35  * support for included PostScript
36  * big rework on FlushShow, word "breaks"
37  * removed FlushFont and made them instant
38  * Still no Gremlin support yet
39  *
40  * Revision 1.5  85/10/03  10:48:09  shore
41  * added FlushShow to xf fix !
42  *
43  * Revision 1.4  85/10/02  16:20:32  shore
44  * fixed xf bug
45  * mounting a font causes a font switch!
46  *
47  * Revision 1.3  85/07/09  13:10:20  shore
48  * added fclose on map file
49  *
50  * Revision 1.2  85/05/14  11:24:23  shore
51  * added flush to trailer
52  * fixed read bug when mounting fonts
53  */
54 
55 /*
56 output language from troff:
57 all numbers are character strings
58 
59 sn	size in points
60 fn	font as number from 1-n
61 cx	ascii character x
62 Cxyz	funny char xyz. terminated by white space
63 Hn	go to absolute horizontal position n
64 Vn	go to absolute vertical position n (down is positive)
65 hn	go n units horizontally (relative)
66 vn	ditto vertically
67 nnc	move right nn, then print c (exactly 2 digits!)
68 		(this wart is an optimization that shrinks output file size
69 		 about 35% and run-time about 15% while preserving ascii-ness)
70 Dt ...\n	draw operation 't':
71 	Dl x y		line from here by x,y
72 	Dc d		circle of diameter d with left side here
73 	De x y		ellipse of axes x,y with left side here
74 	Da x y r	arc counter-clockwise by x,y of radius r
75 	Dp ...		bordered polygon
76 	DP ...		unbordered polygon
77 	D~ x y x y ...	b-spline by x,y then x,y ...
78 	Dg ...		gremlin curve
79 	Dz ...		bezier curve
80 nb a	end of line (information only -- no action needed)
81 	a = space before line, a = after
82 w	paddable word space -- no action needed
83 pn	new page begins -- set v to 0
84 {	push current environment (font info & location)
85 }	pop a saved environment
86 txxxx	print string xxxx using natural widths
87 #...\n	comment
88 x ...\n	device control functions:
89 	x i[nit]	init
90 	x T s		name of device is s
91 	x r[es] n h v	resolution is n/inch
92 			h = min horizontal motion, v = min vert
93 	x p[ause]	pause (can restart)
94 	x s[top]	stop -- done for ever
95 	x t[railer]	generate trailer
96 	x f[font] n s	font position n contains font s
97 	x H[eight] n	set character height to n
98 	x S[slant] n	set slant to N
99 
100 Adobe Extension for included PostScript:
101 %
102 (raw postscript...)
103 .\n
104 
105 */
106 
107 #include <stdio.h>
108 #include <ctype.h>
109 #include <signal.h>
110 #include <pwd.h>
111 #ifdef SYSV
112 extern struct passwd *getpwuid();
113 #endif
114 #include "transcript.h"
115 
116 #include "dev.h"
117 
118 char *malloc();
119 
120 #define	NFONT	10
121 
122 /* DIT state consists of: */
123 int	hpos;		/* current horizontal position */
124 int	vpos;		/* current vertical position */
125 int	fontsize;	/* current font size */
126 int	fontheight;	/* current character height */
127 int	fontslant;	/* current font slant */
128 int	font;		/* current font */
129 int	resolution;	/* device resolution */
130 int	minhoriz;	/* minimum horizontal motion */
131 int	minvert;	/* minimum vertical motion */
132 
133 int	onspecial;
134 int	specfont;
135 int	prevfont;
136 int 	pfont;
137 
138 /* {} push/pop stack */
139 #define DSTACK 10
140 struct ditstack {
141 	int hpos, vpos, fontsize, fontheight, fontslant, font;
142 } ditstack[DSTACK];
143 int dlevel = 0;
144 
145 #define ErrorTolerance 48
146 #define PSWID 0x00000FFF
147 #define ISPSPROC 0x000FF000
148 
149 /* PSscale is equivalent to (x * PSmag / 72000) + 0.5 */
150 #define PSmag 16
151 #define PSscale(x) (((x) + 2250) / 4500)
152 
153 /* we maintain PS coords with PSmag times the precision */
154 /* current PS state is: */
155 
156 int	PSx;		/* current horizontal position */
157 int	PSy;		/* current vertical position */
158 int	savex, savey;	/* position of start of current show string */
159 
160 /* ps move types -- note that XMOVE|YMOVE == XYMOVE ! */
161 #define NONE	0
162 #define XMOVE	1
163 #define YMOVE	2
164 #define XYMOVE	3
165 
166 int movepending = NONE;
167 
168 /* buffer string for show -- save up adjacent chars */
169 #define SHOWSIZE 400
170 char showbuf[SHOWSIZE + 3]; /* extras are for quoting */
171 int showind = 0;	/* index into string of next available byte */
172 int PSshowlen = 0;	/* size in big units of buffered string */
173 int nshow = 0;		/* actual number of show chars in showbuf */
174 int startx;		/* troff starting pos of current string */
175 int thisw;
176 
177 /* #define NONE 0 */
178 #define HMOT	1
179 #define VMOT	2
180 #define CPUT	4
181 #define BRK	8
182 #define FNT	16
183 int lastcmd;
184 
185 int	output = 0;	/* do we do output at all? */
186 int	nolist = 0;	/* output page list if > 0 */
187 int	olist[20];	/* pairs of page numbers */
188 int	spage = 9999;	/* stop every spage pages */
189 int	scount = 0;
190 int	stopped = 0;
191 int	pageno = 0;
192 int	firstpage = TRUE;
193 
194 struct dev dev;
195 struct font *fontbase[NFONT+1];
196 short	*pstab;
197 int	dres;	/* resolution from DESC */
198 int	nsizes; /* number of point sizes from DESC */
199 int	nfonts; /* number of fonts from DESC */
200 int	smnt;	/* index of first special font */
201 int	nchtab;
202 char	*chname;
203 short	*chtab;
204 char	*fitab[NFONT+1];
205 char	*widthtab[NFONT+1];	/* widtab would be a better name */
206 char	*codetab[NFONT+1];	/* device codes */
207 
208 int	*pswidths[NFONT+1]; /* ps width tables */
209 int	fontdelta[NFONT+1]; /* nonzero if xf overwrites font i */
210 
211 /* font position info: */
212 struct {
213 	char *name;
214 	int number;
215 } fontname[NFONT+1];
216 
217 #define	FATAL	1
218 #define	BMASK	0377
219 
220 #ifdef DEBUG
221 int	dbg = 0;
222 int	fdbg = 0;
223 #define debugp(xxx) (dbg != 0 ? (dbg--, printf xxx, fflush(stdout)) : 0)
224 #else
225 #define debugp(x)
226 #endif
227 
228 char	devname[20] = "psc";
229 
230 char	*infilename = "stdin"; /* input file name */
231 char	*prologfile = PSDITPRO;
232 char	*ditdir = DitDir;
233 
234 char	*prog;		/* argv[0] - program name */
235 
236 	/* for curve and polygon drawing */
237 #define MAXPOINTS	200
238 double x[MAXPOINTS], y[MAXPOINTS];
239 int numpoints;
240 
241 main(argc, argv)
242 	int argc;
243 	char *argv[];
244 {
245 	FILE *fp;
246 	int done();
247 
248 	prog = argv[0];
249 	while (argc > 1 && argv[1][0] == '-') {
250 		switch (argv[1][1]) {
251 		case 'f':
252 		case 'F':
253 			if (argv[1][2])
254 				ditdir = &argv[1][2];
255 			else {
256 				ditdir = argv[2];
257 				argv++;
258 				argc--;
259 			}
260 			break;
261 		case 'p':
262 			if (argv[1][2])
263 				prologfile = &argv[1][2];
264 			break;
265 		case 'o':
266 			outlist(&argv[1][2]);
267 			break;
268 		case 'd':
269 #ifdef DEBUG
270 			dbg = atoi(&argv[1][2]);
271 			if (dbg == 0)
272 				dbg = 1;
273 #endif DEBUG
274 			break;
275 		case 'b': 		/* ignore busy */
276 			break;
277 		case 'w': 		/* ignore wait */
278 			break;
279 		case 's':
280 			spage = atoi(&argv[1][2]);
281 			if (spage <= 0)
282 				spage = 9999;
283 			break;
284 		}
285 		argc--;
286 		argv++;
287 	}
288 
289 	if (signal(SIGINT, done) == SIG_IGN) {
290 		signal(SIGINT, SIG_IGN);
291 		signal(SIGQUIT, SIG_IGN);
292 		signal(SIGHUP, SIG_IGN);
293 	} else {
294 		signal(SIGQUIT, done);
295 		signal(SIGHUP, done);
296 	}
297 	signal(SIGTERM, done);
298 
299 	preface();
300 
301 	if (argc <= 1)
302 		conv(stdin);
303 	else
304 		while (--argc > 0) {
305 			if (strcmp(*++argv, "-") == 0)
306 				fp = stdin;
307 			else if ((fp = fopen(*argv, "r")) == NULL) {
308 				fprintf(stderr, "%s: can't open %s\n",
309 					prog, *argv);
310 				pexit(prog, 2);
311 			}
312 			infilename = *argv;
313 			conv(fp);
314 			(void) fclose(fp);
315 		}
316 	done();
317 }
318 
319 /* process list of page numbers to be printed */
320 outlist(s)
321 	register char *s;
322 {
323 	int n1, n2, i;
324 
325 	nolist = 0;
326 	while (*s) {
327 		n1 = 0;
328 		if (isdigit (*s))
329 			do
330 				n1 = 10 * n1 + *s++ - '0';
331 			while (isdigit(*s));
332 		else
333 			n1 = -9999;
334 		n2 = n1;
335 		if (*s == '-') {
336 			s++;
337 			n2 = 0;
338 			if (isdigit(*s))
339 				do
340 					n2 = 10 * n2 + *s++ - '0';
341 				while (isdigit(*s));
342 			else
343 				n2 = 9999;
344 		}
345 		olist[nolist++] = n1;
346 		olist[nolist++] = n2;
347 		if (*s != '\0')
348 			s++;
349 	}
350 	olist[nolist] = 0;
351 #ifdef DEBUG
352 	if (dbg)
353 		for (i = 0; i < nolist; i += 2)
354 			printf("%3d %3d\n", olist[i], olist[i + 1]);
355 #endif
356 }
357 
358 conv(fp)	/* convert a file */
359 	register FILE *fp;
360 {
361 	register int c, k;
362 	int m, n, n1, m1;
363 	char str[100], buf[300];
364 
365 	while ((c = getc(fp)) != EOF)
366 		switch (c) {
367 		case '\n': case ' ': case '\0':
368 			break;
369 		case '{': 		/* push down current environment */
370 			t_push();
371 			break;
372 		case '}':
373 			t_pop();
374 			break;
375 		case '0': case '1': case '2': case '3': case '4':
376 		case '5': case '6': case '7': case '8': case '9':
377 			/* two motion digits plus a character */
378 			hmot((c - '0') * 10 + getc(fp) - '0');
379 			lastcmd = HMOT;
380 			put1(getc(fp), (char *) 0);
381 			lastcmd = CPUT;
382 			break;
383 		case 'c': 		/* single ascii character */
384 			put1(getc(fp), (char *) 0);
385 			lastcmd = CPUT;
386 			break;
387 		case 'C':
388 			fscanf(fp, "%s", str);
389 			put1s(str);
390 			lastcmd = CPUT;
391 			break;
392 		case 't': 		/* straight text */
393 			fgets(buf, sizeof buf, fp);
394 			t_text(buf);
395 			lastcmd = CPUT;
396 			break;
397 		case 'D': 		/* draw function */
398 			fgets(buf, sizeof buf, fp);
399 			switch (buf[0]) {
400 			case 'l': 	/* draw a line */
401 				sscanf(buf + 1, "%d %d", &n, &m);
402 				drawline(n, m);
403 				break;
404 			case 'c': 	/* circle */
405 				sscanf(buf + 1, "%d", &n);
406 				drawcirc(n);
407 				break;
408 			case 'e': 	/* ellipse */
409 				sscanf(buf + 1, "%d %d", &m, &n);
410 				drawellip(m, n);
411 				break;
412 			case 'a': 	/* arc */
413 				sscanf(buf + 1, "%d %d %d %d",
414 					&n, &m, &n1, &m1);
415 				drawarc(n, m, n1, m1);
416 				break;
417 			case '~': 	/* b-spline */
418 			case 'g': 	/* gremlin curve */
419 			case 'z': 	/* bezier cubic */
420 				drawcurve(buf);
421 				break;
422 			case 'p': 	/* filled polygon */
423 			case 'P': 	/* bordered filled polygon */
424 				drawpoly(buf);
425 				break;
426 			case 't': 	/* line thickness */
427 			case 's': 	/* line style */
428 				sscanf(buf + 1, "%d", &n);
429 				printf("%d D%c\n", n, buf[0]);
430 				break;
431 			default:
432 				fprintf(stderr,
433 					"%s: unknown drawing function %s\n",
434 					prog, buf);
435 				exit(2);
436 			}
437 			break;
438 		case 'i':
439 			fscanf(fp, "%d", &n);
440 			printf("%d Di\n", n);
441 			break;
442 		case 's':
443 			fscanf(fp, "%d", &n);
444 			t_size(n);
445 			lastcmd = FNT;
446 			break;
447 		case 'f':
448 			fscanf(fp, "%s", str);
449 			setfont(t_font(str));
450 			lastcmd = FNT;
451 			break;
452 		case 'H': 		/* absolute horizontal motion */
453 			while ((c = getc(fp)) == ' ')
454 				;
455 			k = 0;
456 			do
457 				k = 10 * k + c - '0';
458 			while (isdigit(c = getc(fp)));
459 			ungetc(c, fp);
460 			hgoto(k);
461 			lastcmd = HMOT;
462 			break;
463 		case 'h': 		/* relative horizontal motion */
464 			while ((c = getc(fp)) == ' ')
465 				;
466 			k = 0;
467 			do
468 				k = 10 * k + c - '0';
469 			while (isdigit(c = getc(fp)));
470 			ungetc(c, fp);
471 			hmot(k);
472 			lastcmd = HMOT;
473 			break;
474 		case 'w':
475 			FlushShow(1);
476 			lastcmd = BRK;
477 			break;
478 		case 'V':
479 			fscanf(fp, "%d", &n);
480 			vgoto(n);
481 			lastcmd = VMOT;
482 			break;
483 		case 'v':
484 			fscanf(fp, "%d", &n);
485 			vmot(n);
486 			lastcmd = VMOT;
487 			break;
488 		case 'p': 		/* new page */
489 			fscanf(fp, "%d", &n);
490 			t_page(n);
491 			lastcmd = NONE;
492 			break;
493 		case 'n': 		/* end of line -- ignore */
494 			while (getc(fp) != '\n')
495 				;
496 			FlushShow(1);
497 			lastcmd = BRK;
498 			break;
499 		case '#': 		/* comment */
500 			/* maybe should pass through as a PS comment */
501 			while (getc(fp) != '\n')
502 				;
503 			break;
504 		case 'x': 		/* device control */
505 			devcntrl(fp);
506 			break;
507 		case '%':		/* imbedded PostScript */
508 			/* copy everything up to but NOT including a line */
509 			/* with at single "." */
510 			FlushShow(0);
511 			MoveTo();
512 			DoMove();
513 			printf("\n%% included PostScript\n");
514 			while (fgets(buf, sizeof buf, fp) != NULL) {
515 				if (strcmp(".\n", buf) == 0)
516 					break;
517 				fputs(buf, stdout);
518 			}
519 			break;
520 		default:
521 			fprintf(stderr, "%s: bad input char \\%03o (%c)\n",
522 				prog, c, c);
523 			exit(2);
524 		}
525 }
526 
527 /* put in PostScript prolog */
528 preface()
529 {
530 	register FILE *prolog;
531 	char hostname[256];
532 	char tempfile[512];
533 	struct passwd *pswd;
534 	long clock;
535 	char *libdir;
536 
537 	printf("%%!%s\n", COMMENTVERSION);
538 	pswd = getpwuid(getuid());
539 	(void) gethostname(hostname, sizeof hostname);
540 	printf("%%%%Creator: %s:%s (%s)\n", hostname,
541 		pswd->pw_name, pswd->pw_gecos);
542 	printf("%%%%Title: %s (ditroff)\n", infilename);
543 	printf("%%%%CreationDate: %s", (time(&clock), ctime(&clock)));
544 	printf("%%%%EndComments\n");
545 	if ((libdir = envget("PSLIBDIR")) == NULL)
546 		libdir = LibDir;
547 	mstrcat(tempfile, libdir, prologfile, sizeof tempfile);
548 	if (copyfile(tempfile, stdout) != 0) {
549 		fprintf(stderr, "%s: can't copy prolog file %s\n",
550 			prog, tempfile);
551 		exit(2);
552 	}
553 	printf("ditstart\n");
554 }
555 
556 devcntrl(fp)	/* interpret device control functions */
557 	FILE *fp;
558 {
559 	char str[20], str1[50], buf[50];
560 	int c, n, res, minh, minv;
561 
562 	fscanf(fp, "%s", str);
563 	switch (str[0]) {		/* crude for now */
564 	case 'i': 		/* initialize */
565 		fileinit();
566 		t_init();
567 		lastcmd = NONE;
568 		break;
569 	case 'T': 		/* device name */
570 		/*
571 		fscanf(fp, "%s", devname);
572 		if (strcmp(devname, "psc")) {
573 			fprintf(stderr, "%s: device not psc\n", prog);
574 			exit(2);
575 		}
576 		*/
577 		printf("(%s)xT\n", devname);
578 		lastcmd = NONE;
579 		break;
580 	case 't': 		/* trailer */
581 		t_trailer();
582 		lastcmd = NONE;
583 		break;
584 	case 'p': 		/* pause -- can restart */
585 		t_reset('p');
586 		lastcmd = NONE;
587 		break;
588 	case 's': 		/* stop */
589 		t_reset('s');
590 		lastcmd = NONE;
591 		break;
592 	case 'r': 		/* resolution assumed when prepared */
593 		fscanf(fp, "%d %d %d", &res, &minh, &minv);
594 		t_res(res, minh, minv);
595 		lastcmd = NONE;
596 		break;
597 	case 'f': 		/* font used */
598 		fscanf(fp, "%d %s", &n, str);
599 		fgets(buf, sizeof buf, fp);	/* in case theres a filename */
600 		ungetc('\n', fp);		/* fgets goes too far */
601 		str1[0] = 0;	/* in case there is nothing to come in */
602 		sscanf(buf, "%s", str1);
603 		loadfont(n, str, str1);
604 		lastcmd = FNT;
605 		break;
606 	case 'H': 		/* char height */
607 		fscanf(fp, "%d", &n);
608 		t_charht(n);
609 		lastcmd = FNT;
610 		break;
611 	case 'S': 		/* slant */
612 		fscanf(fp, "%d", &n);
613 		t_slant(n);
614 		lastcmd = FNT;
615 		break;
616 #ifdef XMOD
617 	case 'X': {		/* \X command from ditroff */
618             int last;
619 	    char largebuf[128];
620 	    fscanf (fp, "%1s", str);
621 	    switch (str[0]) {
622 		case 'p' :
623 		    FlushShow(0);MoveTo();DoMove();
624 		    fgets(largebuf, sizeof(largebuf), fp);
625 		    last = strlen(largebuf) - 1;
626 		    if (last >= 0 && largebuf[last] == '\n') {
627 			ungetc('\n', fp);
628 			largebuf[last] = ' ';
629 		    }
630 		    puts(largebuf);
631 		    break;
632 		case 'f' :
633 		    FlushShow(0);MoveTo();DoMove();
634 		    if (fscanf(fp, "%s", largebuf) == 1) {
635 			char *nl = (char *) index(largebuf, '\n');
636 			if (nl) *nl = '\0';
637 			includefile(largebuf);
638 		    } else
639 			fprintf(stderr, "warning - include cmd w/o path.\n");
640 		    break;
641 	    }
642 	}
643 	break;
644 #endif
645 	}
646 	/* skip rest of input line */
647 	while ((c = getc(fp)) != '\n' && c != EOF)
648 		;
649 }
650 
651 #ifdef XMOD
652 includefile(filenm)
653 	char *filenm;
654 {
655 	FILE *inf;
656 	int ch, c1, c2, firstch = 0;
657 
658 	if (!(inf = fopen(filenm, "r"))) {
659 		fprintf(stderr, "psdit: fopen(%s): ", filenm);
660 		perror();
661 		exit(1);
662 	}
663 	c1 = fgetc(inf); c2 = fgetc(inf);
664 	if (c1 != '%' || c2 != '!')
665 		fprintf(stderr, "psdit: %s not a postscript file.\n", filenm),
666 		exit(1);
667 
668 	fputs("%!", stdout);
669 	while ((ch = fgetc(inf)) != EOF) {
670 		putchar(ch);
671 		if (firstch && ch == '%') {
672 			/* we have to double leading '%'s */
673 			putchar('%');
674 		}
675 		firstch = (ch == '\n');
676 	}
677 	fclose(inf);
678 }
679 #endif
680 
681 fileinit()	/* read in font and code files, etc. */
682 {
683 	int i, fin, nw;
684 	char *filebase, *p;
685 	char temp[60];
686 	unsigned msize;
687 
688 	/*
689 	 * Open table for device,
690 	 * read in resolution, size info, font info, etc., and set params.
691 	 */
692 	sprintf(temp, "%s/dev%s/DESC.out", ditdir, devname);
693 	if ((fin = open(temp, 0)) < 0) {
694 		fprintf(stderr, "%s: can't open %s - %s\n",
695 			prog, devname, temp);
696 		pexit(prog, 2);
697 	}
698 	if (read(fin, (char *) &dev, sizeof (struct dev)) !=
699 			sizeof (struct dev)) {
700 		fprintf(stderr, "%s: can't read %s\n", prog, temp);
701 		pexit(prog, 2);
702 	}
703 	dres = dev.res;
704 	nfonts = dev.nfonts;
705 	nsizes = dev.nsizes;
706 	nchtab = dev.nchtab;
707 	/* enough room for whole file */
708 	filebase = malloc((unsigned) dev.filesize);
709 	if (read(fin, filebase, dev.filesize) != dev.filesize) {
710 		fprintf(stderr, "%s: trouble reading %s\n", prog, temp);
711 		pexit(prog, 2);
712 	}
713 	pstab = (short *) filebase;		/* point size table */
714 	chtab = pstab + nsizes + 1;		/* char index table */
715 	chname = (char *) (chtab + dev.nchtab);	/* char name table */
716 	p = chname + dev.lchname;		/* end of char name table */
717 	/* parse the preloaded font tables */
718 	for (i = 1; i <= nfonts; i++) {
719 		fontdelta[i] = 0;
720 		fontbase[i] = (struct font *) p;
721 		nw = *p & BMASK;	/* number of width entries */
722 		if ((smnt == 0) && (fontbase[i]->specfont == 1))
723 			smnt = i;		/* first special font */
724 		p += sizeof (struct font); /* skip header */
725 		widthtab[i] = p;		/* width table */
726 		/* kern table is next */
727 		codetab[i] = p + 2 * nw;	/* device codes */
728 		fitab[i] = p + 3 * nw;		/* font index table */
729 		p += 3 * nw + dev.nchtab + (128 - 32);	/* next font */
730 		t_fp(i, fontbase[i]->namefont, fontbase[i]->intname);
731 		loadpswidths(i, fontbase[i]->namefont);
732 		sayload(i, fontbase[i]->namefont, (char *) 0);
733 #ifdef DEBUG
734 		if (fdbg > 1)
735 			fontprint(i);
736 #endif
737 	}
738 	fontdelta[0] = 0;
739 	msize = 3*255 + dev.nchtab + (128 - 32) + sizeof (struct font);
740 	fontbase[0] = (struct font *) malloc(msize);
741 	widthtab[0] = (char *) fontbase[0] + sizeof (struct font);
742 	fontbase[0]->nwfont = 255;
743 	close(fin);
744 }
745 
746 loadpswidths(i, name)
747 	int i;
748 	char *name;
749 {
750 	char temp[60];
751 	register FILE *auxin;
752 	register int j;
753 	int cc, wid, funny;
754 
755 	sprintf(temp, "%s/dev%s/%s.aux", ditdir, devname, name);
756 	auxin = fopen(temp, "r");
757 	/* allocate table */
758 	if (pswidths[i] == NULL)
759 		pswidths[i] = (int *) malloc(256 * (sizeof (int)));
760 	/* initialize to not-there */
761 	for (j = 0; j <= 255; pswidths[i][j++] = -1)
762 		;
763 	/* read them in */
764 	while (fscanf(auxin, "%d %d %d", &cc, &wid, &funny) != EOF)
765 		pswidths[i][cc] = wid | (funny << 12);
766 	(void) fclose(auxin);
767 }
768 
769 #ifdef DEBUG
770 fontprint(i)	/* debugging print of font i (0, ...) */
771 int i;
772 {
773 	int j, n;
774 	char *p;
775 
776 	printf("font %d:\n", i);
777 	p = (char *) fontbase[i];
778 	n = fontbase[i]->nwfont & BMASK;
779 	printf("base=0%o, nchars=%d, spec=%d, name=%s, widtab=0%o, fitab=0%o\n",
780 		p, n, fontbase[i]->specfont,
781 		fontbase[i]->namefont, widthtab[i], fitab[i]);
782 	printf("widths:\n");
783 	for (j = 0; j <= n; j++) {
784 		printf(" %2d", widthtab[i][j] & BMASK);
785 		if (j % 20 == 19)
786 			printf("\n");
787 	}
788 	printf("\ncodetab:\n");
789 	for (j = 0; j <= n; j++) {
790 		printf(" %2d", codetab[i][j] & BMASK);
791 		if (j % 20 == 19)
792 			printf("\n");
793 	}
794 	printf("\nfitab:\n");
795 	for (j = 0; j <= dev.nchtab + 128 - 32; j++) {
796 		printf(" %2d", fitab[i][j] & BMASK);
797 		if (j % 20 == 19)
798 			printf("\n");
799 	}
800 	printf("\n");
801 }
802 #endif
803 
804 loadfont(n, s, s1) /* load font info for font s on position n */
805 	int n;
806 	char *s, *s1;
807 {
808 	char temp[60];
809 	int fin, nw, norig;
810 	int bcount;
811 
812 	if (n < 0 || n > NFONT) {
813 		fprintf(stderr, "%s: illegal fp command %d %s\n", prog, n, s);
814 		exit(2);
815 	}
816 	if (strcmp(s, fontbase[n]->namefont) == 0)
817 		return;
818 	if (fontbase[n]->namefont != 0)
819 		fontdelta[n] = 1;
820 	if (s1 == NULL || s1[0] == '\0')
821 		sprintf(temp, "%s/dev%s/%s.out", ditdir, devname, s);
822 	else
823 		sprintf(temp, "%s/%s.out", s1, s);
824 	if ((fin = open(temp, 0)) < 0) {
825 		fprintf(stderr, "%s: can't open font table %s\n", prog, temp);
826 		pexit(prog, 2);
827 	}
828 	norig = fontbase[n]->nwfont & BMASK;
829 	bcount = 3 * norig + nchtab + 128 - 32 + sizeof (struct font);
830 	(void) read(fin, (char *) fontbase[n], bcount);
831 	if ((fontbase[n]->nwfont & BMASK) > norig) {
832 		fprintf(stderr, "%s: Font %s too big for position %d\n",
833 			prog, s, n);
834 		exit(2);
835 	}
836 	close(fin);
837 	nw = fontbase[n]->nwfont & BMASK;
838 	widthtab[n] = (char *) fontbase[n] + sizeof (struct font);
839 	codetab[n] = (char *) widthtab[n] + 2 * nw;
840 	fitab[n] = (char *) widthtab[n] + 3 * nw;
841 	t_fp(n, fontbase[n]->namefont, fontbase[n]->intname);
842 	loadpswidths(n, fontbase[n]->namefont);
843 	sayload(n, s, s1);
844 	fontbase[n]->nwfont = norig; /* so can later use full original size */
845 #ifdef DEBUG
846 	if (fdbg > 1)
847 		fontprint(n);
848 #endif
849 }
850 
851 sayload(n, s, s1)	/* position n contains font s (internal s1) */
852 	int n;
853 	char *s, *s1;
854 {
855 	char pass[60];
856 	FILE *ptrfile;
857 	char Adobefont[60];
858 
859 	if (s1 == NULL || s1[0] == '\0')
860 		sprintf(pass, "%s/dev%s/%s.map", ditdir, devname, s);
861 	else
862 		sprintf(pass, "%s/%s.map", s1, s);
863 	if ((ptrfile = fopen(pass, "r")) == NULL) {
864 		fprintf(stderr, "%s: can't open font map file %s\n",
865 			prog, pass);
866 		pexit(prog, 2);
867 	}
868 	fscanf(ptrfile, "%s", Adobefont);
869 	FlushShow(0);
870 	printf("%d(%s)xf %d f\n", n, Adobefont, n);
871 	font = n;
872 	(void) fclose(ptrfile);
873 }
874 
875 done()
876 {
877 	t_reset('s');
878 	exit(0);
879 }
880 
881 t_init()	/* "x i" - initialize device */
882 {
883 	movepending = NONE;
884 	savex = savey = 0;
885 
886 	t_size(10);		/* start somewhere */
887 	t_slant(0);
888 	setfont(1);		/* set font */
889 	printf("xi\n");
890 	printf("%%%%EndProlog\n");
891 }
892 
893 t_push()	/* begin a new block */
894 {
895 	FlushShow(1);
896 	MoveTo();
897 	DoMove();
898 	if (dlevel == DSTACK) {
899 		fprintf(stderr, "%s: ditroff push/pop overflow!\n", prog);
900 		exit(2);
901 	}
902 	ditstack[dlevel].hpos = hpos;
903 	ditstack[dlevel].vpos = vpos;
904 	ditstack[dlevel].fontsize = fontsize;
905 	ditstack[dlevel].fontheight = fontheight;
906 	ditstack[dlevel].fontslant = fontslant;
907 	ditstack[dlevel].font = font;
908 	dlevel++;
909 	printf("\nditpush\n");
910 }
911 
912 t_pop()	/* pop to previous state */
913 {
914 	FlushShow(1);
915 	MoveTo();
916 	DoMove();
917 	if (dlevel == 0) {
918 		fprintf(stderr, "%s: ditroff push/pop underflow!\n", prog);
919 		exit(2);
920 	}
921 	dlevel--;
922 	hpos = ditstack[dlevel].hpos;
923 	vpos = ditstack[dlevel].vpos;
924 	fontsize = ditstack[dlevel].fontsize;
925 	fontheight = ditstack[dlevel].fontheight;
926 	fontslant = ditstack[dlevel].fontslant;
927 	font = ditstack[dlevel].font;
928 	printf("%d s %d xH %d xS %d f\n",
929 		fontsize, fontheight, fontslant, font);
930 	startx = savex = hpos;
931 	savey = vpos;
932 	PSx = hpos * PSmag;
933 	PSy = vpos * PSmag;
934 	printf("%d %d MXY\n", savex, savey);
935 	movepending = NONE;
936 	printf("\nditpop\n");
937 }
938 
939 t_page(n)	/* do whatever new page functions */
940 {
941 	register int i;
942 
943 	if (output && ++scount >= spage) {
944 		t_reset('p');
945 		scount = 0;
946 	}
947 	output = 1;
948 	FlushShow(0);
949 	if (!firstpage)
950 		printf("\n%d p", n);
951 	firstpage = FALSE;
952 	printf("\n%%%%Page: %d %d\n", n, ++pageno, n);
953 	for (i = 0; i <= nfonts; i++)
954 		if (fontdelta[i] != 0)
955 			sayload(i, fontname[i].name, (char *) 0);
956 	vpos = 0;
957 	PSy = 0;
958 	printf("%d s %d xH %d xS %d f\n",
959 		fontsize, fontheight, fontslant, font);
960 	if (nolist == 0)
961 		return;
962 	output = 0;
963 	for (i = 0; i < nolist; i += 2)
964 		if (n >= olist[i] && n <= olist[i + 1]) {
965 			output = 1;
966 			break;
967 		}
968 }
969 
970 t_size(n)	/* convert integer to internal size number*/
971 	int n;
972 {
973 	FlushShow(1);
974 	if (fontsize != n) {
975 		fontsize = n;
976 #ifdef XMOD
977 		fontheight = n;
978 #endif
979 		printf("%d s\n", fontsize);
980 	}
981 }
982 
983 t_charht(n)	/* set character height to n */
984 	int n;
985 {
986 	FlushShow(1);
987 	if (fontheight != n) {
988 		fontheight = n;
989 		printf("%d xH\n", fontheight);
990 	}
991 }
992 
993 t_slant(n)	/* set slant to n */
994 	int n;
995 {
996 	FlushShow(1);
997 	if (fontslant != n) {
998 		fontslant = n;
999 		printf("%d xS\n", fontslant);
1000 	}
1001 }
1002 
1003 t_font(s)	/* convert string to internal font number */
1004 	char *s;
1005 {
1006 	int n;
1007 
1008 	n = atoi(s);
1009 	return n < 0 || n > nfonts ? 1 : n;
1010 }
1011 
1012 t_text(s)	/* print string s as text??? */
1013 	char *s;
1014 {
1015 	fprintf(stderr, "%s: ditroff t <%s> unimplemented!\n", prog, s);
1016 }
1017 
1018 t_reset(c)
1019 {
1020 	output = 1;			/* by God */
1021 	if (c == 'p')
1022 		printf("\nxp\n");
1023 	else if (!stopped) {
1024 		printf("\nxs\n");
1025 		stopped = 1;
1026 	}
1027 	fflush(stdout);
1028 }
1029 
1030 t_res(res, minh, minv)
1031 	int res, minh, minv;
1032 {
1033 	resolution = res;
1034 	minhoriz = minh;
1035 	minvert = minv;
1036 	printf("%d %d %d xr\n", res, minh, minv);
1037 }
1038 
1039 t_trailer()
1040 {
1041 	FlushShow(0);
1042 	printf("\n%d p", pageno);
1043 	printf("\n%%%%Trailer\n");
1044 	printf("xt\n");
1045 }
1046 
1047 put1s(s)	/* s is a funny char name */
1048 char *s;
1049 {
1050 	int i;
1051 
1052 	if (!output)
1053 		return;
1054 	debugp(("%s ", s));
1055 
1056 	/* search for s in the funny char name table */
1057 	for (i = 0; i < nchtab; i++)
1058 		if (strcmp(&chname[chtab[i]], s) == 0)
1059 			break;
1060 	if (i < nchtab)
1061 		put1(i + 128, s);
1062 	else {
1063 		debugp(("not found "));
1064 		putnf(0, s);
1065 	}
1066 }
1067 
1068 #define needsescape(c) ((c) == '\\' || (c) == '(' || (c) == ')')
1069 
1070 put1(c, s)	/* output char c */
1071 	int c;
1072 	char *s;
1073 {
1074 	char *pw;
1075 	register char *p;
1076 	register int i, k;
1077 	register int cc;
1078 	int ofont, code;
1079 	int psinfo, pswid, tw;
1080 
1081 	if (!output)
1082 		return;
1083 	if (c == ' ') {
1084 		thisw = 0;
1085 		FlushShow(0);
1086 		return;
1087 	}
1088 	if (c < ' ') {
1089 		debugp(("non-exist 0%o\n", c));
1090 		return;
1091 	}
1092 	c -= 32;	/* offset char code */
1093 	k = ofont = pfont = font;
1094 	if (onspecial)
1095 		pfont = prevfont;
1096 	if ((i = fitab[pfont][c] & BMASK) != 0) {	/* char on this font */
1097 		p = codetab[pfont];
1098 		pw = widthtab[pfont];
1099 		if (onspecial) {
1100 			setfont(prevfont);
1101 			thisw = 0;
1102 			onspecial = 0;
1103 		}
1104 	} else if (smnt > 0) {			/* on special (we hope) */
1105 		for (k = smnt; k <= nfonts; k += 1)
1106 			if ((i = fitab[k][c] & BMASK) != 0) {
1107 				p = codetab[k];
1108 				pw = widthtab[k];
1109 				prevfont = pfont;
1110 				if (onspecial && k == specfont)
1111 					break;
1112 				setfont(k);
1113 				thisw = 0;
1114 				onspecial = 1;
1115 				specfont = k;
1116 				break;
1117 			}
1118 	}
1119 	if (i == 0 || k > nfonts || (code = p[i] & BMASK) == 0) {
1120 		debugp(("not found 0%o\n", c + 32));
1121 		putnf(c + 32, s);
1122 		return;
1123 	}
1124 	/*
1125 	 * when we get here,
1126 	 *  c == biased character code
1127 	 *	k == font number
1128 	 *  i == index into codetab and widthtab for this character
1129 	 *  p == codetab for this font
1130 	 *  pw == width tab for this font
1131 	 *  code == character code for this char
1132 	 */
1133 	cc = c + 32;
1134 	debugp(((isascii(cc) && isprint(cc)) ? "%c %d\n":"%03o %d\n",
1135 		cc, code));
1136 	psinfo = pswidths[font][code];	/* PS specific char info */
1137 	pswid = psinfo & PSWID;		/* PS character width */
1138 	thisw = pw[i] & BMASK;		/* troff char width */
1139 	tw = thisw = (thisw * fontsize + dev.unitwidth / 2) / dev.unitwidth;
1140 
1141 	if (psinfo & ISPSPROC && psinfo != -1) {
1142 		/* character is implemented by a PostScript proc */
1143 		showspecial(s, code, pswid);
1144 		if (pswid > 0)
1145 			PSx += PSscale(pswid * fontsize * dres);
1146 		thisw = 0;
1147 	} else {
1148 		showchar(code);
1149 		if (pswid > 0)
1150 			PSshowlen += PSscale(pswid * fontsize * dres);
1151 	}
1152 
1153 	/*
1154 	if (font != ofont) {
1155 		setfont(ofont);
1156 		startx = hpos + tw;
1157 		thisw = 0;
1158 		lastcmd = FNT;
1159 	}
1160 	*/
1161 	debugp(("...width (%d)\n", pw[i] & BMASK));
1162 }
1163 
1164 putnf(c, s)	/* note that a character wasnt found */
1165 	int c;
1166 	char *s;
1167 {
1168 	FlushShow(0);
1169 	thisw = 0;
1170 	if (s == NULL || *s == '\0')
1171 		printf("(\%3o)cb\n", c);
1172 	else if (strcmp(s, "\\|") == 0 || strcmp(s, "\\^") == 0 ||
1173 		 strcmp(s, "\\&") == 0)
1174 		return;
1175 	else
1176 		printf("(%s)cb\n", s);
1177 }
1178 
1179 t_fp(n, s, si)	/* font position n now contains font s, intname si */
1180 	int n;		/* position */
1181 	char *s;	/* font (ditname) */
1182 	char *si;	/* font (intname = number) */
1183 {
1184 	fontname[n].name = s;
1185 	fontname[n].number = atoi(si);
1186 }
1187 
1188 setfont(n)	/* set font to n */
1189 	int n;
1190 {
1191 	FlushShow(1);
1192 	if (n < 0 || n > NFONT)
1193 		fprintf(stderr, "%s: illegal font %d\n", prog, n);
1194 	if (font != n) {
1195 		font = n;
1196 		printf("%d f\n", font);
1197 	}
1198 	onspecial = 0;
1199 }
1200 
1201 drawline(dx, dy)	/* draw line from here to dx, dy */
1202 	int dx, dy;
1203 {
1204 	FlushShow(0);
1205 	MoveTo();
1206 	DoMove();
1207 	printf("%d %d Dl\n", dx, dy);
1208 	hpos += dx;
1209 	PSx = hpos * PSmag;
1210 	vpos += dy;
1211 	PSy = vpos * PSmag;
1212 }
1213 
1214 drawcurve(line)
1215 	char *line;
1216 {
1217 	FlushShow(0);
1218 	MoveTo();
1219 	DoMove();
1220 	getpoints(line + 1);
1221 	/* hpos and vpos won't be changed by curve drawing code */
1222 	hpos = x[numpoints];
1223 	vpos = y[numpoints];
1224 	switch (*line) {
1225 	case 'g':
1226 		IS_Initialize();
1227 		IS_Convert();
1228 		break;
1229 	case '~':
1230 		BS_Initialize();
1231 		BS_Convert();
1232 		break;
1233 	case 'z':
1234 		BZ_Offsets();
1235 		BZ_Convert();
1236 		break;
1237 	}
1238 	printf("Dstroke\n");
1239 }
1240 
1241 drawpoly(line)
1242 	char *line;
1243 {
1244 	int stipple;
1245 	register i;
1246 	register char *p;
1247 	int minx, miny, maxx, maxy;
1248 
1249 	FlushShow(0);
1250 	MoveTo();
1251 	DoMove();
1252 	for (p = line + 1; isspace(*p); p++)
1253 		;
1254 	for (stipple = 0; isdigit(*p);
1255 	     stipple = stipple * 10 + *p++ - '0')
1256 		;
1257 	getpoints(p);
1258 	minx = maxx = hpos;
1259 	miny = maxy = vpos;
1260 	for (i = 1; i <= numpoints; i++) {
1261 		printf(" %lg %lg lineto\n", x[i], y[i]);
1262 		if (x[i] > maxx)
1263 			maxx = x[i];
1264 		if (x[i] < minx)
1265 			minx = x[i];
1266 		if (y[i] > maxy)
1267 			maxy = y[i];
1268 		if (y[i] < miny)
1269 			miny = y[i];
1270 	}
1271 	printf("closepath %d %d %d %d %d D%c\n",
1272 	       stipple, minx, miny, maxx, maxy, *line);
1273 	/* XXX, hpos and vpos not changed? */
1274 	PSx = x[numpoints] * PSmag;
1275 	PSy = y[numpoints] * PSmag;
1276 }
1277 
1278 getpoints(s)
1279 	register char *s;
1280 {
1281 	int h = hpos, v = vpos;
1282 
1283 	numpoints = 0;
1284 	for (;;) {
1285 		int dh, dv, neg;
1286 
1287 		numpoints++;
1288 		x[numpoints] = h;
1289 		y[numpoints] = v;
1290 		if (numpoints >= MAXPOINTS - 2)	/* -2 for good measure */
1291 			break;
1292 		for (; isspace(*s); s++)
1293 			;
1294 		if (neg = *s == '-')
1295 			s++;
1296 		if (!isdigit(*s))
1297 			break;
1298 		for (dh = 0; isdigit(*s); dh = dh * 10 + *s++ - '0')
1299 			;
1300 		if (neg)
1301 			dh = - dh;
1302 		for (; isspace(*s); s++)
1303 			;
1304 		if (neg = *s == '-')
1305 			s++;
1306 		if (!isdigit(*s))
1307 			break;
1308 		for (dv = 0; isdigit(*s); dv = dv * 10 + *s++ - '0')
1309 			;
1310 		if (neg)
1311 			dv = - dv;
1312 		h += dh;
1313 		v += dv;
1314 	}
1315 }
1316 
1317 drawcirc(d)
1318 	int d;
1319 {
1320 	FlushShow(0);
1321 	MoveTo();
1322 	DoMove();
1323 	printf("%d Dc\n", d);
1324 	hpos += d;
1325 	PSx = hpos * PSmag;
1326 }
1327 
1328 drawarc(dx1, dy1, dx2, dy2)
1329 	int dx1, dy1, dx2, dy2;
1330 {
1331 	FlushShow(0);
1332 	MoveTo();
1333 	DoMove();
1334 	printf("%d %d %d %d Da\n", dx1, dy1, dx2, dy2);
1335 	hpos += dx1 + dx2;
1336 	PSx = hpos * PSmag;
1337 	vpos += dy1 + dy2;
1338 	PSy = vpos * PSmag;
1339 }
1340 
1341 drawellip(a, b)
1342 	int a, b;
1343 {
1344 	FlushShow(0);
1345 	MoveTo();
1346 	DoMove();
1347 	printf("%d %d De\n", a, b);
1348 }
1349 
1350 hmot(a)	/* relative horizontal motion */
1351 	int a;
1352 {
1353 	register int aa;
1354 
1355 	aa = abs(a);
1356 	if (aa < 8 || aa > 10 * thisw || a >= 100 ||
1357 	    thisw != 0 && abs(thisw - a) > 4)
1358 		FlushShow(1);
1359 	hpos += a;
1360 	if (lastcmd != CPUT)
1361 		startx = hpos;
1362 }
1363 
1364 hgoto(a) /* absolute horizontal motion */
1365 	int a;
1366 {
1367 	FlushShow(1);
1368 	startx = hpos = a;
1369 	thisw = 0;
1370 }
1371 
1372 vmot(a) /* relative vertical motion */
1373 	int a;
1374 {
1375 	FlushShow(1);
1376 	vpos += a;
1377 	thisw = 0;
1378 }
1379 
1380 vgoto(a) /* absolute vertical motion */
1381 	int a;
1382 {
1383 	FlushShow(1);
1384 	vpos = a;
1385 	thisw = 0;
1386 }
1387 
1388 showspecial(s, cc, wid)
1389 	char *s;
1390 	int cc;
1391 	int wid;
1392 {
1393 	char *sp;
1394 
1395 	FlushShow(0);
1396 	MoveTo();
1397 	DoMove();
1398 	putchar('(');
1399 	for (sp = s; *sp != '\0'; sp++) {
1400 		if (needsescape(*sp))
1401 			putchar('\\');
1402 		putchar(*sp);
1403 	}
1404 	printf(")%d %d oc\n", cc, wid);
1405 }
1406 
1407 showchar(c)
1408 	int c;
1409 {
1410 	if (showind == 0)
1411 		MoveTo();
1412 	else if (vpos * PSmag != PSy) {
1413 		FlushShow(0);
1414 		MoveTo();
1415 	}
1416 	if (showind >= SHOWSIZE)
1417 		FlushShow(0);
1418 	if (isascii(c) && isprint(c))
1419 		switch (c) {
1420 		case '\\':
1421 		case '(':
1422 		case ')':
1423 			showbuf[showind++] = '\\';
1424 			/* fall through */
1425 		default:
1426 			showbuf[showind++] = c;
1427 		}
1428 	else {
1429 		showbuf[showind++] = '\\';
1430 		showbuf[showind++] = ((c >> 6) & 03) + '0';
1431 		showbuf[showind++] = ((c >> 3) & 07) + '0';
1432 		showbuf[showind++] = (c & 07) + '0';
1433 	}
1434 	showbuf[showind] = '\0';
1435 	nshow++;
1436 }
1437 
1438 MoveTo()
1439 {
1440 	int x, y;
1441 
1442 	x = hpos * PSmag;
1443 	y = vpos * PSmag;
1444 	if (x != PSx) {
1445 		startx = savex = hpos;
1446 		PSx = x;
1447 		movepending |= XMOVE;
1448 	}
1449 	if (y != PSy) {
1450 		savey = vpos;
1451 		PSy = y;
1452 		movepending |= YMOVE;
1453 	}
1454 }
1455 
1456 FlushMove()
1457 {
1458 	switch (movepending) {
1459 	case NONE:
1460 		break;
1461 	case XMOVE:
1462 		printf("%d", savex);
1463 		break;
1464 	case YMOVE:
1465 		printf("%d", savey);
1466 		break;
1467 	case XYMOVE:
1468 		printf("%d %d", savex, savey);
1469 		break;
1470 	default:
1471 		fprintf(stderr, "%s: invalid move code %d\n",
1472 			prog, movepending);
1473 		exit(2);
1474 	}
1475 }
1476 
1477 char *movecmds[] = { "MX", "MY", "MXY" };
1478 
1479 DoMove()
1480 {
1481 	FlushMove();
1482 	if (movepending != NONE) {
1483 		printf(" %s\n", movecmds[movepending - 1]);
1484 		movepending = NONE;
1485 	}
1486 }
1487 
1488 char showops[] = "SXYN";
1489 
1490 FlushShow(t)
1491 	int t;
1492 {
1493 	long err, tlen;
1494 	float cerror;
1495 
1496 	if (showind == 0) {
1497 		thisw = 0;
1498 		return;
1499 	}
1500 	if (movepending != NONE)
1501 		FlushMove();
1502 	tlen = hpos - startx;
1503 	if (lastcmd == CPUT)
1504 		tlen += thisw;
1505 	err = tlen * PSmag - PSshowlen;
1506 	if (nshow != 1 && abs(err) > ErrorTolerance) {
1507 		cerror = (float) err / ((nshow - 1) * PSmag);
1508 #ifdef DEBUG
1509 		fprintf(stderr, "F%d lc %d thisw %d ", t, lastcmd, thisw);
1510 		fprintf(stderr, "x %ld h %ld tn %ld %ld ",
1511 			startx, hpos, tlen*PSmag, PSshowlen);
1512 		fprintf(stderr, "error %d %.4f %s\n", nshow, cerror, showbuf);
1513 		fflush(stderr);
1514 #endif
1515 		printf(" %.4f(%s)A%c\n", cerror, showbuf, showops[movepending]);
1516 	} else
1517 		printf("(%s)%c\n", showbuf, showops[movepending]);
1518 	showind = 0;
1519 	nshow = 0;
1520 	showbuf[showind] = '\0';
1521 	PSx += PSshowlen;
1522 	PSshowlen = 0;
1523 	startx = hpos;
1524 	if (lastcmd == CPUT)
1525 		startx += thisw;
1526 	thisw = 0;
1527 	movepending = NONE;
1528 }
1529 
1530 /* The following stolen (with modifications) from ... */
1531 /*
1532  * This program is part of gr2ps.  It converts Gremlin's curve output to
1533  * control vertices of Bezier Cubics, as supported by PostScript.
1534  * Gremlin currently supports three kinds of curves:
1535  *	(1) cubic interpolated spline with
1536  *	     i) periodic end condition, if two end points coincide
1537  *	    ii) natural end condition, otherwise
1538  *	(2) uniform cubic B-spline with
1539  *	     i) closed curve (no vertex interpolated), if end vertices coincide
1540  *	    ii) end vertex interpolation, otherwise
1541  *	(3) Bezier cubics
1542  *
1543  * The basic idea of the conversion algorithm for the first two is
1544  *	(1) take each curve segment's two end points as Bezier end vertices.
1545  *	(2) find two intermediate points in the orginal curve segment
1546  *	    (with u=1/4 and u=1/2, for example).
1547  *	(3) solve for the two intermediate control vertices.
1548  * The conversion between Bezier Cubics of Gremlin and that of PostScript
1549  * is straightforward.
1550  *
1551  * Author: Peehong Chen (phc@renoir.berkeley.edu)
1552  * Date: 9/17/1986
1553  */
1554 #include <math.h>
1555 
1556 #define BezierMax	5
1557 #define BC1		1.0/9		/* coefficient of Bezier conversion */
1558 #define BC2		4*BC1
1559 #define BC3		3*BC2
1560 #define BC4		8*BC2
1561 
1562 double Qx, Qy, h[MAXPOINTS], dx[MAXPOINTS],
1563 	dy[MAXPOINTS], d2x[MAXPOINTS], d2y[MAXPOINTS], d3x[MAXPOINTS],
1564 	d3y[MAXPOINTS];
1565 
1566 /*
1567  * This routine converts each segment of a curve, P1, P2, P3, and P4
1568  * to a set of two intermediate control vertices, V2 and V3, in a Bezier
1569  * segment, plus a third vertex of the end point P4 (assuming the current
1570  * position is P1), and then writes a PostScript command "V2 V3 V4 curveto"
1571  * to the output file.
1572  * The two intermediate vertices are obtained using
1573  *    Q(u) = V1 * (1-u)^3 + V2 * 3u(1-u)^2 + V3 * 3(1-u)u^2 + V4 * u^3
1574  * with u=1/4, and u=1/2,
1575  *	Q(1/4) = Q2 = (x2, y2)
1576  *	Q(1/2) = Q3 = (x3, y3)
1577  *	V1 = P1
1578  *	V4 = P4
1579  * and
1580  *	V2 = (32/9)*Q2 - (4/3)*(Q3 + V1) + (1/9)*V4
1581  *	V3 = -(32/9)*Q2 + 4*Q3 + V1 - (4/9)*V4
1582  */
1583 BezierSegment(x1, y1, x2, y2, x3, y3, x4, y4)
1584 	double x1, y1, x2, y2, x3, y3, x4, y4;
1585 {
1586 	double V2x, V2y, V3x, V3y;
1587 
1588 	V2x = BC4 * x2 - BC3 * (x3 + x1) + BC1 * x4;
1589 	V2y = BC4 * y2 - BC3 * (y3 + y1) + BC1 * y4;
1590 	V3x = -BC4 * x2 + 4 * x3 +  x1 - BC2 * x4;
1591 	V3y = -BC4 * y2 + 4 * y3 +  y1 - BC2 * y4;
1592 
1593 	printf(" %lg %lg %lg %lg %lg %lg curveto\n",
1594 		V2x, V2y, V3x, V3y, x4, y4);
1595 	PSx = x4 * PSmag;
1596 	PSy = y4 * PSmag;
1597 } /* end BezierSegment */
1598 
1599 /*
1600  * This routine calculates parameteric values for use in calculating
1601  * curves.  The values are an approximation of cumulative arc lengths
1602  * of the curve (uses cord * length).  For additional information,
1603  * see paper cited below.
1604  *
1605  * This is from Gremlin (called Paramaterize in gremlin),
1606  * with minor modifications (elimination of param list)
1607  */
1608 IS_Parameterize()
1609 {
1610 	register i, j;
1611 	double t1, t2;
1612 	double u[MAXPOINTS];
1613 
1614 	for (i = 1; i <= numpoints; i++) {
1615 		u[i] = 0.0;
1616 		for (j = 1; j < i; j++) {
1617 			t1 = x[j + 1] - x[j];
1618 			t2 = y[j + 1] - y[j];
1619 			u[i] += sqrt(t1 * t1 + t2 * t2);
1620 		}
1621 	}
1622 	for (i = 1; i < numpoints; i++)
1623 		h[i] = u[i + 1] - u[i];
1624 }  /* end IS_Parameterize */
1625 
1626 /*
1627  * This routine solves for the cubic polynomial to fit a spline
1628  * curve to the the points  specified by the list of values.
1629  * The curve generated is periodic.  The alogrithms for this
1630  * curve are from the "Spline Curve Techniques" paper cited below.
1631  *
1632  * This is from Gremlin (called PeriodicSpline in gremlin)
1633  *
1634  */
1635 IS_PeriodicEnd(h, z, dz, d2z, d3z)
1636 	double h[MAXPOINTS];		/* Parameterizeaterization */
1637 	double z[MAXPOINTS];		/* point list */
1638 	double dz[MAXPOINTS];		/* to return the 1st derivative */
1639 	double d2z[MAXPOINTS];		/* 2nd derivative */
1640 	double d3z[MAXPOINTS];		/* and 3rd derivative */
1641 {
1642 	double a[MAXPOINTS];
1643 	double b[MAXPOINTS];
1644 	double c[MAXPOINTS];
1645 	double d[MAXPOINTS];
1646 	double deltaz[MAXPOINTS];
1647 	double r[MAXPOINTS];
1648 	double s[MAXPOINTS];
1649 	double ftmp;
1650 	register i;
1651 
1652 	/* step 1 */
1653 	for (i = 1; i < numpoints; i++)
1654 		if (h[i] != 0)
1655 			deltaz[i] = (z[i + 1] - z[i]) / h[i];
1656 		else
1657 			deltaz[i] = 0;
1658 	h[0] = h[numpoints - 1];
1659 	deltaz[0] = deltaz[numpoints - 1];
1660 
1661 	/* step 2 */
1662 	for (i = 1; i < numpoints - 1; i++)
1663 		d[i] = deltaz[i + 1] - deltaz[i];
1664 	d[0] = deltaz[1] - deltaz[0];
1665 
1666 	/* step 3a */
1667 	a[1] = 2 * (h[0] + h[1]);
1668 	if (a[1] == 0)
1669 		return (-1);  /* 3 consecutive knots at same point */
1670 	b[1] = d[0];
1671 	c[1] = h[0];
1672 
1673 	for (i = 2; i < numpoints - 1; i++) {
1674 		ftmp = h[i - 1];
1675 		a[i] = ftmp + ftmp + h[i] + h[i] - ftmp * ftmp / a[i - 1];
1676 		if (a[i] == 0)
1677 			return (-1);  /* 3 consec knots at same point */
1678 		b[i] = d[i - 1] - ftmp * b[i - 1] / a[i - 1];
1679 		c[i] = - ftmp * c[i - 1]/a[i - 1];
1680 	}
1681 
1682 	/* step 3b */
1683 	r[numpoints - 1] = 1;
1684 	s[numpoints - 1] = 0;
1685 	for (i = numpoints - 2; i > 0; i--) {
1686 		r[i] = - (h[i] * r[i + 1] + c[i]) / a[i];
1687 		s[i] = (6 * b[i] - h[i] * s[i + 1]) / a[i];
1688 	}
1689 
1690 	/* step 4 */
1691 	d2z[numpoints - 1] = (6 * d[numpoints - 2] - h[0] * s[1]
1692 			- h[numpoints - 1] * s[numpoints - 2])
1693 		/ (h[0] * r[1] + h[numpoints - 1] * r[numpoints - 2]
1694 			+ 2 * (h[numpoints - 2] + h[0]));
1695 	for (i = 1; i < numpoints - 1; i++)
1696 		d2z[i] = r[i] * d2z[numpoints - 1] + s[i];
1697 	d2z[numpoints] = d2z[1];
1698 
1699 	/* step 5 */
1700 	for (i = 1; i < numpoints; i++) {
1701 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
1702 		if (h[i] != 0)
1703 			d3z[i] = (d2z[i + 1] - d2z[i]) / h[i];
1704 		else
1705 			d3z[i] = 0;
1706 	}
1707 
1708 	return (0);
1709 }  /* end IS_PeriodicEnd */
1710 
1711 /*
1712  * This routine solves for the cubic polynomial to fit a spline
1713  * curve from the points specified by the list of values.  The alogrithms for
1714  * this curve are from the "Spline Curve Techniques" paper cited below.
1715  *
1716  * This is from Gremlin (called NaturalEndSpline in gremlin)
1717  */
1718 IS_NaturalEnd(h, z, dz, d2z, d3z)
1719 	double h[MAXPOINTS];		/* parameterization */
1720 	double z[MAXPOINTS];		/* point list */
1721 	double dz[MAXPOINTS];		/* to return the 1st derivative */
1722 	double d2z[MAXPOINTS];		/* 2nd derivative */
1723 	double d3z[MAXPOINTS];		/* and 3rd derivative */
1724 {
1725 	double a[MAXPOINTS];
1726 	double b[MAXPOINTS];
1727 	double d[MAXPOINTS];
1728 	double deltaz[MAXPOINTS];
1729 	double ftmp;
1730 	register i;
1731 
1732 	/* step 1 */
1733 	for (i = 1; i < numpoints; i++)
1734 		if (h[i] != 0)
1735 			deltaz[i] = (z[i + 1] - z[i]) / h[i];
1736 		else
1737 			deltaz[i] = 0;
1738 	deltaz[0] = deltaz[numpoints - 1];
1739 
1740 	/* step 2 */
1741 	for (i = 1; i < numpoints - 1; i++)
1742 		d[i] = deltaz[i + 1] - deltaz[i];
1743 	d[0] = deltaz[1] - deltaz[0];
1744 
1745 	/* step 3 */
1746 	a[0] = 2 * (h[2] + h[1]);
1747 	if (a[0] == 0)		/* 3 consec knots at same point */
1748 		return (-1);
1749 	b[0] = d[1];
1750 
1751 	for (i = 1; i < numpoints - 2; i++) {
1752 		ftmp = h[i + 1];
1753 		a[i] = ftmp + ftmp + h[i + 2] + h[i + 2] -
1754 			(ftmp * ftmp) / a[i - 1];
1755 		if (a[i] == 0)		/* 3 consec knots at same point */
1756 			return (-1);
1757 		b[i] = d[i + 1] - ftmp * b[i - 1] / a[i - 1];
1758 	}
1759 
1760 	/* step 4 */
1761 	d2z[numpoints] = d2z[1] = 0;
1762 	for (i = numpoints - 1; i > 1; i--)
1763 		d2z[i] = (6 * b[i - 2] - h[i] * d2z[i + 1]) / a[i - 2];
1764 
1765 	/* step 5 */
1766 	for (i = 1; i < numpoints; i++) {
1767 		dz[i] = deltaz[i] - h[i] * (2 * d2z[i] + d2z[i + 1]) / 6;
1768 		if (h[i] != 0)
1769 			d3z[i] = (d2z[i + 1] - d2z[i]) / h[i];
1770 		else
1771 			d3z[i] = 0;
1772 	}
1773 
1774 	return (0);
1775 }  /* end IS_NaturalEnd */
1776 
1777 /*
1778  * Use the same algorithm Gremlin uses to interpolate a given
1779  * set of points, as described in ``Spline Curve Techniques,''
1780  * by Pattrick Baudelaire, Robert M. Flegal, and Robert F. Sproull,
1781  * Xerox PARC Tech Report No. 78CSL-059.
1782  */
1783 IS_Initialize()
1784 {
1785 	IS_Parameterize();
1786 
1787 	/* Solve for derivatives of the curve at each point
1788 	   separately for x and y (parametric). */
1789 
1790 	if (x[1] == x[numpoints] && y[1] == y[numpoints]) { /* closed curve */
1791 		IS_PeriodicEnd(h, x, dx, d2x, d3x);
1792 		IS_PeriodicEnd(h, y, dy, d2y, d3y);
1793 	} else {
1794 		IS_NaturalEnd(h, x, dx, d2x, d3x);
1795 		IS_NaturalEnd(h, y, dy, d2y, d3y);
1796 	}
1797 }
1798 
1799 /*
1800  * This routine converts cubic interpolatory spline to Bezier control vertices
1801  */
1802 IS_Convert()
1803 {
1804 	double t, t2, t3, x2, y2, x3, y3;
1805 	register j, j1;
1806 
1807 	for (j = 1; j < numpoints; j++) {
1808 		t = .25 * h[j];
1809 		t2 = t * t;
1810 		t3 = t2 * t;
1811 		x2 = x[j] + t * dx[j] + t2 * d2x[j] / 2.0 + t3 * d3x[j] / 6.0;
1812 		y2 = y[j] + t * dy[j] + t2 * d2y[j] / 2.0 + t3 * d3y[j] / 6.0;
1813 
1814 		t = 2 * t;
1815 		t2 = t * t;
1816 		t3 = t2 * t;
1817 		x3 = x[j] + t * dx[j] + t2 * d2x[j] / 2.0 + t3 * d3x[j] / 6.0;
1818 		y3 = y[j] + t * dy[j] + t2 * d2y[j] / 2.0 + t3 * d3y[j] / 6.0;
1819 
1820 		j1 = j + 1;
1821 		BezierSegment(x[j], y[j], x2, y2, x3, y3, x[j1], y[j1]);
1822 	}
1823 } /* end IS_Convert */
1824 
1825 /*
1826  * This routine computes a point in B-spline segment, given i, and u.
1827  * Details of this algorithm can be found in the tech. report cited below.
1828  */
1829 BS_ComputePoint(i, u, xp, yp)
1830 	int i;
1831 	float u;
1832 	double *xp, *yp;
1833 {
1834 	float u2, u3, b_2, b_1, b0, b1;
1835 	register i1, i_2, i_1;
1836 
1837 	i1  = i + 1;
1838 	i_1 = i - 1;
1839 	i_2 = i - 2;
1840 
1841 	u2 = u * u;
1842 	u3 = u2 * u;
1843 	b_2 = (1 - 3 * u + 3 * u2 - u3) / 6.0;
1844 	b_1 = (4 - 6 * u2 + 3 * u3) / 6.0;
1845 	b0  = (1 + 3 * u + 3 * u2 - 3 * u3) / 6.0;
1846 	b1  = u3 / 6.0;
1847 
1848 	*xp = b_2 * x[i_2] + b_1 * x[i_1] + b0 * x[i] + b1 * x[i1];
1849 	*yp = b_2 * y[i_2] + b_1 * y[i_1] + b0 * y[i] + b1 * y[i1];
1850 } /* end BS_ComputePoint */
1851 
1852 /*
1853  * This routine initializes the array of control vertices
1854  * We consider two end conditions here:
1855  *   (1) closed curve -- C2 continuation and end vertex not interpolated, i.e.
1856  *		V[0] = V[n-1], and
1857  *		V[n+1] = V[2].
1858  *   (2) open curve -- end vertex interpolation, i.e.
1859  *		V[0] = 2*V[1] - V[2], and
1860  *		V[n+1] = 2*V[n] - V[n-1].
1861  * Details of uniform cubic B-splines, including other end conditions
1862  * and important properties can be found in Chapters 4-5 of
1863  * Richard H. Bartels and Brian A. Barsky,
1864  * "An Introduction to the Use of Splines in Computer Graphics",
1865  * Tech. Report CS-83-136, Computer Science Division,
1866  * University of California, Berkeley, 1984.
1867  */
1868 BS_Initialize()
1869 {
1870 	register n_1, n1;
1871 
1872 	n_1 = numpoints - 1;
1873 	n1  = numpoints + 1;
1874 	if (x[1] == x[numpoints] && y[1] == y[numpoints]) { /* closed curve */
1875 		x[0] = x[n_1];				/* V[0] */
1876 		y[0] = y[n_1];
1877 		x[n1] = x[2];				/* V[n+1] */
1878 		y[n1] = y[2];
1879 	} else {				/* end vertex interpolation */
1880 		x[0] = 2 * x[1] - x[2];			/* V[0] */
1881 		y[0] = 2 * y[1] - y[2];
1882 		x[n1] = 2 * x[numpoints] - x[n_1];	/* V[n+1] */
1883 		y[n1] = 2 * y[numpoints] - y[n_1];
1884 	}
1885 } /* end BS_Initialize */
1886 
1887 /*
1888  * This routine converts uniform cubic B-spline to Bezier control vertices
1889  */
1890 BS_Convert()
1891 {
1892 	double x1, y1, x2, y2, x3, y3, x4, y4;
1893 	register i;
1894 
1895 	for (i = 2; i <= numpoints; i++) {
1896 		BS_ComputePoint(i, 0.0, &x1, &y1);
1897 		BS_ComputePoint(i, 0.25, &x2, &y2);
1898 		BS_ComputePoint(i, 0.5, &x3, &y3);
1899 		BS_ComputePoint(i, 1.0, &x4, &y4);
1900 		if (i == 2)
1901 			printf("%lg %lg moveto\n", x1, y1);
1902 		BezierSegment(x1, y1, x2, y2, x3, y3, x4, y4);
1903 	}
1904 } /* end BS_Convert */
1905 
1906 /*
1907  * This routine copies the offset between two consecutive control points
1908  * into an array.  That is,
1909  * 	O[i] = (x[i], y[i]) = V[i+1] - V[i],
1910  * for i=1 to N-1, where N is the number of points given.
1911  * The starting end point (V[1]) is saved in (Qx, Qy).
1912  */
1913 BZ_Offsets()
1914 {
1915 	register i;
1916 
1917 	/* Assign offsets btwn points to array for convenience of processing */
1918 	Qx = x[1];
1919 	Qy = y[1];
1920 	for (i = 1; i < numpoints; i++) {
1921 		x[i] = x[i + 1] - x[i];
1922 		y[i] = y[i + 1] - y[i];
1923 	}
1924 }
1925 
1926 /*
1927  * This routine contructs paths of piecewise continuous Bezier cubics
1928  * in PostScript based on the given set of control vertices.
1929  * Given 2 points, a straight line is drawn.
1930  * Given 3 points V[1], V[2], and V[3], a Bezier cubic segment
1931  * of (V[1], (V[1]+V[2])/2, (V[2]+V[3])/2, V[3]) is drawn.
1932  * In the case when N (N >= 4) points are given, N-2 Bezier segments will
1933  * be drawn, each of which (for i=1 to N-2) is translated to PostScript as
1934  *	Q+O[i]/3  Q+(3*O[i]+O[i+1])/6  K+O[i+1]/2  curveto,
1935  * where
1936  *	Q is the current point,
1937  *	K is the continuation offset = Qinitial + Sigma(1, i)(O[i])
1938  * Note that when i is 1, the initial point
1939  *	Q = V[1].
1940  * and when i is N-2, the terminating point
1941  *	K+O[i+1]/2 = V[N].
1942  */
1943 BZ_Convert()
1944 {
1945 	register i, i1;
1946 	double x1, y1, x2, y2, x3, y3, Kx, Ky;
1947 
1948 	if (numpoints == 2) {
1949 		printf(" %lg %lg rlineto\n", x[1], y[1]);
1950 		PSx += x[1] * PSmag;
1951 		PSy += y[1] * PSmag;
1952 		return;
1953 	}
1954 	if (numpoints == 3) {
1955 		x1 = Qx + x[1];
1956 		y1 = Qy + y[1];
1957 		x2 = x1 + x[2];
1958 		y2 = y1 + y[2];
1959 		printf(" %lg %lg %lg %lg %lg %lg curveto\n",
1960 		       (Qx + x1) / 2.0, (Qy + y1) / 2.0, (x1 + x2) / 2.0,
1961 		       (y1 + y2) / 2.0, x2, y2);
1962 		PSx = x2 * PSmag;
1963 		PSy = y2 * PSmag;
1964 		return;
1965 	}
1966 	/* numpoints >= 4 */
1967 	Kx = Qx + x[1];
1968 	Ky = Qy + y[1];
1969 	x[1] *= 2;
1970 	y[1] *= 2;
1971 	x[numpoints - 1] *= 2;
1972 	y[numpoints - 1] *= 2;
1973 	for (i = 1; i <= numpoints - 2; i++) {
1974 		i1 = i + 1;
1975 		x1 = Qx + x[i] / 3;
1976 		y1 = Qy + y[i] / 3;
1977 		x2 = Qx + (3 * x[i] + x[i1]) / 6;
1978 		y2 = Qy + (3 * y[i] + y[i1]) / 6;
1979 		x3 = Kx + x[i1] / 2;
1980 		y3 = Ky + y[i1] / 2;
1981 		printf(" %lg %lg %lg %lg %lg %lg curveto\n",
1982 			x1, y1, x2, y2, x3, y3);
1983 		Qx = x3;
1984 		Qy = y3;
1985 		Kx += x[i1];
1986 		Ky += y[i1];
1987 	}
1988 	PSx = x3 * PSmag;
1989 	PSy = y3 * PSmag;
1990 } /* end BZ_Convert */
1991