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