xref: /original-bsd/old/vpr/vtools/fed/subr.c (revision aba77441)
1 #ifndef lint
2 static char sccsid[] = "@(#)subr.c	4.2 (Berkeley) 08/11/83";
3 #endif
4 
5 /*
6  * subr.c: general subroutines for fed.
7  */
8 
9 #include "fed.h"
10 
11 /*
12  * initialize: various one time initializations.
13  */
14 initialize()
15 {
16 	register int i, j;
17 	register char *cp;
18 
19 	/* Initialize random variables */
20 	curwind = -1;
21 	pencolor = 1;
22 	penweight = 0;
23 
24 	/*
25 	 * Initialize value of sqrtmat.  This is a constant table
26 	 * so we don't have to redo all these square roots when the pen
27 	 * changes every time.
28 	 */
29 	for (i=0; i<10; i++) {
30 		for (j=0; j<10; j++) {
31 			sqrtmat[i][j] = sqrt((float) i*i + j*j);
32 		}
33 	}
34 
35 	/* Initialize base locations on screen. These remain fixed. */
36 	for (i=0; i<NROW; i++)
37 		for (j=0; j<NCOL; j++) {
38 			base[NCOL*i+j].c = (GLCOL+GLPAD) * j + 1;
39 			base[NCOL*i+j].r = SCRHI - (GLROW+GLPAD+10) * i - GLROW - 3;
40 		}
41 
42 	setbuf(stdout, stoutbuf);
43 
44 	curzoom = 1;	/* default is zoomed completely out */
45 	ttyinit();
46 }
47 
48 /*
49  * showfont: Wipe clean the screen, display the font
50  * in a properly spaced fashion, wait for a char to be typed, if it's
51  * p print the font, then clear the screen and ungetc the char.
52  */
53 showfont()
54 {
55 	register int i, cr, cc, nc;
56 	int roff, coff;
57 	char maxc, minc;
58 	char nextcmd;
59 	char tmpbuf[WINDSIZE];
60 
61 	zoomout();
62 	message("Show font from <char>");
63 	minc = inchar();
64 	sprintf(msgbuf, "Show font from %s to <char>", rdchar(minc));
65 	message(msgbuf);
66 	maxc = inchar();
67 
68 	clearg();
69 	zermat(tmpbuf, GLROW, GLCOL);
70 	cr = SCRHI-GLROW; cc = 3;
71 	for (i=minc; i<=maxc; i++) {
72 		if (disptable[i].nbytes) {
73 			/*
74 			 * We really should try to find out how far to the
75 			 * left the glyph goes so we don't run off the left
76 			 * end of the screen, but this is hard, so we fake it.
77 			 * Usually glyphs don't run past the left so it's OK.
78 			 */
79 			if (cc - disptable[i].left < 0)
80 				cc = disptable[i].left;
81 			nc = cc + disptable[i].width;
82 			if (nc >= SCRWID) {
83 				cc = 0;
84 				nc = disptable[i].width;
85 				cr -= 85; /* Should be GLROW but 4*100>360 */
86 				if (cr < 0)
87 					break;	/* Screen full.  Just stop. */
88 			}
89 			dispmsg(rdchar(i), cc, cr, 2);
90 			placechar(i, cr+BASELINE, cc, tmpbuf);
91 			cc = nc;
92 		}
93 	}
94 	for (;;) {
95 		nextcmd = inchar();
96 		if (nextcmd != 'p')
97 			break;
98 		printg();
99 	}
100 	if (nextcmd != 'Q' && nextcmd != 'E' && nextcmd != 'N')
101 		redraw();
102 	else
103 		clearg();
104 	ungetc(nextcmd, stdin);
105 }
106 
107 /*
108  * typein: Like showfont but takes a line of text from the user
109  * and "typesets" it on the screen.
110  */
111 typein()
112 {
113 	register int i, cr, cc, nc;
114 	char *p;
115 	int roff, coff;
116 	char maxc, minc;
117 	char nextcmd;
118 	char tmpbuf[WINDSIZE];
119 	char msgtype[100];
120 
121 	zoomout();
122 	readline("Input line to be typeset: ", msgtype, sizeof msgtype);
123 
124 	clearg();
125 	zermat(tmpbuf, GLROW, GLCOL);
126 	cr = SCRHI-GLROW; cc = 3;
127 	for (p=msgtype; *p; p++) {
128 		i = *p;
129 		if (disptable[i].nbytes) {
130 			if (cc - disptable[i].left < 0)
131 				cc = disptable[i].left;
132 			nc = cc + disptable[i].width;
133 			if (nc >= SCRWID) {
134 				cc = 0;
135 				nc = disptable[i].width;
136 				cr -= 85; /* Should be GLROW but 4*100>360 */
137 				if (cr < 0)
138 					break;	/* Screen full.  Just stop. */
139 			}
140 			dispmsg(rdchar(i), cc, cr, 2);
141 			placechar(i, cr+BASELINE, cc, tmpbuf);
142 			cc = nc;
143 		}
144 	}
145 	for (;;) {
146 		nextcmd = inchar();
147 		if (nextcmd != 'p')
148 			break;
149 		printg();
150 	}
151 	if (nextcmd != 'Q' && nextcmd != 'E' && nextcmd != 'N')
152 		redraw();
153 	else
154 		clearg();
155 	ungetc(nextcmd, stdin);
156 }
157 
158 /*
159  * placechar: draw the character ch at position (llr, llc) on the screen.
160  * Position means the logical center of the character.  zero is a GLROW x GLCOL
161  * matrix of zeros which is needed for comparison, that is, we assume that
162  * the spot on the screen where this is going is blank, so the chars better
163  * not overlap.
164  */
165 placechar(ch, llr, llc, zero)
166 int ch;
167 int llr, llc;
168 bitmat zero;
169 {
170 	bitmat glbuf;
171 	int roff, coff;
172 
173 	glbuf = findbits(ch, GLROW, GLCOL, 0, 0, &roff, &coff);
174 	if (glbuf == NULL)
175 		return;
176 	if (trace)
177 		fprintf(trace, "placechar('%s'), roff=%d, coff=%d, llr=%d, llc=%d, down=%d, left=%d, r=%d, c=%d\n", rdchar(ch), roff, coff, llr, llc, disptable[ch].down, disptable[ch].left, llr-disptable[ch].down, llc-disptable[ch].left);
178 
179 	update(zero, glbuf, GLROW, GLCOL, llr-GLROW+roff, llc-coff);
180 	if (trace)
181 		fprintf(trace, "placechar, free %x\n", glbuf);
182 	free(glbuf);
183 }
184 
185 /*
186  * redraw: The screen has gotten screwed up somehow.
187  * Assume nothing but make it look right.
188  */
189 redraw()
190 {
191 	register int i;
192 
193 	zoomout();
194 	clearg();
195 	turnofrb();
196 	for (i=0; i<NWIND; i++)
197 		if (wind[i].onscreen != NULL) {
198 			zermat(wind[i].onscreen, GLROW, GLCOL);
199 			syncwind(i);
200 
201 			/* Print the char at the lower left of the window */
202 			sprintf(msgbuf, "%s", rdchar(wind[i].used));
203 			dispmsg(msgbuf, base[i].c, base[i].r-11, 2);
204 		}
205 	if (curwind >= 0)
206 		drawbox(base[curwind].r-1, base[curwind].c-1, 1, GLROW+2, GLCOL+2);
207 }
208 
209 /*
210  * findbits: find the data bits of glyph c, wherever they are, and make
211  * nr x nc bitmat and put them in it, shifted by horoff and vertoff.
212  */
213 bitmat
214 findbits(c, nr, nc, horoff, vertoff, rcenter, ccenter)
215 int c;
216 int nr, nc;	/* the size of the dest */
217 int horoff, vertoff;
218 int *rcenter, *ccenter;
219 {
220 	register int i, j;
221 	register int r1, r2, c1, c2;
222 	bitmat retval, source;
223 	int tr, tc;	/* the size of source */
224 	char tmp[WINDSIZE];
225 
226 	if (trace)
227 		fprintf(trace, "findbits(c=%s, nr=%d, nc=%d, horoff=%d, vertoff=%d\n", rdchar(c), nr, nc, horoff, vertoff);
228 	if (disptable[c].nbytes == 0)
229 		return (NULL);
230 	switch (cht[c].wherewind) {
231 	case -2:
232 		if (trace)
233 			fprintf(trace, "case -2, saved from prev place\n");
234 		/* Saved from previous place */
235 		source = cht[c].whereat;
236 
237 		/* Ignore horoff/vertoff assuming they are already right */
238 		*rcenter = cht[c].rcent;
239 		*ccenter = cht[c].ccent;
240 		/*
241 		 * Small but important optimization: if the desired result is
242 		 * a whole window and the source happens to be in a whole
243 		 * window, just return the source pointer.  This saves
244 		 * lots of memory copies and happens quite often.
245 		 */
246 		if (nr == GLROW && nc == GLCOL)
247 			return (source);
248 		tr = GLROW; tc = GLCOL;
249 		break;
250 	case -1:
251 		if (trace)
252 			fprintf(trace, "case -1: first time\n");
253 		/* First time for this glyph: get it from font file */
254 		fseek(fontdes, (long) fbase+disptable[c].addr, 0);
255 		tr = cht[c].nrow; tc = cht[c].ncol;
256 		if (tr > GLROW || tc > GLCOL || disptable[c].nbytes > WINDSIZE)
257 			error("glyph too large for window");
258 		*rcenter = vertoff + disptable[c].up;
259 		*ccenter = horoff  + disptable[c].left;
260 		source = tmp;
261 		fread(source, disptable[c].nbytes, 1, fontdes);
262 		break;
263 	default:
264 		if (trace)
265 			fprintf(trace, "case default, in window %d", cht[c].wherewind);
266 		source = wind[cht[c].wherewind].val;
267 		tr = GLROW; tc = GLCOL;
268 		*rcenter = vertoff + cht[c].rcent;
269 		*ccenter = horoff  + cht[c].ccent;
270 		break;
271 	}
272 	if (trace)
273 		fprintf(trace, "curchar=%c=%d, tr=%d, tc=%d\n", curchar, curchar, tr, tc);
274 
275 	dumpmat("before copy, source", source, tr, tc);
276 	/* Copy in the bits into a bitmat of the right size */
277 	retval = newmat(nr, nc);
278 	r1 = max(0, -vertoff);
279 	r2 = min(GLROW-vertoff-1, GLROW-1);
280 	r2 = min(r2, tr-1);
281 	c1 = max(0, -horoff);
282 	c2 = min(GLCOL-horoff-1, GLCOL-1);
283 	c2 = min(c2, tc-1);
284 	if (trace)
285 		fprintf(trace, "findbits copy: r1=%d, r2=%d, c1=%d, c2=%d, horoff=%d, vertoff=%d\n", r1, r2, c1, c2, horoff, vertoff);
286 	for (i=r1; i<=r2; i++) {
287 		for (j=c1; j<=c2; j++)
288 			setmat(retval, nr, nc, i+vertoff, j+horoff, mat(source, tr, tc, i, j, 6));
289 	}
290 	dumpmat("result of copy", retval, nr, nc);
291 	return (retval);
292 }
293 
294 /*
295  * bufmod: called just before a buffer modifying command.
296  * Makes a backup copy of the glyph so we can undo later.
297  */
298 bufmod()
299 {
300 	changes++;
301 	if (curwind < 0)
302 		return;
303 	if (wind[curwind].undval == NULL)
304 		wind[curwind].undval = newmat(GLROW, GLCOL);
305 	bitcopy(wind[curwind].undval, wind[curwind].val, GLROW, GLCOL);
306 	und_p_r = pen_r; und_p_c = pen_c;
307 	und_c_r = curs_r; und_c_c = curs_c;
308 }
309 
310 /*
311  * undo: restore the backup copy.  We just swap pointers, which is
312  * the same as interchanging the two matrices.  This way, undo is
313  * its own inverse.
314  */
315 undo()
316 {
317 	register bitmat tmp;
318 
319 	if (wind[curwind].undval == NULL) {
320 		error("Nothing to undo");
321 	}
322 	tmp = wind[curwind].val;
323 	wind[curwind].val = wind[curwind].undval;
324 	wind[curwind].undval = tmp;
325 	pen_r = und_p_r; pen_c = und_p_c;
326 	move(base[curwind].c+pen_c, base[curwind].r+GLROW-pen_r);
327 	curs_r = und_c_r; curs_c = und_c_c;
328 	syncwind(curwind);
329 	changes++;
330 }
331 
332 /*
333  * drawline: draw a line of current flavor between the named two points.
334  * All points are relative to current window.
335  *
336  * The algorithm is that of a simple DDA.  This is similar to what the
337  * hardware of the HP 2648 does but the placing of the points will be
338  * different (because of thick pens and erasers).
339  */
340 drawline(from_r, from_c, to_r, to_c)
341 {
342 	int length, i;
343 	float x, y, xinc, yinc;
344 
345 	if (trace)
346 		fprintf(trace, "drawline from (%d, %d) to (%d, %d)\n", from_r, from_c, to_r, to_c);
347 	length = max(abs(to_r-from_r), abs(to_c-from_c));
348 	if (length <= 0) {
349 		/*
350 		 * The actual value doesn't matter, we're just avoiding
351 		 * division by zero here.
352 		 */
353 		xinc = yinc = 1.0;
354 	} else {
355 		xinc = ((float) (to_r-from_r))/length;
356 		yinc = ((float) (to_c-from_c))/length;
357 	}
358 	drawpoint(from_r, from_c);
359 	x = from_r + 0.5; y = from_c + 0.5;
360 
361 	for (i=0; i<length; i++) {
362 		x += xinc; y += yinc;
363 		drawpoint((int) x, (int) y);
364 	}
365 }
366 
367 /*
368  * drawpoint: make a point of the current flavor at (r, c).
369  */
370 drawpoint(r, c)
371 register int r, c;
372 {
373 	register int i, j;
374 
375 	if (penweight == 0)
376 		setmat(wind[curwind].val, GLROW, GLCOL, r, c, pencolor);
377 	else {
378 		for (i=0; i<10; i++)
379 			for (j=0; j<10; j++)
380 				if (penmat[i][j])
381 					setmat(wind[curwind].val, GLROW, GLCOL, r+i-4, c+j-4, pencolor);
382 	}
383 }
384 
385 /*
386  * setcmd: handle the s command.  Format: s <what> <where>.
387  */
388 setcmd()
389 {
390 	char what, where;
391 
392 	message("set <what>");
393 	what = inchar();
394 	switch (what) {
395 
396 	case 'p':	/* set pen */
397 		message("set pen <weight>");
398 		where = inchar();
399 		switch (where) {
400 		case 'f':	/* set pen fine */
401 		case 'l':	/* set pen light */
402 			message("set pen fine");
403 			penweight = 0;
404 			break;
405 		case 'h':	/* set pen heavy */
406 		case 'b':	/* set pen bold */
407 			message("set pen heavy");
408 			penweight = 1;
409 			break;
410 		default:
411 			error("Illegal kind of pen weight");
412 		}
413 		break;
414 
415 	case 's':	/* set size of heavy pen */
416 		message("set pen size to <size>");
417 		where = inchar() - '0';
418 		sprintf(msgbuf, "set pen size to %d", where);
419 		message(msgbuf);
420 		if (where > 0 && where < 10) {
421 			setpen(where);
422 		} else
423 			error("Illegal size");
424 		break;
425 
426 	case 'd':
427 		message("set draw");
428 		pencolor = 1;
429 		break;
430 
431 	case 'e':
432 		message("set erase");
433 		pencolor = 0;
434 		break;
435 
436 	default:
437 		error("Illegal set");
438 	}
439 }
440 
441 /*
442  * setpen: set the heavy pen size to s.
443  * Main work here is defining template of pen.
444  */
445 setpen(s)
446 int s;
447 {
448 	register int i, j;
449 	register float radius;
450 
451 	if (s < 1)
452 		s = 1;
453 	hpensize = s;
454 	radius = hpensize;
455 	radius /= 2;
456 	for (i=0; i<10; i++) {
457 		for (j=0; j<10; j++) {
458 			penmat[i][j] = (radius >= sqrtmat[abs(i-4)][abs(j-4)]);
459 		}
460 	}
461 
462 	/*
463 	 * Kludge to make a 2-wide pen possible by specifying 1.
464 	 */
465 	if (hpensize == 1)
466 		penmat[4][5] = 1;
467 
468 	if (trace)
469 		for (i=0; i<10; i++) {
470 			for (j=0; j<10; j++) {
471 				fprintf(trace, "%c", penmat[i][j] ? 'P' : '.');
472 			}
473 			fprintf(trace, "\n");
474 		}
475 }
476 
477 /*
478  * error: print the given error message and return for another command.
479  */
480 error(msg)
481 char *msg;
482 {
483 	message(msg);
484 	longjmp(env);
485 }
486 
487 /*
488  * copymove: do a move or copy command.
489  * cmd is C or M, the command.
490  */
491 copymove(cmd)
492 char cmd;
493 {
494 	char *action;
495 	char src, dest;
496 	bitmat cpy;
497 	char lochr[5];
498 
499 	if (cmd == 'C')
500 		action = "copy";
501 	else
502 		action = "move";
503 	sprintf(msgbuf, "%s <from>", action);
504 	message(msgbuf);
505 	src = inchar();
506 	sprintf(msgbuf, "%s %s to <to>", action, rdchar(src));
507 	message(msgbuf);
508 	dest = inchar();
509 	strcpy(lochr, rdchar(src));
510 	sprintf(msgbuf, "%s %s to %s", action, lochr, rdchar(dest));
511 	message(msgbuf);
512 
513 	/* Do the copy */
514 	disptable[dest] = disptable[src];
515 	cht[dest] = cht[src];
516 	if (cht[dest].wherewind >= 0)
517 		wind[cht[dest].wherewind].used = dest;
518 
519 	if (cmd == 'C') {
520 		if (cht[dest].wherewind != -1) {
521 			/*
522 			 * Make copies of the window so changing
523 			 * one won't change the other.
524 			 * The old copy gets the window on the screen, if any,
525 			 * relegating the new copy to the background.
526 			 */
527 			cpy = newmat(GLROW, GLCOL);
528 			if (cht[dest].wherewind >= 0)
529 				bitcopy(cpy, wind[cht[src].wherewind].val, GLROW, GLCOL);
530 			else
531 				bitcopy(cpy, cht[src].whereat, GLROW, GLCOL);
532 			if (cht[dest].wherewind == curwind)
533 				curwind = -1;
534 			cht[dest].wherewind = -2;
535 			cht[dest].whereat = cpy;
536 		}
537 	} else {
538 		/*
539 		 * Move. Delete the old entries.
540 		 */
541 		disptable[src].addr = disptable[src].nbytes = 0;
542 		cht[src].wherewind = -1;
543 	}
544 	changes++;
545 }
546 
547 /*
548  * cch: make sure there is a current character.
549  */
550 cch()
551 {
552 	if (curwind < 0)
553 		error("No current glyph");
554 }
555 
556 /*
557  * confirm: if there have been changes, ask user if he is sure.
558  */
559 confirm()
560 {
561 	char ch;
562 
563 	if (changes == 0)
564 		return;
565 	message("Changes since last write -- Are you sure?");
566 	ch = inchar();
567 	if (isupper(ch))
568 		ch = tolower(ch);
569 	switch (ch) {
570 	case 'y':
571 	case 'q':
572 	case 'e':
573 		return;
574 	case 'n':
575 	default:
576 		error("Not sure - aborted");
577 	}
578 }
579 
580 /*
581  * delchar: the D command.  Delete a character from the buffer.
582  */
583 delchar()
584 {
585 	register char c, c1, c2;
586 	register int w;
587 	char buf[5];
588 
589 	message("delete <char>");
590 	c1 = inchar();
591 	sprintf(msgbuf, "delete %s through <char>", rdchar(c1));
592 	message(msgbuf);
593 	c2 = inchar();
594 	strcpy(buf, rdchar(c1));
595 	sprintf(msgbuf, "delete %s through %s", buf, rdchar(c2));
596 	message(msgbuf);
597 	changes++;
598 
599 	for (c=c1; c<=c2; c++) {
600 		if ((w = cht[c].wherewind) >= 0) {
601 			zermat(wind[w].val, GLROW, GLCOL);
602 			syncwind(w);
603 		}
604 		cht[c].wherewind = -1;
605 		disptable[c].addr = 0;
606 		disptable[c].nbytes = 0;
607 		disptable[c].up = 0;
608 		disptable[c].down = 0;
609 		disptable[c].left = 0;
610 		disptable[c].right = 0;
611 		disptable[c].width = 0;
612 	}
613 }
614 
615 /*
616  * zoom out to full screen so the screen doean't go nuts when we
617  * print off the current zoom window.  Save old value of zoom in
618  * oldzoom so space can put us back.
619  */
620 zoomout()
621 {
622 	if (curzoom != 1)
623 		zoomn(curzoom = 1);
624 }
625 
626 /*
627  * newglyph: the n command.
628  */
629 newglyph()
630 {
631 	register int i, j;
632 	int windno;
633 	int vertoff, horoff;
634 	char *tmp;
635 
636 	message("new glyph <char>");
637 	curchar = inchar();
638 	sprintf(msgbuf, "new glyph %s", rdchar(curchar));
639 	message(msgbuf);
640 
641 	if (trace)
642 		fprintf(trace, "\n\nnewglyph(%s)\n", rdchar(curchar));
643 	if (disptable[curchar].nbytes != 0) {
644 		if (trace)
645 			fprintf(trace, "char exists: %s\n", rdchar(curchar));
646 		sprintf(msgbuf, "char exists: %s", rdchar(curchar));
647 		error(msgbuf);
648 	}
649 
650 	turnofcurs();
651 	/*
652 	 * Not on screen.  First find a suitable window,
653 	 * using round robin.
654 	 */
655 	windno = nextwind;
656 	if (trace)
657 		fprintf(trace, "chose window %d\n", windno);
658 	if (++nextwind >= NWIND)
659 		nextwind = 0;
660 #ifdef notdef
661 	if (nextwind >= 3)
662 		nextwind = 0;
663 #endif
664 	wind[windno].used = curchar;
665 
666 	/* Put a box around the current window */
667 	if (windno != curwind) {
668 		drawbox(base[curwind].r-1, base[curwind].c-1, 0, GLROW+2, GLCOL+2);
669 		drawbox(base[windno].r-1, base[windno].c-1, 1, GLROW+2, GLCOL+2);
670 	}
671 
672 	/* Print the char at the lower left of the window */
673 	sprintf(msgbuf, "%s", rdchar(curchar));
674 	dispmsg(msgbuf, base[windno].c, base[windno].r-11, 2);
675 
676 	/* Now make room in the window */
677 	if (wind[windno].onscreen == NULL) {
678 		/* Brand new window, have to allocate space */
679 		wind[windno].onscreen = newmat(GLROW, GLCOL);
680 	} else {
681 		/* Save prev glyph for later */
682 		cht[wind[curchar].used].whereat = wind[windno].val;
683 		cht[wind[curchar].used].wherewind = -2;
684 	}
685 	if (wind[windno].undval != NULL) {
686 		if (trace)
687 			fprintf(trace, "newglyph frees undo: %x\n", wind[windno].undval);
688 		free(wind[windno].undval);
689 	}
690 	wind[windno].undval = NULL;
691 
692 	/*
693 	 * Vertical & horizontal offsets.  Line up the baseline
694 	 * of the char at BASELINE from bottom, but center
695 	 * horizontally.
696 	 */
697 	wind[windno].val = newmat(GLROW, GLCOL);
698 
699 	curwind = windno;
700 	cht[curchar].wherewind = windno;
701 	cht[curchar].rcent = curs_r = GLROW - BASELINE;
702 	cht[curchar].ccent = curs_c = GLCOL / 2;
703 
704 #ifdef notdef
705 	dumpmat("wind[windno].onscreen", wind[windno].onscreen, GLROW, GLCOL);
706 #endif
707 	syncwind(windno);
708 
709 	/*
710 	 * Mung the zoom out to 1 and back.  This is needed to
711 	 * re-center the glyph on the screen if zoomed in, otherwise
712 	 * if you move by one window it puts the cursor way over at
713 	 * the right with only half the window visible.
714 	 */
715 	if ((i = curzoom) > 1) {
716 		zoomn(1);
717 		zoomn(i);
718 	}
719 }
720 
721 /*
722  * numedit: change one of the numerical parameters.
723  */
724 numedit()
725 {
726 	short * sp = 0;
727 	char * cp = 0;
728 	char c, f;
729 	char *fld;
730 	short ovalue, nvalue;
731 	char numb[20];
732 
733 	message("number of <char>");
734 	c = inchar();
735 	sprintf(msgbuf, "number of %s <field>", rdchar(c));
736 	message(msgbuf);
737 	f = inchar();
738 
739 	switch (f) {
740 	case 'a': sp = (short *)
741 			&disptable[c].addr;	fld = "addr";	break;
742 	case 'n': sp = &disptable[c].nbytes;	fld = "nbytes";	break;
743 	case 'u': cp = &disptable[c].up;	fld = "up";	break;
744 	case 'd': cp = &disptable[c].down;	fld = "down";	break;
745 	case 'l': cp = &disptable[c].left;	fld = "left";	break;
746 	case 'r': cp = &disptable[c].right;	fld = "right";	break;
747 	case 'w': sp = &disptable[c].width;	fld = "width";	break;
748 	case 's': sp = (short *) &disptable[c].nbytes;
749 						fld = "size";	break;
750 	default: error("No such field");
751 	}
752 
753 	ovalue = sp ? *sp : *cp;
754 	sprintf(msgbuf, "number of %s %s (old value %d) is ", rdchar(c), fld, ovalue);
755 	readline(msgbuf, numb, sizeof numb);
756 	nvalue = atoi(numb);
757 	if (cp)
758 		*cp = nvalue;
759 	else
760 		*sp = nvalue;
761 	changes++;
762 }
763 
764 /*
765  * These routines turn the cursor and rubber band line on and off,
766  * remembering its state for the o and r commands.
767  */
768 turnoncurs()
769 {
770 	curon();
771 	curcurs = 1;
772 }
773 
774 turnofcurs()
775 {
776 	curoff();
777 	curcurs = 0;
778 }
779 
780 turnonrb()
781 {
782 	rbon();
783 	currb = 1;
784 }
785 
786 turnofrb()
787 {
788 	rboff();
789 	currb = 0;
790 }
791 
792 synccurs()
793 {
794 	register int x, y;
795 
796 	x = base[curwind].c + curs_c;
797 	y = base[curwind].r + GLROW - curs_r - 1;
798 	movecurs(x, y);
799 }
800 
801 inchar()
802 {
803 	sync();
804 	synccurs();
805 	return (rawchar());
806 }
807 
808 /*
809  * fillin - fill in with 1's all the spots that are in the enclosed
810  * area that (x, y) is in.
811  */
812 fillin(x, y)
813 int x, y;
814 {
815 	if (x<0 || x>=GLROW || y<0 || y>=GLCOL ||
816 		mat(wind[curwind].val, GLROW, GLCOL, x, y))
817 		return;
818 
819 	setmat(wind[curwind].val, GLROW, GLCOL, x, y, 1);
820 	fillin(x-1, y);
821 	fillin(x+1, y);
822 	fillin(x, y-1);
823 	fillin(x, y+1);
824 }
825 
826 /*
827  * syncwind: make sure that window #n shows on the screen what it's
828  * supposed to after an arbitrary change.
829  */
830 syncwind(n)
831 int n;
832 {
833 	if (trace)
834 		fprintf(trace, "syncwind(%d)\n", n);
835 	update(wind[n].onscreen, wind[n].val, GLROW, GLCOL, base[n].r, base[n].c);
836 	bitcopy(wind[n].onscreen, wind[n].val, GLROW, GLCOL);
837 }
838 
839 /*
840  * Embolden artificially emboldens the glyphs in the font by smearing
841  * them to the right by the current heavy pen size.  Or else italicize it.
842  */
843 artificial()
844 {
845 	int low, high, cur;
846 	int oldps, newps;
847 	char lowch[10];
848 #define ITAL	0
849 #define BOLD	1
850 #define RESIZE	2
851 #define SMOOTH	3
852 	int kind;
853 	char *strbold;
854 
855 	sprintf(msgbuf, "Artificially <embolden/italicize/resize/smooth>");
856 	message(msgbuf);
857 
858 	cur = inchar();
859 	switch(cur) {
860 	case 'i': case 'I': kind = ITAL; strbold = "italicize"; break;
861 	case 'e': case 'E': kind = BOLD; strbold = "embolden"; break;
862 	case 'r': case 'R': kind = RESIZE; strbold = "resize"; break;
863 	case 's': case 'S': kind = SMOOTH; strbold = "smooth"; break;
864 	default: error("No such artificial operation");
865 	}
866 
867 	sprintf(msgbuf, "Artificially %s glyphs from <char>", strbold);
868 	message(msgbuf);
869 	low = inchar();
870 	strcpy(lowch, rdchar(low));
871 	sprintf(msgbuf, "Artificially %s glyphs from %s to <char>", strbold, lowch);
872 	message(msgbuf);
873 	high = inchar();
874 	if (kind == RESIZE) {
875 		sprintf(msgbuf, "Artificially %s glyphs from %s to %s from <point size>", strbold, lowch, rdchar(high));
876 		oldps = readnum(msgbuf);
877 		sprintf(msgbuf, "Artificially %s glyphs from %s to %s from %dP to <point size>P", strbold, lowch, rdchar(high), oldps);
878 		newps = readnum(msgbuf);
879 		sprintf(msgbuf, "Artificially %s glyphs from %s to %s from %dP to %dP", strbold, lowch, rdchar(high), oldps, newps);
880 		message(msgbuf);
881 		if (oldps <= 0 || oldps > 36 || newps <= 0 || newps > 36 || oldps == newps)
882 			error("Bad point sizes");
883 	} else {
884 		sprintf(msgbuf, "Artificially %s glyphs from %s to %s", strbold, lowch, rdchar(high));
885 		message(msgbuf);
886 	}
887 
888 	for (cur=low; cur<=high; cur++) {
889 		getglyph(cur);
890 		if (curchar == cur) {	/* e.g. if the getglyph succeeded */
891 			fflush(stdout);
892 			switch (kind) {
893 			case BOLD:
894 				boldglyph();
895 				break;
896 			case ITAL:
897 				italglyph();
898 				break;
899 			case RESIZE:
900 				if (oldps > newps)
901 					shrinkglyph(oldps, newps);
902 				else
903 					blowupglyph(oldps, newps);
904 				break;
905 			case SMOOTH:
906 				smoothglyph();
907 				break;
908 			}
909 			syncwind(curwind);
910 		}
911 	}
912 	message("Done");
913 }
914 
915 /*
916  * Artificially embolden the current glyph.
917  */
918 boldglyph()
919 {
920 	register int r, c, i;
921 	int smear = hpensize < 2 ? 2 : hpensize;
922 
923 	for (r=0; r<GLROW; r++)
924 		for (c=GLCOL-1; c>=smear; c--)
925 			for (i=1; i<=smear; i++)
926 				if (mat(wind[curwind].val, GLROW, GLCOL, r, c-i))
927 					setmat(wind[curwind].val, GLROW, GLCOL, r, c, 1);
928 }
929 
930 /*
931  * Artificially italicize the current glyph.
932  */
933 italglyph()
934 {
935 	register int r, c, i, off;
936 	int baser = cht[curchar].rcent; /* GLROW - BASELINE; */
937 
938 	for (r=0; r<baser; r++) {
939 		off = (baser-r) / SLOPE + 0.5;
940 		for (c=GLCOL-1; c>=off; c--) {
941 			setmat(wind[curwind].val, GLROW, GLCOL, r, c,
942 				mat(wind[curwind].val, GLROW, GLCOL, r, c-off));
943 		}
944 		for (c=off-1; c>=0; c--)
945 			setmat(wind[curwind].val, GLROW, GLCOL, r, c, 0);
946 	}
947 	for (r=baser; r<GLROW; r++) {
948 		off = (r-baser) * (2.0/7.0) + 0.5;
949 		for (c=off; c<GLCOL; c++)
950 			setmat(wind[curwind].val, GLROW, GLCOL, r, c-off,
951 				mat(wind[curwind].val, GLROW, GLCOL, r, c));
952 		for (c=off-1; c>=0; c--)
953 			setmat(wind[curwind].val, GLROW, GLCOL, r, c, 0);
954 	}
955 }
956 
957 /*
958  * Blow up or shrink a glyph from oldps points to newps points.
959  * The basic idea is that for each on point in the old glyph we
960  * find the corresponding point in the new glyph and copy the value.
961  */
962 shrinkglyph(oldps, newps)
963 int oldps, newps;
964 {
965 	float ratio;
966 	register int or, oc, nr, nc;
967 	int n;
968 	bitmat tmp, curw;
969 	int baser = cht[curchar].rcent;
970 	int basec = cht[curchar].ccent;
971 
972 	ratio = (float) newps / (float) oldps;
973 	tmp = newmat(GLROW, GLCOL);
974 	curw = wind[curwind].val;
975 	bitcopy(tmp, curw, GLROW, GLCOL);
976 	zermat(curw, GLROW, GLCOL);
977 	for (or=0; or<GLROW; or++) {
978 		nr = baser + (or-baser)*ratio + 0.5;
979 		for (oc=0; oc<GLCOL; oc++) {
980 			nc = basec + (oc-basec)*ratio + 0.5;
981 			if (nr < 0 || nr >= GLROW || nc < 0 || nc >= GLCOL)
982 				n = 0;
983 			else
984 				n = mat(tmp, GLROW, GLCOL, or, oc);
985 			if (n)
986 				setmat(curw, GLROW, GLCOL, nr, nc, n);
987 		}
988 	}
989 	disptable[curchar].width = disptable[curchar].width * ratio + 0.5;
990 	free(tmp);
991 }
992 
993 /*
994  * blow up a glyph.  Otherwise like shrinkglyph.
995  */
996 blowupglyph(oldps, newps)
997 int oldps, newps;
998 {
999 	float ratio;
1000 	register int or, oc, nr, nc;
1001 	int n;
1002 	bitmat tmp, curw;
1003 	int baser = cht[curchar].rcent;
1004 	int basec = cht[curchar].ccent;
1005 
1006 	ratio = (float) oldps / (float) newps;
1007 	tmp = newmat(GLROW, GLCOL);
1008 	curw = wind[curwind].val;
1009 	bitcopy(tmp, curw, GLROW, GLCOL);
1010 	zermat(curw, GLROW, GLCOL);
1011 	for (nr=0; nr<GLROW; nr++) {
1012 		or = baser + (nr-baser)*ratio + 0.5;
1013 		for (nc=0; nc<GLCOL; nc++) {
1014 			oc = basec + (nc-basec)*ratio + 0.5;
1015 			if (or < 0 || or >= GLROW || oc < 0 || oc >= GLCOL)
1016 				n = 0;
1017 			else
1018 				n = mat(tmp, GLROW, GLCOL, or, oc);
1019 			if (n)
1020 				setmat(curw, GLROW, GLCOL, nr, nc, n);
1021 		}
1022 	}
1023 	disptable[curchar].width = disptable[curchar].width / ratio + 0.5;
1024 	free(tmp);
1025 }
1026 
1027 /*
1028  * Smooth a glyph.  We look for corners and trim the point.  Corners of
1029  * both blanks and dots in all 4 orientations are looked for.
1030  */
1031 smoothglyph()
1032 {
1033 	bitmat tmp, curw;
1034 	register int r, c;
1035 	register int c3;
1036 	int a3, b2, b3, b4, c1, c2, c4, c5, d2, d3, d4, e3;
1037 
1038 	tmp = newmat(GLROW, GLCOL);
1039 	curw = wind[curwind].val;
1040 	bitcopy(tmp, curw, GLROW, GLCOL);
1041 	for (r=2; r<GLROW-2; r++)
1042 		for (c=2; c<GLCOL-2; c++) {
1043 			/*
1044 			 *		a3
1045 			 *	     b2 b3 b4
1046 			 *	  c1 c2 c3 c4 c5
1047 			 *	     d2 d3 d4
1048 			 *	        d4
1049 			 * where c3 is the square we are interested in
1050 			 */
1051 			b3 = mat(tmp, GLROW, GLCOL, r-1, c  );
1052 			c2 = mat(tmp, GLROW, GLCOL, r  , c-1);
1053 			c4 = mat(tmp, GLROW, GLCOL, r  , c+1);
1054 			d3 = mat(tmp, GLROW, GLCOL, r+1, c  );
1055 			/* exactly 2 of the 4 neighbors must be dots */
1056 			if (b3+c2+c4+d3 != 2) continue;
1057 
1058 			c3 = mat(tmp, GLROW, GLCOL, r  , c  );
1059 			b2 = mat(tmp, GLROW, GLCOL, r-1, c-1);
1060 			b4 = mat(tmp, GLROW, GLCOL, r-1, c+1);
1061 			d2 = mat(tmp, GLROW, GLCOL, r+1, c-1);
1062 			d4 = mat(tmp, GLROW, GLCOL, r+1, c+1);
1063 			/* exactly one of the 4 diags must match the center */
1064 			if (b2+b4+d2+d4 != 3 - 2*c3) continue;
1065 
1066 			a3 = mat(tmp, GLROW, GLCOL, r-2, c  );
1067 			c1 = mat(tmp, GLROW, GLCOL, r  , c-2);
1068 			c5 = mat(tmp, GLROW, GLCOL, r  , c+2);
1069 			e3 = mat(tmp, GLROW, GLCOL, r+2, c  );
1070 
1071 			/* Figure out which of the 4 directions */
1072 			if (b2==c3) {
1073 				if (b3+c2+c1+a3 != 4*c3) continue;
1074 			} else
1075 			if (b4==c3) {
1076 				if (b3+c4+c5+a3 != 4*c3) continue;
1077 			} else
1078 			if (d2==c3) {
1079 				if (d3+c2+c1+e3 != 4*c3) continue;
1080 			} else
1081 			if (d4==c3) {
1082 				if (d3+c4+c5+e3 != 4*c3) continue;
1083 			}
1084 
1085 			/* It must be a corner.  Toggle it. */
1086 			setmat(curw, GLROW, GLCOL, r, c, !c3);
1087 		}
1088 	free(tmp);
1089 }
1090 
1091 /*
1092  * Read a number from bottom line ala readline.
1093  * This should probably go in lib2648.
1094  */
1095 int
1096 readnum(prompt)
1097 char *prompt;
1098 {
1099 	char buf[10];
1100 	int retval;
1101 
1102 	readline(prompt, buf, sizeof buf);
1103 	retval = atoi(buf);
1104 	if (trace)
1105 		fprintf(trace, "readline returns '%s', retval=%d\n", buf, retval);
1106 	return (retval);
1107 }
1108 
1109 invert()
1110 {
1111 	register int r, c;
1112 	int tmp1, tmp2, kind;
1113 	bitmat curw = wind[curwind].val;
1114 
1115 	message("Invert <horizontally/vertically>");
1116 	kind = inchar();
1117 	switch (kind) {
1118 	case 'h': case 'H':
1119 		message("Invert horizontally");
1120 		for (r=0; r<GLROW; r++) {
1121 			if (trace)
1122 				fprintf(trace, "row %d\n", r);
1123 			for (c=0; c<=(GLCOL-1)/2; c++) {
1124 				tmp1 = mat(curw, GLROW, GLCOL, r, c);
1125 				tmp2 = mat(curw, GLROW, GLCOL, r, GLCOL-1-c);
1126 				if (trace)
1127 					fprintf(trace, "cols %d (%d) <=> %d (%d)\n", c, tmp1, GLCOL-1-c, tmp2);
1128 				setmat(curw, GLROW, GLCOL, r, c, tmp2);
1129 				setmat(curw, GLROW, GLCOL, r, GLCOL-1-c, tmp1);
1130 			}
1131 		}
1132 		break;
1133 	case 'v': case 'V':
1134 		message("Invert vertically");
1135 		for (c=0; c<GLCOL; c++) {
1136 			for (r=0; r<=(GLROW-1)/2; r++) {
1137 				tmp1 = mat(curw, GLROW, GLCOL, r, c);
1138 				tmp2 = mat(curw, GLROW, GLCOL, GLROW-1-r, c);
1139 				setmat(curw, GLROW, GLCOL, r, c, tmp2);
1140 				setmat(curw, GLROW, GLCOL, GLROW-1-r, c, tmp1);
1141 			}
1142 		}
1143 		break;
1144 	default:
1145 		error("Bad choice");
1146 	}
1147 	syncwind(curwind);
1148 }
1149