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