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