1 /*	SCCS Id: @(#)termcap.c	3.4	2000/07/10	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include "hack.h"
6 
7 #if defined (TTY_GRAPHICS) && !defined(NO_TERMS)
8 
9 #include "wintty.h"
10 
11 #include "tcap.h"
12 
13 
14 #ifdef MICROPORT_286_BUG
15 #define Tgetstr(key) (tgetstr(key,tbuf))
16 #else
17 #define Tgetstr(key) (tgetstr(key,&tbufptr))
18 #endif /* MICROPORT_286_BUG **/
19 
20 static char * FDECL(s_atr2str, (int));
21 static char * FDECL(e_atr2str, (int));
22 
23 void FDECL(cmov, (int, int));
24 void FDECL(nocmov, (int, int));
25 #if defined(TEXTCOLOR) && defined(TERMLIB)
26 # ifdef OVLB
27 #  if !defined(UNIX) || !defined(TERMINFO)
28 #   ifndef TOS
29 static void FDECL(analyze_seq, (char *, int *, int *));
30 #   endif
31 #  endif
32 static void NDECL(init_hilite);
33 static void NDECL(kill_hilite);
34 # endif /* OVLB */
35 #endif
36 
37 #ifdef OVLB
38 	/* (see tcap.h) -- nh_CM, nh_ND, nh_CD, nh_HI,nh_HE, nh_US,nh_UE,
39 				ul_hack */
40 struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0,0, 0,0, FALSE };
41 #endif /* OVLB */
42 
43 STATIC_VAR char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE;
44 STATIC_VAR char *VS, *VE;
45 STATIC_VAR char *ME;
46 STATIC_VAR char *MR;
47 #if 0
48 STATIC_VAR char *MB, *MH;
49 STATIC_VAR char *MD;     /* may already be in use below */
50 #endif
51 #ifdef TERMLIB
52 # ifdef TEXTCOLOR
53 STATIC_VAR char *MD;
54 # endif
55 STATIC_VAR int SG;
56 #ifdef OVLB
57 STATIC_OVL char PC = '\0';
58 #else /* OVLB */
59 STATIC_DCL char PC;
60 #endif /* OVLB */
61 STATIC_VAR char tbuf[512];
62 #endif
63 
64 #ifdef TEXTCOLOR
65 # ifdef TOS
66 const char *hilites[CLR_MAX];	/* terminal escapes for the various colors */
67 # else
68 char NEARDATA *hilites[CLR_MAX]; /* terminal escapes for the various colors */
69 # endif
70 #endif
71 
72 #ifdef OVLB
73 static char *KS = (char *)0, *KE = (char *)0;	/* keypad sequences */
74 static char nullstr[] = "";
75 #endif /* OVLB */
76 
77 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
78 extern boolean HE_resets_AS;
79 #endif
80 
81 #ifndef TERMLIB
82 STATIC_VAR char tgotobuf[20];
83 # ifdef TOS
84 #define tgoto(fmt, x, y)	(Sprintf(tgotobuf, fmt, y+' ', x+' '), tgotobuf)
85 # else
86 #define tgoto(fmt, x, y)	(Sprintf(tgotobuf, fmt, y+1, x+1), tgotobuf)
87 # endif
88 #endif /* TERMLIB */
89 
90 #ifndef MSDOS
91 
92 STATIC_DCL void NDECL(init_ttycolor);
93 
94 boolean colorflag = FALSE;			/* colors are initialized */
95 int ttycolors[CLR_MAX];
96 
97 void
init_ttycolor()98 init_ttycolor()
99 {
100 	if (!colorflag) {
101 		ttycolors[CLR_RED]		= CLR_RED;
102 		ttycolors[CLR_GREEN]		= CLR_GREEN;
103 		ttycolors[CLR_BROWN]		= CLR_BROWN;
104 		ttycolors[CLR_BLUE]		= CLR_BLUE;
105 		ttycolors[CLR_MAGENTA]		= CLR_MAGENTA;
106 		ttycolors[CLR_CYAN]		= CLR_CYAN;
107 		ttycolors[CLR_GRAY]		= CLR_GRAY;
108 		if (iflags.wc2_newcolors) {
109 			ttycolors[CLR_BLACK]	= CLR_BLACK;
110 			defsyms[S_corr].color	= CLR_BLACK;
111 			defsyms[S_dnstair].color= CLR_WHITE;
112 			defsyms[S_upstair].color= CLR_WHITE;
113 		} else {
114 			ttycolors[CLR_BLACK]	= CLR_BLUE;
115 		}
116 		ttycolors[CLR_ORANGE]		= CLR_ORANGE;
117 		ttycolors[CLR_BRIGHT_GREEN]	= CLR_BRIGHT_GREEN;
118 		ttycolors[CLR_YELLOW]		= CLR_YELLOW;
119 		ttycolors[CLR_BRIGHT_BLUE]	= CLR_BRIGHT_BLUE;
120 		ttycolors[CLR_BRIGHT_MAGENTA]	= CLR_BRIGHT_MAGENTA;
121 		ttycolors[CLR_BRIGHT_CYAN]	= CLR_BRIGHT_CYAN;
122 		ttycolors[CLR_WHITE]		= CLR_WHITE;
123 	}
124 }
125 
126 static int FDECL(convert_uchars,(char *, uchar *, int));
127 
128 #ifdef VIDEOSHADES
129 /*
130  * OPTIONS=videocolors:1-2-3-4-5-6-7-8-9-10-11-12-13-14-15
131  * Left to right assignments for:
132  *	red	green	 brown	blue	magenta	cyan	gray	black
133  *	orange	br.green yellow	br.blue	br.mag	br.cyan	white
134  */
assign_videocolors(char * colorvals)135 int assign_videocolors(char *colorvals)
136 {
137 	int i,icolor;
138 	uchar *tmpcolor;
139 
140 	init_ttycolor();
141 
142 	i = strlen(colorvals);
143 	tmpcolor = (uchar *)alloc(i);
144 	if (convert_uchars(colorvals,tmpcolor,i) < 0) return FALSE;
145 
146 	icolor = CLR_RED;
147 	for( i = 0; tmpcolor[i] != 0; ++i) {
148 	    if (icolor <= CLR_WHITE)
149 		ttycolors[icolor++] = tmpcolor[i];
150 	}
151 
152 	colorflag = TRUE;
153 	free((genericptr_t)tmpcolor);
154 	return 1;
155 }
156 #endif
157 
158 static int
convert_uchars(bufp,list,size)159 convert_uchars(bufp,list,size)
160     char *bufp; 	/* current pointer */
161     uchar *list;	/* return list */
162     int size;
163 {
164     unsigned int num = 0;
165     int count = 0;
166 
167     list[count] = 0;
168 
169     while (1) {
170 	switch(*bufp) {
171 	    case ' ':  case '\0':
172 	    case '\t': case '-':
173 	    case '\n':
174 		if (num) {
175 		    list[count++] = num;
176 		    list[count] = 0;
177 		    num = 0;
178 		}
179 		if ((count==size) || !*bufp) return count;
180 		bufp++;
181 		break;
182 	    case '#':
183 		if (num) {
184 		    list[count++] = num;
185 		    list[count] = 0;
186 		}
187 		return count;
188 	    case '0': case '1': case '2': case '3':
189 	    case '4': case '5': case '6': case '7':
190 	    case '8': case '9':
191 		num = num*10 + (*bufp-'0');
192 		if (num > 15) return -1;
193 		bufp++;
194 		break;
195 	    default: return -1;
196 	}
197     }
198     /*NOTREACHED*/
199 }
200 #endif /* !MSDOS */
201 
202 #ifdef OVLB
203 
204 void
tty_startup(wid,hgt)205 tty_startup(wid, hgt)
206 int *wid, *hgt;
207 {
208 	register int i;
209 #ifdef TERMLIB
210 	register const char *term;
211 	register char *tptr;
212 	char *tbufptr, *pc;
213 #endif
214 
215 #ifdef TEXTCOLOR
216 # ifndef MSDOS
217 	init_ttycolor();
218 # endif
219 #endif
220 
221 #ifdef TERMLIB
222 
223 # ifdef VMS
224 	term = verify_termcap();
225 	if (!term)
226 # endif
227 		term = getenv("TERM");
228 
229 # if defined(TOS) && defined(__GNUC__)
230 	if (!term)
231 		term = "builtin";		/* library has a default */
232 # endif
233 	if (!term)
234 #endif
235 #ifndef ANSI_DEFAULT
236 		error("Can't get TERM.");
237 #else
238 # ifdef TOS
239 	{
240 		CO = 80; LI = 25;
241 		TI = VS = VE = TE = nullstr;
242 		HO = "\033H";
243 		CE = "\033K";		/* the VT52 termcap */
244 		UP = "\033A";
245 		nh_CM = "\033Y%c%c";	/* used with function tgoto() */
246 		nh_ND = "\033C";
247 		XD = "\033B";
248 		BC = "\033D";
249 		SO = "\033p";
250 		SE = "\033q";
251 	/* HI and HE will be updated in init_hilite if we're using color */
252 		nh_HI = "\033p";
253 		nh_HE = "\033q";
254 		*wid = CO;
255 		*hgt = LI;
256 		CL = "\033E";		/* last thing set */
257 		return;
258 	}
259 # else /* TOS */
260 	{
261 #  ifdef MICRO
262 		get_scr_size();
263 #   ifdef CLIPPING
264 		if(CO < COLNO || LI < ROWNO+3)
265 			setclipped();
266 #   endif
267 #  endif
268 		HO = "\033[H";
269 /*		nh_CD = "\033[J"; */
270 		CE = "\033[K";		/* the ANSI termcap */
271 #  ifndef TERMLIB
272 		nh_CM = "\033[%d;%dH";
273 #  else
274 		nh_CM = "\033[%i%d;%dH";
275 #  endif
276 		UP = "\033[A";
277 		nh_ND = "\033[C";
278 		XD = "\033[B";
279 #  ifdef MICRO	/* backspaces are non-destructive */
280 		BC = "\b";
281 #  else
282 		BC = "\033[D";
283 #  endif
284 		nh_HI = SO = "\033[1m";
285 		nh_US = "\033[4m";
286 		MR = "\033[7m";
287 		TI = nh_HE = ME = SE = nh_UE = "\033[0m";
288 		/* strictly, SE should be 2, and nh_UE should be 24,
289 		   but we can't trust all ANSI emulators to be
290 		   that complete.  -3. */
291 #  ifndef MICRO
292 		AS = "\016";
293 		AE = "\017";
294 #  endif
295 		TE = VS = VE = nullstr;
296 #  ifdef TEXTCOLOR
297 		for (i = 0; i < CLR_MAX / 2; i++)
298 		    if (i != CLR_BLACK) {
299 			hilites[i|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm"));
300 			Sprintf(hilites[i|BRIGHT], "\033[1;3%dm", i);
301 			if (iflags.wc2_newcolors || (i != CLR_GRAY))
302 #   ifdef MICRO
303 			    if (i == CLR_BLUE) hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT];
304 			    else
305 #   endif
306 			    {
307 				hilites[i] = (char *) alloc(sizeof("\033[0;3%dm"));
308 				Sprintf(hilites[i], "\033[0;3%dm", i);
309 			    }
310 		    }
311 #  endif
312 		*wid = CO;
313 		*hgt = LI;
314 		CL = "\033[2J";		/* last thing set */
315 		return;
316 	}
317 # endif /* TOS */
318 #endif /* ANSI_DEFAULT */
319 
320 #ifdef TERMLIB
321 	tptr = (char *) alloc(1024);
322 
323 	tbufptr = tbuf;
324 	if(!strncmp(term, "5620", 4))
325 		flags.null = FALSE;	/* this should be a termcap flag */
326 	if(tgetent(tptr, term) < 1) {
327 		char buf[BUFSZ];
328 		(void) strncpy(buf, term,
329 				(BUFSZ - 1) - (sizeof("Unknown terminal type: .  ")));
330 		buf[BUFSZ-1] = '\0';
331 		error("Unknown terminal type: %s.", term);
332 	}
333 	if ((pc = Tgetstr("pc")) != 0)
334 		PC = *pc;
335 
336 	if(!(BC = Tgetstr("le")))	/* both termcap and terminfo use le */
337 # ifdef TERMINFO
338 	    error("Terminal must backspace.");
339 # else
340 	    if(!(BC = Tgetstr("bc"))) {	/* termcap also uses bc/bs */
341 #  ifndef MINIMAL_TERM
342 		if(!tgetflag("bs"))
343 			error("Terminal must backspace.");
344 #  endif
345 		BC = tbufptr;
346 		tbufptr += 2;
347 		*BC = '\b';
348 	    }
349 # endif
350 
351 # ifdef MINIMAL_TERM
352 	HO = (char *)0;
353 # else
354 	HO = Tgetstr("ho");
355 # endif
356 	/*
357 	 * LI and CO are set in ioctl.c via a TIOCGWINSZ if available.  If
358 	 * the kernel has values for either we should use them rather than
359 	 * the values from TERMCAP ...
360 	 */
361 # ifndef MICRO
362 	if (!CO) CO = tgetnum("co");
363 	if (!LI) LI = tgetnum("li");
364 # else
365 #  if defined(TOS) && defined(__GNUC__)
366 	if (!strcmp(term, "builtin"))
367 		get_scr_size();
368 	else {
369 #  endif
370 		CO = tgetnum("co");
371 		LI = tgetnum("li");
372 		if (!LI || !CO)			/* if we don't override it */
373 			get_scr_size();
374 #  if defined(TOS) && defined(__GNUC__)
375 	}
376 #  endif
377 # endif
378 # ifdef CLIPPING
379 	if(CO < COLNO || LI < ROWNO+3)
380 		setclipped();
381 # endif
382 	nh_ND = Tgetstr("nd");
383 	if(tgetflag("os"))
384 		error("UnNetHack can't have OS.");
385 	if(tgetflag("ul"))
386 		ul_hack = TRUE;
387 	CE = Tgetstr("ce");
388 	UP = Tgetstr("up");
389 	/* It seems that xd is no longer supported, and we should use
390 	   a linefeed instead; unfortunately this requires resetting
391 	   CRMOD, and many output routines will have to be modified
392 	   slightly. Let's leave that till the next release. */
393 	XD = Tgetstr("xd");
394 /* not:		XD = Tgetstr("do"); */
395 	if(!(nh_CM = Tgetstr("cm"))) {
396 	    if(!UP && !HO)
397 		error("UnNetHack needs CM or UP or HO.");
398 	    tty_raw_print("Playing UnNetHack on terminals without CM is suspect.");
399 	    tty_wait_synch();
400 	}
401 	SO = Tgetstr("so");
402 	SE = Tgetstr("se");
403 	nh_US = Tgetstr("us");
404 	nh_UE = Tgetstr("ue");
405 	SG = tgetnum("sg");	/* -1: not fnd; else # of spaces left by so */
406 	if(!SO || !SE || (SG > 0)) SO = SE = nh_US = nh_UE = nullstr;
407 	TI = Tgetstr("ti");
408 	TE = Tgetstr("te");
409 	VS = VE = nullstr;
410 # ifdef TERMINFO
411 	VS = Tgetstr("eA");	/* enable graphics */
412 # endif
413 	KS = Tgetstr("ks");	/* keypad start (special mode) */
414 	KE = Tgetstr("ke");	/* keypad end (ordinary mode [ie, digits]) */
415 	MR = Tgetstr("mr");	/* reverse */
416 # if 0
417 	MB = Tgetstr("mb");	/* blink */
418 	MD = Tgetstr("md");	/* boldface */
419 	MH = Tgetstr("mh");	/* dim */
420 # endif
421 	ME = Tgetstr("me");	/* turn off all attributes */
422 	if (!ME || (SE == nullstr)) ME = SE;	/* default to SE value */
423 
424 	/* Get rid of padding numbers for nh_HI and nh_HE.  Hope they
425 	 * aren't really needed!!!  nh_HI and nh_HE are outputted to the
426 	 * pager as a string - so how can you send it NULs???
427 	 *  -jsb
428 	 */
429 	nh_HI = (char *) alloc((unsigned)(strlen(SO)+1));
430 	nh_HE = (char *) alloc((unsigned)(strlen(ME)+1));
431 	i = 0;
432 	while (digit(SO[i])) i++;
433 	Strcpy(nh_HI, &SO[i]);
434 	i = 0;
435 	while (digit(ME[i])) i++;
436 	Strcpy(nh_HE, &ME[i]);
437 	AS = Tgetstr("as");
438 	AE = Tgetstr("ae");
439 	nh_CD = Tgetstr("cd");
440 # ifdef TEXTCOLOR
441 	MD = Tgetstr("md");
442 # endif
443 # ifdef TEXTCOLOR
444 #  if defined(TOS) && defined(__GNUC__)
445 	if (!strcmp(term, "builtin") || !strcmp(term, "tw52") ||
446 	    !strcmp(term, "st52")) {
447 		init_hilite();
448 	}
449 #  else
450 	init_hilite();
451 #  endif
452 # endif
453 	*wid = CO;
454 	*hgt = LI;
455 	if (!(CL = Tgetstr("cl")))	/* last thing set */
456 		error("UnNetHack needs CL.");
457 	if ((int)(tbufptr - tbuf) > (int)(sizeof tbuf))
458 		error("TERMCAP entry too big...\n");
459 	free((genericptr_t)tptr);
460 #endif /* TERMLIB */
461 }
462 
463 /* note: at present, this routine is not part of the formal window interface */
464 /* deallocate resources prior to final termination */
465 void
tty_shutdown()466 tty_shutdown()
467 {
468 #if defined(TEXTCOLOR) && defined(TERMLIB)
469 	kill_hilite();
470 #endif
471 	/* we don't attempt to clean up individual termcap variables [yet?] */
472 	return;
473 }
474 
475 void
tty_number_pad(state)476 tty_number_pad(state)
477 int state;
478 {
479 	switch (state) {
480 	    case -1:	/* activate keypad mode (escape sequences) */
481 		    if (KS && *KS) xputs(KS);
482 		    break;
483 	    case  1:	/* activate numeric mode for keypad (digits) */
484 		    if (KE && *KE) xputs(KE);
485 		    break;
486 	    case  0:	/* don't need to do anything--leave terminal as-is */
487 	    default:
488 		    break;
489 	}
490 }
491 
492 #ifdef TERMLIB
493 extern void NDECL((*decgraphics_mode_callback));    /* defined in drawing.c */
494 static void NDECL(tty_decgraphics_termcap_fixup);
495 
496 /*
497    We call this routine whenever DECgraphics mode is enabled, even if it
498    has been previously set, in case the user manages to reset the fonts.
499    The actual termcap fixup only needs to be done once, but we can't
500    call xputs() from the option setting or graphics assigning routines,
501    so this is a convenient hook.
502  */
503 static void
tty_decgraphics_termcap_fixup()504 tty_decgraphics_termcap_fixup()
505 {
506 	static char ctrlN[]   = "\016";
507 	static char ctrlO[]   = "\017";
508 	static char appMode[] = "\033=";
509 	static char numMode[] = "\033>";
510 
511 	/* these values are missing from some termcaps */
512 	if (!AS) AS = ctrlN;	/* ^N (shift-out [graphics font]) */
513 	if (!AE) AE = ctrlO;	/* ^O (shift-in  [regular font])  */
514 	if (!KS) KS = appMode;	/* ESC= (application keypad mode) */
515 	if (!KE) KE = numMode;	/* ESC> (numeric keypad mode)	  */
516 	/*
517 	 * Select the line-drawing character set as the alternate font.
518 	 * Do not select NA ASCII as the primary font since people may
519 	 * reasonably be using the UK character set.
520 	 */
521 	if (iflags.DECgraphics) xputs("\033)0");
522 #ifdef PC9800
523 	init_hilite();
524 #endif
525 
526 #if defined(ASCIIGRAPH) && !defined(NO_TERMS)
527 	/* some termcaps suffer from the bizarre notion that resetting
528 	   video attributes should also reset the chosen character set */
529     {
530 	const char *nh_he = nh_HE, *ae = AE;
531 	int he_limit, ae_length;
532 
533 	if (digit(*ae)) {	/* skip over delay prefix, if any */
534 	    do ++ae; while (digit(*ae));
535 	    if (*ae == '.') { ++ae; if (digit(*ae)) ++ae; }
536 	    if (*ae == '*') ++ae;
537 	}
538 	/* can't use nethack's case-insensitive strstri() here, and some old
539 	   systems don't have strstr(), so use brute force substring search */
540 	ae_length = strlen(ae), he_limit = strlen(nh_he);
541 	while (he_limit >= ae_length) {
542 	    if (strncmp(nh_he, ae, ae_length) == 0) {
543 		HE_resets_AS = TRUE;
544 		break;
545 	    }
546 	    ++nh_he, --he_limit;
547 	}
548     }
549 #endif
550 }
551 #endif	/* TERMLIB */
552 
553 #if defined(ASCIIGRAPH) && defined(PC9800)
554 extern void NDECL((*ibmgraphics_mode_callback));    /* defined in drawing.c */
555 #endif
556 
557 #ifdef PC9800
558 extern void NDECL((*ascgraphics_mode_callback));    /* defined in drawing.c */
559 static void NDECL(tty_ascgraphics_hilite_fixup);
560 
561 static void
tty_ascgraphics_hilite_fixup()562 tty_ascgraphics_hilite_fixup()
563 {
564     register int c;
565 
566     for (c = 0; c < CLR_MAX / 2; c++)
567 	if (c != CLR_BLACK) {
568 		hilites[c|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm"));
569 		Sprintf(hilites[c|BRIGHT], "\033[1;3%dm", c);
570 		if (iflags.wc2_newcolors || (c != CLR_GRAY)) {
571 			hilites[c] = (char *) alloc(sizeof("\033[0;3%dm"));
572 			Sprintf(hilites[c], "\033[0;3%dm", c);
573 		}
574 	}
575 }
576 #endif /* PC9800 */
577 
578 void
tty_start_screen()579 tty_start_screen()
580 {
581 	xputs(TI);
582 	xputs(VS);
583 #ifdef PC9800
584     if (!iflags.IBMgraphics && !iflags.DECgraphics)
585 	    tty_ascgraphics_hilite_fixup();
586     /* set up callback in case option is not set yet but toggled later */
587     ascgraphics_mode_callback = tty_ascgraphics_hilite_fixup;
588 # ifdef ASCIIGRAPH
589     if (iflags.IBMgraphics) init_hilite();
590     /* set up callback in case option is not set yet but toggled later */
591     ibmgraphics_mode_callback = init_hilite;
592 # endif
593 #endif /* PC9800 */
594 
595 #ifdef TERMLIB
596 	if (iflags.DECgraphics) tty_decgraphics_termcap_fixup();
597 	/* set up callback in case option is not set yet but toggled later */
598 	decgraphics_mode_callback = tty_decgraphics_termcap_fixup;
599 #endif
600 	if (iflags.num_pad) tty_number_pad(1);	/* make keypad send digits */
601 }
602 
603 void
tty_end_screen()604 tty_end_screen()
605 {
606 	clear_screen();
607 	xputs(VE);
608 	xputs(TE);
609 }
610 
611 /* Cursor movements */
612 
613 #endif /* OVLB */
614 
615 #ifdef OVL0
616 /* Note to OVLx tinkerers.  The placement of this overlay controls the location
617    of the function xputc().  This function is not currently in trampoli.[ch]
618    files for what is deemed to be performance reasons.  If this define is moved
619    and or xputc() is taken out of the ROOT overlay, then action must be taken
620    in trampoli.[ch]. */
621 
622 void
nocmov(x,y)623 nocmov(x, y)
624 int x,y;
625 {
626 	if ((int) ttyDisplay->cury > y) {
627 		if(UP) {
628 			while ((int) ttyDisplay->cury > y) {	/* Go up. */
629 				xputs(UP);
630 				ttyDisplay->cury--;
631 			}
632 		} else if(nh_CM) {
633 			cmov(x, y);
634 		} else if(HO) {
635 			home();
636 			tty_curs(BASE_WINDOW, x+1, y);
637 		} /* else impossible("..."); */
638 	} else if ((int) ttyDisplay->cury < y) {
639 		if(XD) {
640 			while((int) ttyDisplay->cury < y) {
641 				xputs(XD);
642 				ttyDisplay->cury++;
643 			}
644 		} else if(nh_CM) {
645 			cmov(x, y);
646 		} else {
647 			while((int) ttyDisplay->cury < y) {
648 				xputc('\n');
649 				ttyDisplay->curx = 0;
650 				ttyDisplay->cury++;
651 			}
652 		}
653 	}
654 	if ((int) ttyDisplay->curx < x) {		/* Go to the right. */
655 		if(!nh_ND) cmov(x, y); else	/* bah */
656 			/* should instead print what is there already */
657 		while ((int) ttyDisplay->curx < x) {
658 			xputs(nh_ND);
659 			ttyDisplay->curx++;
660 		}
661 	} else if ((int) ttyDisplay->curx > x) {
662 		while ((int) ttyDisplay->curx > x) {	/* Go to the left. */
663 			xputs(BC);
664 			ttyDisplay->curx--;
665 		}
666 	}
667 }
668 
669 void
cmov(x,y)670 cmov(x, y)
671 register int x, y;
672 {
673 	xputs(tgoto(nh_CM, x, y));
674 	ttyDisplay->cury = y;
675 	ttyDisplay->curx = x;
676 }
677 
678 /* See note at OVLx ifdef above.   xputc() is a special function. */
679 void
xputc(c)680 xputc(c)
681 #if defined(apollo)
682 int c;
683 #else
684 char c;
685 #endif
686 {
687 	(void) putchar(c);
688 }
689 
690 void
xputs(s)691 xputs(s)
692 const char *s;
693 {
694 # ifndef TERMLIB
695 	(void) fputs(s, stdout);
696 # else
697 #  if defined(NHSTDC) || defined(ULTRIX_PROTO)
698 	tputs(s, 1, (int (*)())xputc);
699 #  else
700 	tputs(s, 1, xputc);
701 #  endif
702 # endif
703 }
704 
705 void
cl_end()706 cl_end()
707 {
708 	if(CE)
709 		xputs(CE);
710 	else {	/* no-CE fix - free after Harold Rynes */
711 		/* this looks terrible, especially on a slow terminal
712 		   but is better than nothing */
713 		register int cx = ttyDisplay->curx+1;
714 
715 		while(cx < CO) {
716 			xputc(' ');
717 			cx++;
718 		}
719 		tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1,
720 						(int)ttyDisplay->cury);
721 	}
722 }
723 
724 #endif /* OVL0 */
725 #ifdef OVLB
726 
727 void
clear_screen()728 clear_screen()
729 {
730 	/* note: if CL is null, then termcap initialization failed,
731 		so don't attempt screen-oriented I/O during final cleanup.
732 	 */
733 	if (CL) {
734 		xputs(CL);
735 		home();
736 	}
737 }
738 
739 #endif /* OVLB */
740 #ifdef OVL0
741 
742 void
home()743 home()
744 {
745 	if(HO)
746 		xputs(HO);
747 	else if(nh_CM)
748 		xputs(tgoto(nh_CM, 0, 0));
749 	else
750 		tty_curs(BASE_WINDOW, 1, 0);	/* using UP ... */
751 	ttyDisplay->curx = ttyDisplay->cury = 0;
752 }
753 
754 void
standoutbeg()755 standoutbeg()
756 {
757 	if(SO) xputs(SO);
758 }
759 
760 void
standoutend()761 standoutend()
762 {
763 	if(SE) xputs(SE);
764 }
765 
766 #if 0	/* if you need one of these, uncomment it (here and in extern.h) */
767 void
768 revbeg()
769 {
770 	if(MR) xputs(MR);
771 }
772 
773 void
774 boldbeg()
775 {
776 	if(MD) xputs(MD);
777 }
778 
779 void
780 blinkbeg()
781 {
782 	if(MB) xputs(MB);
783 }
784 
785 void
786 dimbeg()
787 /* not in most termcap entries */
788 {
789 	if(MH) xputs(MH);
790 }
791 
792 void
793 m_end()
794 {
795 	if(ME) xputs(ME);
796 }
797 #endif
798 
799 #endif /* OVL0 */
800 #ifdef OVLB
801 
802 void
backsp()803 backsp()
804 {
805 	xputs(BC);
806 }
807 
808 void
tty_nhbell()809 tty_nhbell()
810 {
811 	if (flags.silent) return;
812 	(void) putchar('\007');		/* curx does not change */
813 	(void) fflush(stdout);
814 }
815 
816 #endif /* OVLB */
817 #ifdef OVL0
818 
819 #ifdef ASCIIGRAPH
820 void
graph_on()821 graph_on() {
822 	if (AS) xputs(AS);
823 }
824 
825 void
graph_off()826 graph_off() {
827 	if (AE) xputs(AE);
828 }
829 #endif
830 
831 #endif /* OVL0 */
832 #ifdef OVL1
833 
834 #if !defined(MICRO)
835 # ifdef VMS
836 static const short tmspc10[] = {		/* from termcap */
837 	0, 2000, 1333, 909, 743, 666, 333, 166, 83, 55, 50, 41, 27, 20, 13, 10,
838 	5
839 };
840 # else
841 static const short tmspc10[] = {		/* from termcap */
842 	0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
843 };
844 # endif
845 #endif
846 
847 /* delay 50 ms */
848 void
tty_delay_output()849 tty_delay_output()
850 {
851 #if defined(MICRO)
852 	register int i;
853 #endif
854 #ifdef TIMED_DELAY
855 	if (flags.nap) {
856 		(void) fflush(stdout);
857 		msleep(50);		/* sleep for 50 milliseconds */
858 		return;
859 	}
860 #endif
861 #if defined(MICRO)
862 	/* simulate the delay with "cursor here" */
863 	for (i = 0; i < 3; i++) {
864 		cmov(ttyDisplay->curx, ttyDisplay->cury);
865 		(void) fflush(stdout);
866 	}
867 #else /* MICRO */
868 	/* BUG: if the padding character is visible, as it is on the 5620
869 	   then this looks terrible. */
870 	if(flags.null)
871 # ifdef TERMINFO
872 		/* cbosgd!cbcephus!pds for SYS V R2 */
873 #  ifdef NHSTDC
874 		tputs("$<50>", 1, (int (*)())xputc);
875 #  else
876 		tputs("$<50>", 1, xputc);
877 #  endif
878 # else
879 #  if defined(NHSTDC) || defined(ULTRIX_PROTO)
880 		tputs("50", 1, (int (*)())xputc);
881 #  else
882 		tputs("50", 1, xputc);
883 #  endif
884 # endif
885 
886 	else if(ospeed > 0 && ospeed < SIZE(tmspc10) && nh_CM) {
887 		/* delay by sending cm(here) an appropriate number of times */
888 		register int cmlen = strlen(tgoto(nh_CM, ttyDisplay->curx,
889 							ttyDisplay->cury));
890 		register int i = 500 + tmspc10[ospeed]/2;
891 
892 		while(i > 0) {
893 			cmov((int)ttyDisplay->curx, (int)ttyDisplay->cury);
894 			i -= cmlen*tmspc10[ospeed];
895 		}
896 	}
897 #endif /* MICRO */
898 }
899 
900 #endif /* OVL1 */
901 #ifdef OVLB
902 
903 void
cl_eos()904 cl_eos()			/* free after Robert Viduya */
905 {				/* must only be called with curx = 1 */
906 
907 	if(nh_CD)
908 		xputs(nh_CD);
909 	else {
910 		register int cy = ttyDisplay->cury+1;
911 		while(cy <= LI-2) {
912 			cl_end();
913 			xputc('\n');
914 			cy++;
915 		}
916 		cl_end();
917 		tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1,
918 						(int)ttyDisplay->cury);
919 	}
920 }
921 
922 #if defined(TEXTCOLOR) && defined(TERMLIB)
923 # if defined(UNIX) && defined(TERMINFO)
924 /*
925  * Sets up color highlighting, using terminfo(4) escape sequences.
926  *
927  * Having never seen a terminfo system without curses, we assume this
928  * inclusion is safe.  On systems with color terminfo, it should define
929  * the 8 COLOR_FOOs, and avoid us having to guess whether this particular
930  * terminfo uses BGR or RGB for its indexes.
931  *
932  * If we don't get the definitions, then guess.  Original color terminfos
933  * used BGR for the original Sf (setf, Standard foreground) codes, but
934  * there was a near-total lack of user documentation, so some subsequent
935  * terminfos, such as early Linux ncurses and SCO UNIX, used RGB.  Possibly
936  * as a result of the confusion, AF (setaf, ANSI Foreground) codes were
937  * introduced, but this caused yet more confusion.  Later Linux ncurses
938  * have BGR Sf, RGB AF, and RGB COLOR_FOO, which appears to be the SVR4
939  * standard.  We could switch the colors around when using Sf with ncurses,
940  * which would help things on later ncurses and hurt things on early ncurses.
941  * We'll try just preferring AF and hoping it always agrees with COLOR_FOO,
942  * and falling back to Sf if AF isn't defined.
943  *
944  * In any case, treat black specially so we don't try to display black
945  * characters on the assumed black background.
946  */
947 
948 	/* `curses' is aptly named; various versions don't like these
949 	    macros used elsewhere within nethack; fortunately they're
950 	    not needed beyond this point, so we don't need to worry
951 	    about reconstructing them after the header file inclusion. */
952 #undef delay_output
953 #undef TRUE
954 #undef FALSE
955 #define m_move curses_m_move	/* Some curses.h decl m_move(), not used here */
956 
957 #include <curses.h>
958 
959 #ifndef HAVE_TPARM
960 extern char *tparm();
961 #endif
962 
963 #  ifdef COLOR_BLACK	/* trust include file */
964 #ifndef VIDEOSHADES
965 #undef COLOR_BLACK
966 #endif
967 #  else
968 #   ifndef _M_UNIX	/* guess BGR */
969 #ifdef VIDEOSHADES
970 #define COLOR_BLACK   0
971 #endif
972 #define COLOR_BLUE    1
973 #define COLOR_GREEN   2
974 #define COLOR_CYAN    3
975 #define COLOR_RED     4
976 #define COLOR_MAGENTA 5
977 #define COLOR_YELLOW  6
978 #define COLOR_WHITE   7
979 #   else		/* guess RGB */
980 #define COLOR_RED     1
981 #define COLOR_GREEN   2
982 #define COLOR_YELLOW  3
983 #define COLOR_BLUE    4
984 #define COLOR_MAGENTA 5
985 #define COLOR_CYAN    6
986 #define COLOR_WHITE   7
987 #   endif
988 #  endif
989 #ifndef VIDEOSHADES
990 #define COLOR_BLACK COLOR_BLUE
991 #endif
992 
993 const int ti_map[8] = {
994 	COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW,
995 	COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE };
996 
997 static void
init_hilite()998 init_hilite()
999 {
1000 	register int c;
1001 	char *setf, *scratch;
1002 
1003 	for (c = 0; c < SIZE(hilites); c++)
1004 		hilites[c] = nh_HI;
1005 	hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0;
1006 
1007 	if (tgetnum("Co") < 8
1008 	    || ((setf = tgetstr("AF", (char **)0)) == (char *)0
1009 		 && (setf = tgetstr("Sf", (char **)0)) == (char *)0))
1010 		return;
1011 
1012 	for (c = 0; c < CLR_MAX / 2; c++) {
1013 	    scratch = tparm(setf, ti_map[c]);
1014 	    if (iflags.wc2_newcolors || (c != CLR_GRAY)) {
1015 		hilites[c] = (char *) alloc(strlen(scratch) + 1);
1016 		Strcpy(hilites[c], scratch);
1017 	    }
1018 	    if (c != CLR_BLACK) {
1019 		hilites[c|BRIGHT] = (char*) alloc(strlen(scratch)+strlen(MD)+1);
1020 		Strcpy(hilites[c|BRIGHT], MD);
1021 		Strcat(hilites[c|BRIGHT], scratch);
1022 	    }
1023 
1024 	}
1025 	if (!iflags.wc2_newcolors)
1026 		hilites[CLR_BLACK] = hilites[CLR_BLUE];
1027 }
1028 
1029 # else /* UNIX && TERMINFO */
1030 
1031 #  ifndef TOS
1032 /* find the foreground and background colors set by nh_HI or nh_HE */
1033 static void
analyze_seq(str,fg,bg)1034 analyze_seq (str, fg, bg)
1035 char *str;
1036 int *fg, *bg;
1037 {
1038 	register int c, code;
1039 	int len;
1040 
1041 #   ifdef MICRO
1042 	*fg = CLR_GRAY; *bg = CLR_BLACK;
1043 #   else
1044 	*fg = *bg = NO_COLOR;
1045 #   endif
1046 
1047 	c = (str[0] == '\233') ? 1 : 2;	 /* index of char beyond esc prefix */
1048 	len = strlen(str) - 1;		 /* length excluding attrib suffix */
1049 	if ((c != 1 && (str[0] != '\033' || str[1] != '[')) ||
1050 	    (len - c) < 1 || str[len] != 'm')
1051 		return;
1052 
1053 	while (c < len) {
1054 	    if ((code = atoi(&str[c])) == 0) { /* reset */
1055 		/* this also catches errors */
1056 #   ifdef MICRO
1057 		*fg = CLR_GRAY; *bg = CLR_BLACK;
1058 #   else
1059 		*fg = *bg = NO_COLOR;
1060 #   endif
1061 	    } else if (code == 1) { /* bold */
1062 		*fg |= BRIGHT;
1063 #   if 0
1064 	/* I doubt we'll ever resort to using blinking characters,
1065 	   unless we want a pulsing glow for something.  But, in case
1066 	   we do... - 3. */
1067 	    } else if (code == 5) { /* blinking */
1068 		*fg |= BLINK;
1069 	    } else if (code == 25) { /* stop blinking */
1070 		*fg &= ~BLINK;
1071 #   endif
1072 	    } else if (code == 7 || code == 27) { /* reverse */
1073 		code = *fg & ~BRIGHT;
1074 		*fg = *bg | (*fg & BRIGHT);
1075 		*bg = code;
1076 	    } else if (code >= 30 && code <= 37) { /* hi_foreground RGB */
1077 		*fg = code - 30;
1078 	    } else if (code >= 40 && code <= 47) { /* hi_background RGB */
1079 		*bg = code - 40;
1080 	    }
1081 	    while (digit(str[++c]));
1082 	    c++;
1083 	}
1084 }
1085 #  endif
1086 
1087 /*
1088  * Sets up highlighting sequences, using ANSI escape sequences (highlight code
1089  * found in print.c).  The nh_HI and nh_HE sequences (usually from SO) are
1090  * scanned to find foreground and background colors.
1091  */
1092 
1093 static void
init_hilite()1094 init_hilite()
1095 {
1096 	register int c;
1097 #  ifdef TOS
1098 	extern unsigned long tos_numcolors;	/* in tos.c */
1099 	static char NOCOL[] = "\033b0", COLHE[] = "\033q\033b0";
1100 
1101 	if (tos_numcolors <= 2) {
1102 		return;
1103 	}
1104 /* Under TOS, the "bright" and "dim" colors are reversed. Moreover,
1105  * on the Falcon the dim colors are *really* dim; so we make most
1106  * of the colors the bright versions, with a few exceptions where
1107  * the dim ones look OK.
1108  */
1109 	hilites[0] = NOCOL;
1110 	for (c = 1; c < SIZE(hilites); c++) {
1111 		char *foo;
1112 		foo = (char *) alloc(sizeof("\033b0"));
1113 		if (tos_numcolors > 4)
1114 			Sprintf(foo, "\033b%c", (c&~BRIGHT)+'0');
1115 		else
1116 			Strcpy(foo, "\033b0");
1117 		hilites[c] = foo;
1118 	}
1119 
1120 	if (tos_numcolors == 4) {
1121 		TI = "\033b0\033c3\033E\033e";
1122 		TE = "\033b3\033c0\033J";
1123 		nh_HE = COLHE;
1124 		hilites[CLR_GREEN] = hilites[CLR_GREEN|BRIGHT] = "\033b2";
1125 		hilites[CLR_RED] = hilites[CLR_RED|BRIGHT] = "\033b1";
1126 	} else {
1127 		sprintf(hilites[CLR_BROWN], "\033b%c", (CLR_BROWN^BRIGHT)+'0');
1128 		sprintf(hilites[CLR_GREEN], "\033b%c", (CLR_GREEN^BRIGHT)+'0');
1129 
1130 		TI = "\033b0\033c\017\033E\033e";
1131 		TE = "\033b\017\033c0\033J";
1132 		nh_HE = COLHE;
1133 		hilites[CLR_WHITE] = hilites[CLR_BLACK] = NOCOL;
1134 		hilites[NO_COLOR] = hilites[CLR_GRAY];
1135 	}
1136 
1137 #  else /* TOS */
1138 
1139 	int backg, foreg, hi_backg, hi_foreg;
1140 
1141 	for (c = 0; c < SIZE(hilites); c++)
1142 	    hilites[c] = nh_HI;
1143 	hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0;
1144 
1145 	analyze_seq(nh_HI, &hi_foreg, &hi_backg);
1146 	analyze_seq(nh_HE, &foreg, &backg);
1147 
1148 	for (c = 0; c < SIZE(hilites); c++)
1149 	    /* avoid invisibility */
1150 	    if ((backg & ~BRIGHT) != c) {
1151 #   ifdef MICRO
1152 		if (c == CLR_BLUE) continue;
1153 #   endif
1154 		if (c == foreg)
1155 		    hilites[c] = (char *)0;
1156 		else if (c != hi_foreg || backg != hi_backg) {
1157 		    hilites[c] = (char *) alloc(sizeof("\033[%d;3%d;4%dm"));
1158 		    Sprintf(hilites[c], "\033[%d", !!(c & BRIGHT));
1159 		    if ((c | BRIGHT) != (foreg | BRIGHT))
1160 			Sprintf(eos(hilites[c]), ";3%d", c & ~BRIGHT);
1161 		    if (backg != CLR_BLACK)
1162 			Sprintf(eos(hilites[c]), ";4%d", backg & ~BRIGHT);
1163 		    Strcat(hilites[c], "m");
1164 		}
1165 	    }
1166 
1167 #   ifdef MICRO
1168 	/* brighten low-visibility colors */
1169 	hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT];
1170 #   endif
1171 #  endif /* TOS */
1172 }
1173 # endif /* UNIX */
1174 
1175 static void
kill_hilite()1176 kill_hilite()
1177 {
1178 # ifndef TOS
1179 	register int c;
1180 
1181 	for (c = 0; c < CLR_MAX / 2; c++) {
1182 		if ((!iflags.wc2_newcolors) &&
1183 		    (c == CLR_BLUE || c == CLR_GRAY)) continue;
1184 
1185 		if (hilites[c|BRIGHT] == hilites[c])  hilites[c|BRIGHT] = 0;
1186 		if (hilites[c] && (hilites[c] != nh_HI))
1187 			free((genericptr_t) hilites[c]),  hilites[c] = 0;
1188 		if (hilites[c|BRIGHT] && (hilites[c|BRIGHT] != nh_HI))
1189 			free((genericptr_t) hilites[c|BRIGHT]),  hilites[c|BRIGHT] = 0;
1190 	}
1191 # endif
1192 	return;
1193 }
1194 #endif /* TEXTCOLOR */
1195 
1196 
1197 static char nulstr[] = "";
1198 
1199 static char *
s_atr2str(n)1200 s_atr2str(n)
1201 int n;
1202 {
1203     switch (n) {
1204 	    case ATR_ULINE:
1205 		    if(nh_US) return nh_US;
1206 	    case ATR_BOLD:
1207 	    case ATR_BLINK:
1208 #if defined(TERMLIB) && defined(TEXTCOLOR)
1209 		    if (MD) return MD;
1210 #endif
1211 		    return nh_HI;
1212 	    case ATR_INVERSE:
1213 		    return MR;
1214     }
1215     return nulstr;
1216 }
1217 
1218 static char *
e_atr2str(n)1219 e_atr2str(n)
1220 int n;
1221 {
1222     switch (n) {
1223 	    case ATR_ULINE:
1224 		    if(nh_UE) return nh_UE;
1225 	    case ATR_BOLD:
1226 	    case ATR_BLINK:
1227 		    return nh_HE;
1228 	    case ATR_INVERSE:
1229 		    return ME;
1230     }
1231     return nulstr;
1232 }
1233 
1234 
1235 void
term_start_attr(attr)1236 term_start_attr(attr)
1237 int attr;
1238 {
1239 	if (attr) {
1240 		xputs(s_atr2str(attr));
1241 	}
1242 }
1243 
1244 
1245 void
term_end_attr(attr)1246 term_end_attr(attr)
1247 int attr;
1248 {
1249 	if(attr) {
1250 		xputs(e_atr2str(attr));
1251 	}
1252 }
1253 
1254 
1255 void
term_start_raw_bold()1256 term_start_raw_bold()
1257 {
1258 	xputs(nh_HI);
1259 }
1260 
1261 
1262 void
term_end_raw_bold()1263 term_end_raw_bold()
1264 {
1265 	xputs(nh_HE);
1266 }
1267 
1268 
1269 #ifdef TEXTCOLOR
1270 
1271 void
term_end_color()1272 term_end_color()
1273 {
1274 	xputs(nh_HE);
1275 }
1276 
1277 
1278 void
term_start_color(color)1279 term_start_color(color)
1280 int color;
1281 {
1282 	if (iflags.wc2_newcolors)
1283 		xputs(hilites[ttycolors[color]]);
1284 	else
1285 		xputs(hilites[color]);
1286 }
1287 
1288 
1289 int
has_color(color)1290 has_color(color)
1291 int color;
1292 {
1293 #ifdef X11_GRAPHICS
1294 	/* XXX has_color() should be added to windowprocs */
1295 	if (windowprocs.name != NULL &&
1296 	    !strcmpi(windowprocs.name, "X11")) return TRUE;
1297 #endif
1298 #ifdef GEM_GRAPHICS
1299 	/* XXX has_color() should be added to windowprocs */
1300 	if (windowprocs.name != NULL &&
1301 	    !strcmpi(windowprocs.name, "Gem")) return TRUE;
1302 #endif
1303 #ifdef QT_GRAPHICS
1304 	/* XXX has_color() should be added to windowprocs */
1305 	if (windowprocs.name != NULL &&
1306 	    !strcmpi(windowprocs.name, "Qt")) return TRUE;
1307 #endif
1308 #ifdef AMII_GRAPHICS
1309 	/* hilites[] not used */
1310 	return iflags.use_color;
1311 #endif
1312 #ifdef CURSES_GRAPHICS
1313     /* XXX has_color() should be added to windowprocs */
1314     /* iflags.wc_color is set to false and the option disabled if the
1315      terminal cannot display color */
1316     if (windowprocs.name != NULL &&
1317      !strcmpi(windowprocs.name, "curses")) return iflags.wc_color;
1318 #endif
1319 	return hilites[color] != (char *)0;
1320 }
1321 
1322 #endif /* TEXTCOLOR */
1323 
1324 #endif /* OVLB */
1325 
1326 #endif /* TTY_GRAPHICS */
1327 
1328 /*termcap.c*/
1329