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