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