1 /* @(#)dterm.c	1.14	(Berkeley)	02/26/85"
2  *
3  *	Converts ditroff output to text on a terminal.  It is NOT meant to
4  *	produce readable output, but is to show one how one's paper is (in
5  *	general) formatted - what will go where on which page.
6  *
7  *	options:
8  *
9  *	  -hn	set horizontal resolution to n (in characters per inch;
10  *		default is 10.0).
11  *
12  *	  -vn	set vertical resolution (default is 6.0).
13  *
14  *	  -ln	set maximum output line-length to n (default is 79).
15  *
16  *	-olist	output page list - as in troff.
17  *
18  *	  -c	continue at end of page.  Default is to stop at the end
19  *		of each page, print "dterm:" and wait for a command.
20  *		Type ? to get a list of available commands.
21  *
22  *	  -m	print margins.  Default action is to cut printing area down
23  *		to only the part of the page with information on it.
24  *
25  *	  -a	make the output readable - i.e. print like a "troff -a" with
26  *		no character overlap, and one space 'tween words
27  *
28  *	  -L	put a form feed (^L) at the end of each page
29  *
30  *	  -w	sets h = 20, v = 12, l = 131, also sets -c, -m and -L to allow
31  *		for extra-wide printouts on the printer.
32  *
33  *	-fxxx	get special character definition file "xxx".  Default is
34  *		FONTDIR/devter/specfile.
35  */
36 
37 
38 #include	<stdio.h>
39 #include	<ctype.h>
40 #include	<math.h>
41 
42 
43 #define	FATAL		1
44 #define	PGWIDTH		266		/* WAY too big - for good measure */
45 #define	PGHEIGHT	220
46 #define LINELEN		78
47 #define SPECFILE	"devter/specfile"
48 #ifndef FONTDIR
49 #define FONTDIR		"/usr/lib/font"
50 #endif
51 
52 #define hgoto(n)	hpos = n
53 #define vgoto(n)	vpos = n
54 #define hmot(n)		hpos += n
55 #define vmot(n)		vpos += n
56 
57 #define	sgn(n)		((n > 0) ? 1 : ((n < 0) ? -1 : 0))
58 #define	abs(n)		((n) >= 0 ? (n) : -(n))
59 #define	max(x,y)	((x) > (y) ? (x) : (y))
60 #define	min(x,y)	((x) < (y) ? (x) : (y))
61 #define sqr(x)		(long int)(x)*(x)
62 
63 
64 char	SccsId [] = "@(#)dterm.c	1.14	(Berkeley)	02/26/85";
65 
66 char	**spectab;		/* here go the special characters */
67 char	specfile[100] = FONTDIR;/* place to look up special characters */
68 char	*malloc();
69 char	*operand();
70 
71 int 	keepon	= 0;	/* flags:  Don't stop at the end of each page? */
72 int	clearsc = 0;		/* Put out form feed at each page? */
73 int	output	= 0;		/* Do we do output at all? */
74 int	nolist	= 0;		/* Output page list if > 0 */
75 int	margin	= 0;		/* Print blank margins? */
76 int	ascii	= 0;		/* make the output "readable"? */
77 int	olist[20];		/* pairs of page numbers */
78 
79 float	hscale	= 10.0;		/* characters and lines per inch for output */
80 float	vscale	= 6.0;		/*	device (defaults are for printer) */
81 FILE	*fp = stdin;		/* input file pointer */
82 
83 char	pagebuf[PGHEIGHT][PGWIDTH];
84 int	minh	= PGWIDTH;
85 int	maxh	= 0;
86 int	minv	= PGHEIGHT;
87 int	maxv	= 0;
88 int	linelen = LINELEN;
89 
90 int	hpos;		/* horizontal position to be next (left = 0) */
91 int	vpos;		/* current vertical position (down positive) */
92 
93 int	np;		/* number of pages seen */
94 int	npmax;		/* high-water mark of np */
95 int	pgnum[40];	/* their actual numbers */
96 long	pgadr[40];	/* their seek addresses */
97 
98 int	DP;			/* step size for drawing */
99 int	maxdots	= 3200;		/* maximum number of dots in an object */
100 
101 
102 
103 main(argc, argv)
104 int argc;
105 char **argv;
106 {
107 	strcat(specfile, "/");
108 	strcat(specfile, SPECFILE);
109 	while (--argc > 0 && **++argv == '-') {
110 	    switch ((*argv)[1]) {
111 		case 'f':		/* special character filepath */
112 			strncpy(specfile, operand(&argc, &argv), 100);
113 			break;
114 		case 'l':		/* output line length */
115 			linelen = atoi(operand(&argc, &argv)) - 1;
116 			break;
117 		case 'h':		/* horizontal scale (char/inch) */
118 			hscale = atof(operand(&argc, &argv));
119 			break;
120 		case 'v':		/* vertical scale (char/inch) */
121 			vscale = atof(operand(&argc, &argv));
122 			break;
123 		case 'o':		/* output list */
124 			outlist(operand(&argc, &argv));
125 			break;
126 		case 'c':		/* continue at endofpage */
127 			keepon = !keepon;
128 			break;
129 		case 'm':		/* print margins */
130 			margin = !margin;
131 			break;
132 		case 'a':		/* readable mode */
133 			ascii = !ascii;
134 			break;
135 		case 'L':		/* form feed after each page */
136 			clearsc = !clearsc;
137 			break;
138  		case 'w':		/* "wide" printer format */
139 			hscale = 16.0;
140 			vscale = 9.6;
141 			linelen = 131;
142 			keepon = 1;
143 			clearsc = 1;
144 			break;
145 	    }
146 	}
147 
148 	if (argc < 1)
149 		conv(stdin);
150 	else
151 		while (argc--) {
152 			if (strcmp(*argv, "-") == 0)
153 				fp = stdin;
154 			else if ((fp = fopen(*argv, "r")) == NULL)
155 				error(FATAL, "can't open %s", *argv);
156 			conv(fp);
157 			fclose(fp);
158 			argv++;
159 		}
160 	done();
161 }
162 
163 
164 /*----------------------------------------------------------------------------*
165  | Routine:	char  * operand (& argc,  & argv)
166  |
167  | Results:	returns address of the operand given with a command-line
168  |		option.  It uses either "-Xoperand" or "-X operand", whichever
169  |		is present.  The program is terminated if no option is present.
170  |
171  | Side Efct:	argc and argv are updated as necessary.
172  *----------------------------------------------------------------------------*/
173 
174 char *operand(argcp, argvp)
175 int * argcp;
176 char ***argvp;
177 {
178 	if ((**argvp)[2]) return(**argvp + 2); /* operand immediately follows */
179 	if ((--*argcp) <= 0) {			/* operand next word */
180 	    fprintf (stderr, "command-line option operand missing.\n");
181 	    exit(1);
182 	}
183 	return(*(++(*argvp)));			/* no operand */
184 }
185 
186 
187 outlist(s)	/* process list of page numbers to be printed */
188 char *s;
189 {
190 	int n1, n2, i;
191 
192 	nolist = 0;
193 	while (*s) {
194 		n1 = 0;
195 		if (isdigit(*s))
196 			do
197 				n1 = 10 * n1 + *s++ - '0';
198 			while (isdigit(*s));
199 		else
200 			n1 = -9999;
201 		n2 = n1;
202 		if (*s == '-') {
203 			s++;
204 			n2 = 0;
205 			if (isdigit(*s))
206 				do
207 					n2 = 10 * n2 + *s++ - '0';
208 				while (isdigit(*s));
209 			else
210 				n2 = 9999;
211 		}
212 		olist[nolist++] = n1;
213 		olist[nolist++] = n2;
214 		if (*s != '\0')
215 			s++;
216 	}
217 	olist[nolist] = 0;
218 }
219 
220 
221 in_olist(n)	/* is n in olist? */
222 int n;
223 {
224 	int i;
225 
226 	if (nolist == 0)
227 		return(1);	/* everything is included */
228 	for (i = 0; i < nolist; i += 2)
229 		if (n >= olist[i] && n <= olist[i+1])
230 			return(1);
231 	return(0);
232 }
233 
234 
235 conv(fp)
236 register FILE *fp;
237 {
238 	register int c;
239 	int m, n, i, n1, m1;
240 	char str[100], buf[300];
241 
242 	while ((c = getc(fp)) != EOF) {
243 		switch (c) {
244 		case '\n':	/* when input is text */
245 		case '\t':
246 		case ' ':
247 		case 0:
248 			break;
249 
250 		case '0': case '1': case '2': case '3': case '4':
251 		case '5': case '6': case '7': case '8': case '9':
252 				/* two motion digits plus a character */
253 			if (ascii) {
254 			    hmot((int)hscale);
255 			    getc(fp);
256 			} else {
257 			    hmot((c-'0')*10 + getc(fp)-'0');
258 			}
259 			put1(getc(fp));
260 			break;
261 
262 		case 'c':	/* single ascii character */
263 			put1(getc(fp));
264 			break;
265 
266 		case 'C':	/* funny character */
267 			fscanf(fp, "%s", str);
268 			put1s(str);
269 			break;
270 
271 		case 't':	/* straight text */
272 			fgets(buf, sizeof(buf), fp);
273 			t_text(buf);
274 			break;
275 
276 		case 'D':	/* draw function */
277 			fgets(buf, sizeof(buf), fp);
278 			switch (buf[0]) {
279 			case 'l':	/* draw a line */
280 				sscanf(buf+1, "%d %d", &n, &m);
281 				drawline(n, m);
282 				break;
283 			case 'c':	/* circle */
284 				sscanf(buf+1, "%d", &n);
285 				drawcirc(n);
286 				break;
287 			case 'e':	/* ellipse */
288 				sscanf(buf+1, "%d %d", &m, &n);
289 				drawellip(m, n);
290 				break;
291 			case 'a':	/* arc */
292 				sscanf(buf+1, "%d %d %d %d", &n, &m, &n1, &m1);
293 				drawarc(n, m, n1, m1);
294 				break;
295 			case 'q':	/* versatec polygon - ignore */
296 				while (buf[strlen(buf) - 1] != '\n')
297 				    if (fgets(buf, sizeof(buf), fp) == NULL)
298 					error(FATAL,"unexpected end of input");
299 				break;
300 			case 'P':	/* unbordered */
301 			case 'p':	/* polygon */
302 				sscanf(buf+1, "%d", &n);
303 				n = 1;
304 				while(buf[n++] == ' ');
305 				while(isdigit(buf[n])) n++;
306 				drawwig(buf+n, 1);
307 				break;
308 			case 'g':	/* "gremlin" curve */
309 			case '~':	/* wiggly line */
310 				drawwig(buf+1, 0);
311 				break;
312 			case 't':	/* thickness - not important */
313 			case 's':	/* style - not important */
314 				break;
315 			default:
316 				error(FATAL,"unknown drawing command %s\n",buf);
317 				break;
318 			}
319 			break;
320 		case 'i':	/* stipple pattern request - ignored */
321 		case 's':	/* point size - ignored */
322 			fscanf(fp, "%d", &n);
323 			break;
324 
325 		case 'f':	/* font request - ignored */
326 			fscanf(fp, "%s", str);
327 			break;
328 
329 		case 'H':	/* absolute horizontal motion */
330 			fscanf(fp, "%d", &n);
331 			hgoto(n);
332 			break;
333 
334 		case 'h':	/* relative horizontal motion */
335 			fscanf(fp, "%d", &n);
336 			hmot(n);
337 			break;
338 
339 		case 'w':	/* word space */
340 			if (ascii) {
341 			    hmot((int)hscale);
342 			}
343 			break;
344 
345 		case 'V':	/* absolute vertical motion */
346 			fscanf(fp, "%d", &n);
347 			vgoto(n);
348 			break;
349 
350 		case 'v':	/* relative vertical motion */
351 			fscanf(fp, "%d", &n);
352 			vmot(n);
353 			break;
354 
355 		case 'p':	/* new page */
356 			fscanf(fp, "%d", &n);
357 			t_page(n);
358 			break;
359 
360 		case 'P':	/* new span (ignored) */
361 			fscanf(fp, "%d", &n);
362 			break;
363 
364 		case 'n':	/* end of line */
365 			hpos = 0;
366 		case '#':	/* comment */
367 			while (getc(fp) != '\n')
368 				;
369 			break;
370 
371 		case 'x':	/* device control */
372 			devcntrl(fp);
373 			break;
374 
375 		default:
376 			error(!FATAL, "unknown input character %o %c\n", c, c);
377 			done();
378 		}
379 	}
380 }
381 
382 
383 devcntrl(fp)	/* interpret device control functions */
384 FILE *fp;
385 {
386 	int c, n;
387 	char str[20];
388 
389 	fscanf(fp, "%s", str);
390 	switch (str[0]) {	/* crude for now */
391 	case 'i':	/* initialize */
392 		t_init(0);
393 		break;
394 	case 'r':	/* resolution assumed when prepared */
395 		fscanf(fp, "%d", &n);
396 		hscale = (float) n / hscale;
397 		vscale = (float) n / vscale;
398 		DP = min (hscale, vscale);	/* guess the drawing */
399 		DP = max (DP, 1);		/* resolution */
400 		break;
401 	case 'f':	/* font used */
402 	case 'T':	/* device name */
403 	case 't':	/* trailer */
404 	case 'p':	/* pause -- can restart */
405 	case 's':	/* stop */
406 		break;
407 	}
408 	while (getc(fp) != '\n')	/* skip rest of input line */
409 		;
410 }
411 
412 		/* error printing routine - first argument is a "fatal" flag */
413 error(f, s, a1, a2, a3, a4, a5, a6, a7) {
414     fprintf(stderr, "dterm: ");
415     fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7);
416     fprintf(stderr, "\n");
417     if (f) exit(1);
418 }
419 
420 
421 t_init(reinit)	/* initialize device */
422 int reinit;
423 {
424 	register int i;
425 	register int j;
426 	register FILE *fp;	/* file to look up special characters */
427 	register char *charptr;	/* string pointer to step through specials */
428 	register char *tabptr;	/* string pointer for spectab setting */
429 	char specials[5000];	/* intermediate input buffer (made bigger */
430 				/*   than we'll EVER use... */
431 
432 
433 	fflush(stdout);
434 	hpos = vpos = 0;
435 	for (i = 0; i < PGHEIGHT; i++)
436 		for (j = 0; j < PGWIDTH; j++)
437 			pagebuf[i][j] = ' ';
438 	minh = PGWIDTH;
439 	maxh = 0;
440 	minv = PGHEIGHT;
441 	maxv = 0;
442 
443 	if (reinit) return;		/* if this is the first time, read */
444 					/* special character table file. */
445 	if ((fp = fopen (specfile, "r")) != NULL) {
446 	    charptr = &specials[0];
447 	    for (i = 2; fscanf(fp, "%s", charptr) != EOF; i++) {
448 		charptr += strlen(charptr) + 1;
449 	    }
450 	    fclose(fp);
451 	    *charptr++ = '\0';			/* ending strings */
452 	    *charptr++ = '\0';
453 						/* allocate table */
454 	    spectab = (char **) malloc(i * sizeof(char*));
455 	    spectab[0] = tabptr = malloc(j = (int) (charptr - &specials[0]));
456 
457 						/* copy whole table */
458 	    for (charptr = &specials[0]; j--; *tabptr++ = *charptr++);
459 
460 	    tabptr = spectab[0];		/* set up pointers to table */
461 	    for (j = 0; i--; j++) {
462 		spectab[j] = tabptr;
463 		tabptr += strlen(tabptr) + 1;
464 	    }
465 
466 	} else {	/* didn't find table - allocate a null one */
467 
468 	    error (!FATAL, "Can't open special character file: %s", specfile);
469 	    spectab = (char **) malloc(2 * sizeof(char*));
470 	    spectab[0] = malloc (2);
471 	    spectab[1] = spectab[0] + 1;
472 	    *spectab[0] = '\0';
473 	    *spectab[1] = '\0';
474 	}
475 }
476 
477 
478 		/* just got "p#" command.  print the current page and */
479 t_page(n)	/* do whatever new page functions */
480 {
481 	long ftell();
482 	int c, m, i;
483 	char buf[100], *bp;
484 
485 	pgnum[np++] = n;
486 	pgadr[np] = ftell(fp);
487 	if (np > npmax)
488 		npmax = np;
489 	if (output == 0) {
490 		output = in_olist(n);
491 		t_init(1);
492 		return;
493 	}
494 
495 	putpage();
496 	fflush(stdout);
497 
498 	if (clearsc) putchar('');
499 	if (keepon) {
500 		t_init(1);
501 		return;
502 	}
503   next:
504 	for (bp = buf; (*bp = readch()); )
505 		if (*bp++ == '\n')
506 			break;
507 	*bp = 0;
508 	switch (buf[0]) {
509 	case 0:
510 		done();
511 		break;
512 	case '\n':
513 		output = in_olist(n);
514 		t_init(1);
515 		return;
516 	case '-':
517 	case 'p':
518 		m = atoi(&buf[1]) + 1;
519 		if (fp == stdin) {
520 			fputs("you can't; it's not a file\n", stderr);
521 			break;
522 		}
523 		if (np - m <= 0) {
524 			fputs("too far back\n", stderr);
525 			break;
526 		}
527 		np -= m;
528 		fseek(fp, pgadr[np], 0);
529 		output = 1;
530 		t_init(1);
531 		return;
532 	case '0': case '1': case '2': case '3': case '4':
533 	case '5': case '6': case '7': case '8': case '9':
534 		m = atoi(&buf[0]);
535 		for (i = 0; i < npmax; i++)
536 			if (m == pgnum[i])
537 				break;
538 		if (i >= npmax || fp == stdin) {
539 			fputs("you can't\n", stderr);
540 			break;
541 		}
542 		np = i + 1;
543 		fseek(fp, pgadr[np], 0);
544 		output = 1;
545 		t_init(1);
546 		return;
547 	case 'o':
548 		outlist(&buf[1]);
549 		output = 0;
550 		t_init(1);
551 		return;
552 	case '?':
553 		fputs("p	print this page again\n", stderr);
554 		fputs("-n	go back n pages\n", stderr);
555 		fputs("n	print page n (previously printed)\n", stderr);
556 		fputs("o...	set the -o output list to ...\n", stderr);
557 		break;
558 	default:
559 		fputs("?\n", stderr);
560 		break;
561 	}
562 	goto next;
563 }
564 
565 			/* print the contents of the current page.  puts out */
566 putpage()		/* only the part of the page that's been written on */
567 {			/* unless "margin" is set. */
568 	int i, j, k;
569 
570 	fflush(stdout);
571 	if (margin) minv = minh = 0;
572 	for (i = minv; i <= maxv; i++) {
573 		for (k = maxh; pagebuf[i][k] == ' '; k--)
574 			;
575 		if (k > minh + linelen)
576 			k = minh + linelen;
577 		for (j = minh; j <= k; j++)
578 			putchar(pagebuf[i][j]);
579 		putchar('\n');
580 	}
581 	fflush(stdout);
582 }
583 
584 
585 t_text(s)		/* print string s as text */
586 char *s;
587 {
588 	int c;
589 	char str[100];
590 
591 	if (!output)
592 		return;
593 	while ((c = *s++) != '\n') {
594 		if (c == '\\') {
595 			switch (c = *s++) {
596 			case '\\':
597 			case 'e':
598 				put1('\\');
599 				break;
600 			case '(':
601 				str[0] = *s++;
602 				str[1] = *s++;
603 				str[2] = '\0';
604 				put1s(str);
605 				break;
606 			}
607 		} else {
608 			put1(c);
609 		}
610 		hmot((int)hscale);
611 	}
612 }
613 
614 
615 put1s(s)	/* s is a funny char name */
616 char *s;
617 {
618 	int i;
619 	char *p;
620 	static char prev[10] = "";
621 	static int previ;
622 
623 	if (!output)
624 		return;
625 	if (strcmp(s, prev) != 0) {
626 		previ = -1;
627 		for (i = 0; *spectab[i] != '\0'; i += 2)
628 			if (strcmp(spectab[i], s) == 0) {
629 				strcpy(prev, s);
630 				previ = i;
631 				break;
632 			}
633 	}
634 	if (previ >= 0) {
635 		for (hmot((int)-hscale), p = spectab[previ+1]; *p; p++) {
636 			hmot((int)hscale);
637 			store(*p);
638 		}
639 	} else
640 		prev[0] = '\0';
641 }
642 
643 
644 put1(c)			/* output char c */
645 int c;
646 {
647 	if (!output)
648 		return;
649 	store(c);
650 }
651 
652 
653 done()
654 {
655 	output = 1;
656 	putpage();
657 	fflush(stdout);
658 	exit(0);
659 }
660 
661 
662 readch ()
663 {
664 	int c;
665 	static FILE *rcf;
666 	static nbol;	/* 0 if at beginning of a line */
667 
668 	if (rcf == NULL) {
669 		rcf = fopen ("/dev/tty", "r");
670 		setbuf (rcf, NULL);
671 	}
672 
673 	if (!nbol)
674 		fprintf (stderr, "dterm: ");	/* issue prompt */
675 	if ((c = getc (rcf)) == EOF)
676 		return 0;
677 	nbol = (c != '\n');
678 	return c;
679 }
680 
681 
682 store(c)		/* put 'c' in the page at (hpos, vpos) */
683 {
684 	register int i;
685 	register int j;
686 
687 
688 	i = hpos / hscale;	/* scale the position to page coordinates */
689 	j = vpos / vscale;
690 
691 	if (i >= PGWIDTH) i = PGWIDTH - 1;	/* don't go over the edge */
692 	else if (i < 0) i = 0;
693 	if (j >= PGHEIGHT) j = PGHEIGHT - 1;
694 	else if (j < 0) j = 0;
695 
696 	pagebuf[j][i] = c;		/* write the character */
697 
698 	if (i > maxh) maxh = i;		/* update the page bounds */
699 	if (i < minh) minh = i;
700 	if (j > maxv) maxv = j;
701 	if (j < minv) minv = j;
702 }
703 
704 
705 drawline(dx, dy)	/* draw line from here to dx, dy using s */
706 int dx, dy;
707 {
708 	register int xd;
709 	register int yd;
710 	register int i;
711 	register int numdots;
712 	int dirmot, perp;
713 	int motincr, perpincr;
714 	int ohpos, ovpos;
715 	float val, slope;
716 	float incrway;
717 
718 	ohpos = hpos;
719 	ovpos = vpos;
720 	xd = dx / DP;
721 	yd = dy / DP;
722 	if (xd == 0) {
723 		numdots = abs (yd);
724 		numdots = min(numdots, maxdots);
725 		motincr = DP * sgn (yd);
726 		put1('|');
727 		for (i = 0; i < numdots; i++) {
728 			vmot(motincr);
729 			put1('|');
730 		}
731 	} else
732 	if (yd == 0) {
733 		numdots = abs (xd);
734 		numdots = min(numdots, maxdots);
735 		motincr = DP * sgn (xd);
736 		put1('-');
737 		for (i = 0; i < numdots; i++) {
738 			hmot(motincr);
739 			put1('-');
740 		}
741 	} else {
742 	    if (abs (xd) > abs (yd)) {
743 		val = slope = (float) xd/yd;
744 		numdots = abs (xd);
745 		dirmot = 'h';
746 		perp = 'v';
747 		motincr = DP * sgn (xd);
748 		perpincr = DP * sgn (yd);
749 	    } else {
750 		val = slope = (float) yd/xd;
751 		numdots = abs (yd);
752 		dirmot = 'v';
753 		perp = 'h';
754 		motincr = DP * sgn (yd);
755 		perpincr = DP * sgn (xd);
756 	    }
757 	    numdots = min(numdots, maxdots);
758 	    incrway = sgn ((int) slope);
759 	    put1('*');
760 	    for (i = 0; i < numdots; i++) {
761 		val -= incrway;
762 		if (dirmot == 'h')
763 			hmot(motincr);
764 		else
765 			vmot(motincr);
766 		if (val * slope < 0) {
767 			if (perp == 'h')
768 				hmot(perpincr);
769 			else
770 				vmot(perpincr);
771 			val += slope;
772 		}
773 		put1('*');
774 	    }
775 	}
776 	hgoto(ohpos + dx);
777 	vgoto(ovpos + dy);
778 }
779 
780 
781 drawwig(s, poly)	/* draw wiggly line or polygon, if "poly" set */
782 char *s;
783 int poly;
784 {
785 	int x[50], y[50], xp, yp, pxp, pyp;
786 	float t1, t2, t3, w;
787 	int i, j, numdots, N;
788 	char temp[50], *p, *getstr();
789 
790 	p = s;
791 	for (N = 2; (p=getstr(p,temp)) != NULL && N < sizeof(x)/sizeof(x[0]);) {
792 		x[N] = atoi(temp);
793 		p = getstr(p, temp);
794 		y[N++] = atoi(temp);
795 	}
796 	if (poly) {
797 		for (i = 2; i < N; i++)
798 			drawline(x[i], y[i]);
799 		return;
800 	}
801 	x[0] = x[1] = hpos;
802 	y[0] = y[1] = vpos;
803 	for (i = 1; i < N; i++) {
804 		x[i+1] += x[i];
805 		y[i+1] += y[i];
806 	}
807 	x[N] = x[N-1];
808 	y[N] = y[N-1];
809 	pxp = pyp = -9999;
810 	for (i = 0; i < N-1; i++) {	/* interval */
811 		numdots = (dist(x[i], y[i], x[i+1], y[i+1])
812 			     + dist(x[i+1], y[i+1], x[i+2], y[i+2])) / 2;
813 		numdots /= DP;
814 		numdots = min(numdots, maxdots);
815 		for (j = 0; j < numdots; j++) {	/* points within */
816 			w = (float) j / numdots;
817 			t1 = 0.5 * w * w;
818 			w = w - 0.5;
819 			t2 = 0.75 - w * w;
820 			w = w - 0.5;
821 			t3 = 0.5 * w * w;
822 			xp = t1 * x[i+2] + t2 * x[i+1] + t3 * x[i] + 0.5;
823 			yp = t1 * y[i+2] + t2 * y[i+1] + t3 * y[i] + 0.5;
824 			if (xp != pxp || yp != pyp) {
825 				hgoto(xp);
826 				vgoto(yp);
827 				put1('*');
828 				pxp = xp;
829 				pyp = yp;
830 			}
831 		}
832 	}
833 }
834 
835 
836 /* copy next non-blank string from p to temp, update p */
837 
838 char *getstr(p, temp)
839 char *p, *temp;
840 {
841 	while (*p == ' ' || *p == '\t' || *p == '\n')
842 		p++;
843 	if (*p == '\0') {
844 		temp[0] = 0;
845 		return(NULL);
846 	}
847 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
848 		*temp++ = *p++;
849 	*temp = '\0';
850 	return(p);
851 }
852 
853 
854 drawcirc(d)
855 {
856 	int xc, yc;
857 
858 	xc = hpos;
859 	yc = vpos;
860 	conicarc(hpos + d/2, -vpos, hpos, -vpos, hpos, -vpos, d/2, d/2);
861 	hgoto(xc + d);	/* circle goes to right side */
862 	vgoto(yc);
863 }
864 
865 
866 dist(x1, y1, x2, y2)	/* integer distance from x1,y1 to x2,y2 */
867 {
868 	float dx, dy;
869 
870 	dx = x2 - x1;
871 	dy = y2 - y1;
872 	return sqrt(dx*dx + dy*dy) + 0.5;
873 }
874 
875 
876 drawarc(dx1, dy1, dx2, dy2)
877 {
878 	int x0, y0, x2, y2, r;
879 
880 	x0 = hpos + dx1;	/* center */
881 	y0 = vpos + dy1;
882 	x2 = x0 + dx2;	/* "to" */
883 	y2 = y0 + dy2;
884 	r = sqrt((float) dx1 * dx1 + (float) dy1 * dy1) + 0.5;
885 	conicarc(x0, -y0, hpos, -vpos, x2, -y2, r, r);
886 }
887 
888 
889 drawellip(a, b)
890 {
891 	int xc, yc;
892 
893 	xc = hpos;
894 	yc = vpos;
895 	conicarc(hpos + a/2, -vpos, hpos, -vpos, hpos, -vpos, a/2, b/2);
896 	hgoto(xc + a);
897 	vgoto(yc);
898 }
899 
900 
901 conicarc(x, y, x0, y0, x1, y1, a, b)
902 {
903 		/* based on Bresenham, CACM Feb 77, pp 102-3 by Chris Van Wyk */
904 		/* capitalized vars are an internal reference frame */
905 	long dotcount = 0;
906 	int	xs, ys, xt, yt, Xs, Ys, qs, Xt, Yt, qt,
907 		M1x, M1y, M2x, M2y, M3x, M3y,
908 		Q, move, Xc, Yc;
909 	int ox1, oy1;
910 	long	delta;
911 	float	xc, yc;
912 	float	radius, slope;
913 	float	xstep, ystep;
914 
915 	ox1 = x1;
916 	oy1 = y1;
917 	if (a != b)	/* an arc of an ellipse; internally, think of circle */
918 		if (a > b) {
919 			xstep = (float)a / b;
920 			ystep = 1;
921 			radius = b;
922 		} else {
923 			xstep = 1;
924 			ystep = (float)b / a;
925 			radius = a;
926 		}
927 	else {
928 	    /* a circular arc; radius computed from center and first point */
929 		xstep = ystep = 1;
930 		radius = sqrt((float)(sqr(x0 - x) + sqr(y0 - y)));
931 	}
932 
933 	xc = x0;
934 	yc = y0;
935 	/* now, use start and end point locations to figure out
936 	the angle at which start and end happen; use these
937 	angles with known radius to figure out where start
938 	and end should be
939 	*/
940 	slope = atan2((double)(y0 - y), (double)(x0 - x) );
941 	if (slope == 0.0 && x0 < x)
942 		slope = 3.14159265;
943 	x0 = x + radius * cos(slope) + 0.5;
944 	y0 = y + radius * sin(slope) + 0.5;
945 	slope = atan2((double)(y1 - y), (double)(x1 - x));
946 	if (slope == 0.0 && x1 < x)
947 		slope = 3.14159265;
948 	x1 = x + radius * cos(slope) + 0.5;
949 	y1 = y + radius * sin(slope) + 0.5;
950 	/* step 2: translate to zero-centered circle */
951 	xs = x0 - x;
952 	ys = y0 - y;
953 	xt = x1 - x;
954 	yt = y1 - y;
955 	/* step 3: normalize to first quadrant */
956 	if (xs < 0)
957 		if (ys < 0) {
958 			Xs = abs(ys);
959 			Ys = abs(xs);
960 			qs = 3;
961 			M1x = 0;
962 			M1y = -1;
963 			M2x = 1;
964 			M2y = -1;
965 			M3x = 1;
966 			M3y = 0;
967 		} else {
968 			Xs = abs(xs);
969 			Ys = abs(ys);
970 			qs = 2;
971 			M1x = -1;
972 			M1y = 0;
973 			M2x = -1;
974 			M2y = -1;
975 			M3x = 0;
976 			M3y = -1;
977 		}
978 	else if (ys < 0) {
979 		Xs = abs(xs);
980 		Ys = abs(ys);
981 		qs = 0;
982 		M1x = 1;
983 		M1y = 0;
984 		M2x = 1;
985 		M2y = 1;
986 		M3x = 0;
987 		M3y = 1;
988 	} else {
989 		Xs = abs(ys);
990 		Ys = abs(xs);
991 		qs = 1;
992 		M1x = 0;
993 		M1y = 1;
994 		M2x = -1;
995 		M2y = 1;
996 		M3x = -1;
997 		M3y = 0;
998 	}
999 
1000 	Xc = Xs;
1001 	Yc = Ys;
1002 	if (xt < 0)
1003 		if (yt < 0) {
1004 			Xt = abs(yt);
1005 			Yt = abs(xt);
1006 			qt = 3;
1007 		} else {
1008 			Xt = abs(xt);
1009 			Yt = abs(yt);
1010 			qt = 2;
1011 		}
1012 	else if (yt < 0) {
1013 		Xt = abs(xt);
1014 		Yt = abs(yt);
1015 		qt = 0;
1016 	} else {
1017 		Xt = abs(yt);
1018 		Yt = abs(xt);
1019 		qt = 1;
1020 	}
1021 
1022 		/* step 4: calculate number of quadrant crossings */
1023 	if (((4 + qt - qs) % 4 == 0) && (Xt <= Xs) && (Yt >= Ys))
1024 		Q = 3;
1025 	else
1026 		Q = (4 + qt - qs) % 4 - 1;
1027 		/* step 5: calculate initial decision difference */
1028 	delta = sqr(Xs + 1) + sqr(Ys - 1) - sqr(xs) - sqr(ys);
1029 				/* here begins the work of drawing. */
1030 	while ((Q >= 0) || ((Q > -2) && ((Xt > Xc) && (Yt < Yc)))) {
1031 		if (dotcount++ % DP == 0) {
1032 			hgoto((int)xc);
1033 			vmot(-vpos-((int)yc));
1034 			put1('*');
1035 		}
1036 		if (Yc < 0.5) {
1037 			/* reinitialize */
1038 			Xs = Xc = 0;
1039 			Ys = Yc = sqrt((float)(sqr(xs) + sqr(ys)));
1040 			delta = sqr(Xs + 1) + sqr(Ys - 1) - sqr(xs) - sqr(ys);
1041 			Q--;
1042 			M1x = M3x;
1043 			M1y = M3y;
1044 			 {
1045 				int	T;
1046 				T = M2y;
1047 				M2y = M2x;
1048 				M2x = -T;
1049 				T = M3y;
1050 				M3y = M3x;
1051 				M3x = -T;
1052 			}
1053 		} else {
1054 			if (delta <= 0)
1055 				if (2 * delta + 2 * Yc - 1 <= 0)
1056 					move = 1;
1057 				else
1058 					move = 2;
1059 			else if (2 * delta - 2 * Xc - 1 <= 0)
1060 				move = 2;
1061 			else
1062 				move = 3;
1063 			switch (move) {
1064 			case 1:
1065 				Xc++;
1066 				delta += 2 * Xc + 1;
1067 				xc += M1x * xstep;
1068 				yc += M1y * ystep;
1069 				break;
1070 			case 2:
1071 				Xc++;
1072 				Yc--;
1073 				delta += 2 * Xc - 2 * Yc + 2;
1074 				xc += M2x * xstep;
1075 				yc += M2y * ystep;
1076 				break;
1077 			case 3:
1078 				Yc--;
1079 				delta -= 2 * Yc + 1;
1080 				xc += M3x * xstep;
1081 				yc += M3y * ystep;
1082 				break;
1083 			}
1084 		}
1085 	}
1086 	drawline((int)xc-ox1,(int)yc-oy1);
1087 }
1088