1 /* $LynxId: LYCurses.c,v 1.174 2013/07/21 00:40:06 tom Exp $ */
2 #include <HTUtils.h>
3 #include <HTAlert.h>
4 
5 #ifdef __MINGW32__
6 #ifdef UNIX
7 #undef UNIX
8 #endif /* UNIX */
9 #endif /* __MINGW32__ */
10 
11 #ifdef __DJGPP__
12 #include <pc.h>
13 #endif /* __DJGPP__ */
14 
15 #include <LYCurses.h>
16 #include <LYStyle.h>
17 #include <LYUtils.h>
18 #include <LYGlobalDefs.h>
19 #include <LYSignal.h>
20 #include <LYClean.h>
21 #include <LYReadCFG.h>
22 #include <LYStrings.h>
23 #include <LYCharSets.h>
24 #include <UCAux.h>
25 #include <HTFont.h>
26 
27 #include <LYexit.h>
28 #include <LYLeaks.h>
29 
30 #ifdef VMS
31 #include <LYMainLoop.h>
32 #endif
33 
34 #if defined(VMS) && defined(__GNUC__)
35 #include <gnu_hacks.h>
36 #undef LINES
37 #undef COLS
38 #define LINES lines
39 #define COLS cols
40 extern int _NOSHARE(LINES);
41 extern int _NOSHARE(COLS);
42 #endif /* VMS && __GNUC__ */
43 
44 #ifdef USE_COLOR_STYLE
45 #include <AttrList.h>
46 #include <LYHash.h>
47 #endif
48 
49 #ifdef NEED_WCHAR_H
50 #include <wchar.h>
51 #endif
52 
53 #if defined(COLOR_CURSES)
54 int lynx_has_color = FALSE;
55 #endif
56 
57 #ifdef HAVE_XCURSES
58 char *XCursesProgramName = "Lynx";
59 #endif
60 
61 #if defined(USE_COLOR_STYLE) && !defined(USE_COLOR_TABLE)
62 #define COLOR_BKGD ((s_normal != NOSTYLE) ? hashStyles[s_normal].color : A_NORMAL)
63 #else
64 #define COLOR_BKGD ((COLOR_PAIRS >= 9) ? (chtype) get_color_pair(9) : A_NORMAL)
65 #endif
66 
67 #ifdef USE_CURSES_PADS
68 WINDOW *LYwin = 0;
69 int LYshiftWin = 0;
70 int LYwideLines = FALSE;
71 int LYtableCols = 0;		/* in 1/12 of screen width */
72 BOOLEAN LYuseCursesPads = TRUE;	/* use pads for left/right shifting */
73 #endif
74 
75 /*
76  * These are routines to start and stop curses and to cleanup the screen at the
77  * end.
78  */
79 
80 static int dumbterm(char *terminal);
81 BOOLEAN LYCursesON = FALSE;
82 
83 #if defined(USE_BLINK) && defined(__EMX__)
84 static void make_blink_boldbg(void);
85 #endif
86 
87 #if defined(USE_COLOR_TABLE) || defined(USE_SLANG)
88 int Current_Attr;
89 static int Masked_Attr;
90 #endif
91 
92 #ifdef USE_SLANG
93 unsigned Lynx_Color_Flags = 0;
94 BOOLEAN FullRefresh = FALSE;
95 int curscr = 0;
96 
97 #ifdef SLANG_MBCS_HACK
98 /*
99  * Will be set by size_change.  - KW
100  */
101 int PHYSICAL_SLtt_Screen_Cols = 10;
102 #endif /* SLANG_MBCS_HACK */
103 
LY_SLrefresh(void)104 void LY_SLrefresh(void)
105 {
106     if (FullRefresh) {
107 	SLsmg_suspend_smg();
108 	SLsmg_resume_smg();
109 	FullRefresh = FALSE;
110     } else {
111 	SLsmg_refresh();
112     }
113 
114     return;
115 }
116 
117 /* the following renamed from LY_SLclear since it is more like erase()
118    described in curses man pages than like clear(); but for USE_SLANG
119    clear() is still a macro calling this, and will do the same thing as
120    erase(). - kw */
LY_SLerase(void)121 void LY_SLerase(void)
122 {
123     SLsmg_gotorc(0, 0);
124     SLsmg_erase_eos();
125 }
126 
127 #ifdef VMS
VTHome(void)128 void VTHome(void)
129 {
130     printf("\033[;H");
131 
132     return;
133 }
134 #endif /* VMS */
135 
LYaddAttr(int a)136 void LYaddAttr(int a)
137 {
138     Current_Attr |= a;
139     SLsmg_set_color((SLsmg_Color_Type) (Current_Attr & ~Masked_Attr));
140 }
141 
LYsubAttr(int a)142 void LYsubAttr(int a)
143 {
144     Current_Attr &= ~a;
145     SLsmg_set_color((SLsmg_Color_Type) (Current_Attr & ~Masked_Attr));
146 }
147 
lynx_setup_attrs(void)148 static void lynx_setup_attrs(void)
149 {
150     static int monoattr[] =
151     {
152 	0,
153 	SLTT_BOLD_MASK,
154 	SLTT_REV_MASK,
155 	SLTT_REV_MASK | SLTT_BOLD_MASK,
156 	SLTT_ULINE_MASK,
157 	SLTT_ULINE_MASK | SLTT_BOLD_MASK,
158 	SLTT_ULINE_MASK | SLTT_REV_MASK,
159 	SLTT_ULINE_MASK | SLTT_BOLD_MASK | SLTT_REV_MASK
160     };
161     int n;
162 
163     for (n = 1; n <= 7; n++)
164 	SLtt_set_mono(n, NULL, (SLtt_Char_Type) (monoattr[n] & ~Masked_Attr));
165 }
166 
lynx_setup_colors(void)167 void lynx_setup_colors(void)
168 {
169     CTRACE((tfp, "lynx_setup_colors\n"));
170     SLtt_set_color(0, NULL, DEFAULT_FG, DEFAULT_BG);
171     SLtt_set_color(1, NULL, "blue", DEFAULT_BG);	/* bold */
172     SLtt_set_color(2, NULL, "yellow", "blue");	/* reverse */
173     SLtt_set_color(4, NULL, "magenta", DEFAULT_BG);	/* underline */
174     /*
175      * The other objects are '|'ed together to get rest.
176      */
177     SLtt_set_color(3, NULL, "green", DEFAULT_BG);	/* bold-reverse */
178     SLtt_set_color(5, NULL, "blue", DEFAULT_BG);	/* bold-underline */
179     SLtt_set_color(6, NULL, "red", DEFAULT_BG);		/* reverse-underline */
180     SLtt_set_color(7, NULL, "magenta", "cyan");		/* reverse-underline-bold */
181     /*
182      * Now set monochrome attributes.
183      */
184     lynx_setup_attrs();
185 }
186 
sl_suspend(int sig)187 static void sl_suspend(int sig)
188 {
189 #ifdef SIGSTOP
190 #ifndef VMS
191     int r, c;
192 
193     lynx_enable_mouse(0);
194     if (sig == SIGTSTP)
195 	SLsmg_suspend_smg();
196     SLang_reset_tty();
197     kill(getpid(), SIGSTOP);
198 #if SLANG_VERSION > 9929
199     SLang_init_tty(-1, 0, 1);
200 #else
201     SLang_init_tty(3, 0, 1);
202 #endif /* SLANG_VERSION > 9929 */
203     signal(SIGTSTP, sl_suspend);
204 #if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
205     SLtty_set_suspend_state(1);
206 #endif
207     if (sig == SIGTSTP)
208 	SLsmg_resume_smg();
209     /*
210      * Get new window size in case it changed.
211      */
212     r = SLtt_Screen_Rows;
213     c = SLtt_Screen_Cols;
214     size_change(0);
215     if ((r != SLtt_Screen_Rows) || (c != SLtt_Screen_Cols)) {
216 	recent_sizechange = TRUE;
217     }
218     lynx_enable_mouse(1);
219 #endif /* !VMS */
220 #endif /* SIGSTOP */
221     return;
222 }
223 #else
224 
225 #ifdef FANCY_CURSES
226 
227 #ifndef VMS
228 /* *INDENT-OFF* */
229 /* definitions for the mono attributes we can use */
230 static struct {
231     const char *name;
232     int code;
233 } Mono_Attrs[7] =
234 {
235     { "normal",		A_NORMAL },
236     { "bold",		A_BOLD },
237     { "reverse",	A_REVERSE },
238     { "underline",	A_UNDERLINE },
239     { "standout",	A_STANDOUT },
240     { "blink",		A_BLINK },
241     { "dim",		A_DIM },
242 };
243 /* *INDENT-ON* */
244 
string_to_attr(const char * name)245 int string_to_attr(const char *name)
246 {
247     unsigned i;
248 
249     for (i = 0; i < TABLESIZE(Mono_Attrs); i++) {
250 	if (!strcasecomp(Mono_Attrs[i].name, name)) {
251 	    return Mono_Attrs[i].code;
252 	}
253     }
254     return 0;
255 }
256 #endif /* VMS */
257 
258 #ifdef USE_COLOR_STYLE
attr_to_string(int code)259 static char *attr_to_string(int code)
260 {
261     static char *result;
262 
263     if (code >= 0) {
264 	unsigned i;
265 	int pair = PAIR_NUMBER((unsigned) code);
266 	int bold = (pair != 0 && ((unsigned) code & A_BOLD) != 0);
267 
268 	StrAllocCopy(result, "");
269 
270 	if (bold)
271 	    code &= (int) ~A_BOLD;
272 
273 	for (i = 0; i < TABLESIZE(Mono_Attrs); i++) {
274 	    if (Mono_Attrs[i].code & code) {
275 		if (non_empty(result))
276 		    StrAllocCat(result, "+");
277 		StrAllocCat(result, Mono_Attrs[i].name);
278 	    }
279 	}
280 	if (pair != 0) {
281 	    short f, b;
282 
283 	    if (pair_content((short) pair, &f, &b) != ERR) {
284 		if (non_empty(result))
285 		    StrAllocCat(result, "+");
286 		StrAllocCat(result, lookup_color(bold ? f + COLORS : f));
287 		StrAllocCat(result, "/");
288 		StrAllocCat(result, lookup_color(b));
289 	    }
290 	}
291     } else {
292 	FREE(result);
293     }
294     return result;
295 }
296 #endif /* USE_COLOR_STYLE */
297 #endif /* FANCY_CURSES */
298 #endif /* USE_SLANG */
299 
300 /*
301  *  This function boxes windows for (n)curses.
302  */
LYbox(WINDOW * win,int formfield GCC_UNUSED)303 void LYbox(WINDOW * win, int formfield GCC_UNUSED)
304 {
305 #ifdef USE_SLANG
306     SLsmg_draw_box(win->top_y,
307 		   win->left_x,
308 		   (unsigned) win->height,
309 		   (unsigned) win->width + 4);
310 #else
311 #ifdef VMS
312     /*
313      * This should work for VAX-C and DEC-C, since they both have the same
314      * win._max_y and win._max_x members -TD
315      *
316      * (originally VMSbox by FM)
317      */
318     int i;
319 
320     wmove(win, 0, 0);
321     waddstr(win, "\033)0\016l");
322     for (i = 1; i < win->_max_x; i++)
323 	waddch(win, 'q');
324     waddch(win, 'k');
325     for (i = 1; i < win->_max_y - 1; i++) {
326 	wmove(win, i, 0);
327 	waddch(win, 'x');
328 	wmove(win, i, win->_max_x - 1);
329 	waddch(win, 'x');
330     }
331     wmove(win, i, 0);
332     waddch(win, 'm');
333     for (i = 1; i < win->_max_x; i++)
334 	waddch(win, 'q');
335     waddstr(win, "j\017");
336 #else /* !VMS */
337     int boxvert, boxhori;
338 
339     UCSetBoxChars(current_char_set, &boxvert, &boxhori, BOXVERT, BOXHORI);
340 #ifdef CSS
341     if (formfield)
342 	wcurses_css(win, "frame", ABS_ON);
343 #endif
344     /*
345      * If we don't have explicitly specified characters for either vertical or
346      * horizontal lines, the characters that box() would use for the corners
347      * probably also won't work well.  So we specify our own ASCII characters
348      * for the corners and call wborder() instead of box().  - kw
349      */
350     LynxWChangeStyle(win, s_menu_frame, STACK_ON);
351 #ifdef HAVE_WBORDER
352     if (!boxvert || !boxhori) {
353 	box(win,
354 	    (chtype) boxvert,
355 	    (chtype) boxhori);
356     } else if (boxvert == '*' || boxhori == '*') {
357 	wborder(win,
358 		(chtype) boxvert,
359 		(chtype) boxvert,
360 		(chtype) boxhori,
361 		(chtype) boxhori,
362 		'*', '*', '*', '*');
363     } else {
364 	wborder(win,
365 		(chtype) boxvert,
366 		(chtype) boxvert,
367 		(chtype) boxhori,
368 		(chtype) boxhori,
369 		'/', '\\', '\\', '/');
370     }
371 #else
372     box(win, boxvert, boxhori);
373 #endif
374     LynxWChangeStyle(win, s_menu_frame, STACK_OFF);
375 #ifdef CSS
376     if (formfield)
377 	wcurses_css(win, "frame", ABS_OFF);
378 #endif
379 #endif /* VMS */
380     wrefresh(win);
381 #endif /* USE_SLANG */
382 }
383 
384 #if defined(USE_COLOR_STYLE)
385 /* Ok, explanation of the USE_COLOR_STYLE styles.  The basic styles (ie non
386  * HTML) are set the same as the SLANG version for ease of programming.  The
387  * other styles are simply the HTML enum from HTMLDTD.h + 16.
388  */
389 HTCharStyle displayStyles[DSTYLE_ELEMENTS];
390 
391 /*
392  * set a style's attributes - RP
393  */
setStyle(int style,int color,int cattr,int mono)394 void setStyle(int style,
395 	      int color,
396 	      int cattr,
397 	      int mono)
398 {
399     displayStyles[style].color = color;
400     displayStyles[style].cattr = cattr;
401     displayStyles[style].mono = mono;
402 }
403 
setHashStyle(int style,int color,int cattr,int mono,const char * element)404 void setHashStyle(int style,
405 		  int color,
406 		  int cattr,
407 		  int mono,
408 		  const char *element)
409 {
410     bucket *ds = &hashStyles[style];
411 
412     CTRACE2(TRACE_STYLE,
413 	    (tfp, "CSS(SET): <%s> hash=%d, ca=%#x, ma=%#x\n",
414 	     element, style, color, mono));
415 
416     ds->color = color;
417     ds->cattr = cattr;
418     ds->mono = mono;
419     ds->code = style;
420     FREE(ds->name);
421     StrAllocCopy(ds->name, element);
422 }
423 
424 /*
425  * set the curses attributes to be color or mono - RP
426  */
LYAttrset(WINDOW * win,int color,int mono)427 static void LYAttrset(WINDOW * win, int color,
428 		      int mono)
429 {
430     char *shown = NULL;
431 
432     if (lynx_has_color
433 	&& LYShowColor >= SHOW_COLOR_ON
434 	&& color >= 0) {
435 	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset color %#x -> (%s)\n",
436 			      color, shown = attr_to_string(color)));
437 	(void) wattrset(win, (unsigned) color);
438     } else if (mono >= 0) {
439 	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset mono %#x -> (%s)\n",
440 			      mono, shown = attr_to_string(mono)));
441 	(void) wattrset(win, (unsigned) mono);
442     } else {
443 	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset (A_NORMAL)\n"));
444 	(void) wattrset(win, A_NORMAL);
445     }
446     if (shown != NULL)
447 	(void) attr_to_string(-1);
448 }
449 
curses_w_style(WINDOW * win,int style,int dir)450 void curses_w_style(WINDOW * win, int style,
451 		    int dir)
452 {
453 #if OMIT_SCN_KEEPING
454 # define SPECIAL_STYLE /*(CSHASHSIZE+1) */ 88888
455 /* if TRACEs are not compiled in, this macro is redundant - we needn't valid
456 'ds' to stack off. */
457 #endif
458 
459     int YP, XP;
460     bucket *ds;
461     BOOL free_ds = TRUE;
462 
463     switch (style) {
464 #if OMIT_SCN_KEEPING
465     case SPECIAL_STYLE:
466 	ds = special_bucket();
467 	break;
468 #endif
469     case NOSTYLE:
470 	ds = nostyle_bucket();
471 	break;
472     default:
473 	ds = &hashStyles[style];
474 	free_ds = FALSE;
475 	break;
476     }
477 
478     if (!ds->name) {
479 	CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:Style %d not configured\n", style));
480 #if !OMIT_SCN_KEEPING
481 	if (free_ds)
482 	    free(ds);
483 	return;
484 #endif
485     }
486 
487     CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:<%s%s> style %d code %#x, color %#x\n",
488 			  (dir ? "" : "/"),
489 			  ds->name, style, ds->code, ds->color));
490 
491     getyx(win, YP, XP);
492 
493     if (style == s_normal && dir) {
494 	LYAttrset(win, ds->color, ds->mono);
495 	if (win == LYwin)
496 	    SetCachedStyle(YP, XP, (unsigned) s_normal);
497 	if (free_ds)
498 	    free(ds);
499 	return;
500     }
501 
502     switch (dir) {
503 	/* ABS_OFF is the same as STACK_OFF for the moment */
504     case STACK_OFF:
505 	if (last_colorattr_ptr) {
506 	    int last_attr = last_styles[--last_colorattr_ptr];
507 
508 	    LYAttrset(win, last_attr, last_attr);
509 	} else
510 	    LYAttrset(win, A_NORMAL, -1);
511 	break;
512 
513     case STACK_ON:		/* remember the current attributes */
514 	if (last_colorattr_ptr >= MAX_LAST_STYLES) {
515 	    CTRACE2(TRACE_STYLE, (tfp, "........... %s (0x%x) %s\r\n",
516 				  "attribute cache FULL, dropping last",
517 				  last_styles[last_colorattr_ptr],
518 				  "in LynxChangeStyle(curses_w_style)"));
519 	    last_colorattr_ptr = MAX_LAST_STYLES - 1;
520 	}
521 	last_styles[last_colorattr_ptr++] = (int) LYgetattrs(win);
522 	/* don't cache style changes for active links */
523 #if OMIT_SCN_KEEPING
524 	/* since we don't compute the hcode to stack off in HTML.c, we
525 	 * don't know whether this style is configured.  So, we
526 	 * shouldn't simply return on stacking on unconfigured
527 	 * styles, we should push curr attrs on stack.  -HV
528 	 */
529 	if (!ds->name)
530 	    break;
531 #endif
532 	/* FALL THROUGH */
533     case ABS_ON:		/* change without remembering the previous style */
534 	/* don't cache style changes for active links and edits */
535 	if (style != s_alink
536 	    && style != s_curedit
537 	    && style != s_aedit
538 	    && style != s_aedit_sel
539 	    && style != s_aedit_pad
540 	    && style != s_aedit_arr) {
541 	    CTRACE2(TRACE_STYLE, (tfp, "CACHED: <%s> @(%d,%d)\n",
542 				  ds->name, YP, XP));
543 	    if (win == LYwin)
544 		SetCachedStyle(YP, XP, (unsigned) style);
545 	}
546 	LYAttrset(win, ds->color, ds->mono);
547 	break;
548     }
549 
550     if (free_ds)
551 	free(ds);
552 
553     return;
554 }
555 
556 /*
557  * wrapper function to set on-screen styles - RP
558  */
wcurses_css(WINDOW * win,char * name,int dir)559 void wcurses_css(WINDOW * win, char *name,
560 		 int dir)
561 {
562     int try_again = 1;
563 
564     while (try_again) {
565 	int tmpHash = hash_code(name);
566 
567 	CTRACE2(TRACE_STYLE, (tfp, "CSSTRIM:trying to set [%s] style - ", name));
568 	if (tmpHash == NOSTYLE) {
569 	    char *pclass = strrchr(name, '.');
570 
571 	    CTRACE2(TRACE_STYLE, (tfp, "undefined, trimming at %p\n", pclass));
572 	    if (pclass)
573 		*pclass = '\0';
574 	    else
575 		try_again = 0;
576 	} else {
577 	    CTRACE2(TRACE_STYLE, (tfp, "ok (%d)\n", hash_code(name)));
578 	    curses_w_style(win, hash_code(name), dir);
579 	    try_again = 0;
580 	}
581     }
582 }
583 
curses_css(char * name,int dir)584 void curses_css(char *name,
585 		int dir)
586 {
587     wcurses_css(LYwin, name, dir);
588 }
589 
curses_style(int style,int dir)590 void curses_style(int style,
591 		  int dir)
592 {
593     curses_w_style(LYwin, style, dir);
594 }
595 #endif /* USE_COLOR_STYLE */
596 
597 static BOOL lynx_called_initscr = FALSE;
598 
599 #if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
600 #define COLOR_CFG_MAX 8
601 
602 /*
603  * This block of code is designed to produce the same color effects using SVr4
604  * curses as the slang library's implementation in this module.  That maps the
605  * SGR codes into a 0-7 index into the color table, with special treatment for
606  * backgrounds.  There's a bit of convoluted (but necessary) code handling the
607  * special case of initialization before 'initscr()' is called.
608  * 1997/1/19 - T.E.Dickey <dickey@clark.net>
609  */
610 /* *INDENT-OFF* */
611 #define COLOR_CFG(c) c, (c) == DEFAULT_COLOR
612 static struct {
613     int fg, dft_fg, bg, dft_bg;
614 } lynx_color_cfg[] = {
615     /*0*/ { COLOR_CFG(DEFAULT_FG),       COLOR_CFG(DEFAULT_BG)},
616     /*1*/ { COLOR_CFG(COLOR_BLUE),       COLOR_CFG(DEFAULT_BG)},
617     /*2*/ { COLOR_CFG((COLOR_YELLOW)+8), COLOR_CFG(COLOR_BLUE)},
618     /*3*/ { COLOR_CFG(COLOR_GREEN),      COLOR_CFG(DEFAULT_BG)},
619     /*4*/ { COLOR_CFG(COLOR_MAGENTA),    COLOR_CFG(DEFAULT_BG)},
620     /*5*/ { COLOR_CFG(COLOR_BLUE),       COLOR_CFG(DEFAULT_BG)},
621     /*6*/ { COLOR_CFG(COLOR_RED),        COLOR_CFG(DEFAULT_BG)},
622     /*7*/ { COLOR_CFG(COLOR_MAGENTA),    COLOR_CFG(COLOR_CYAN)}
623 };
624 /* *INDENT-ON* */
625 
626 #define COLOR_PAIRS_MAX (COLOR_CFG_MAX * 3 + 1)
627 
628 /*
629  * Hold the codes for color-pairs here until 'initscr()' is called.
630  */
631 static struct {
632     int fg;
633     int bg;
634 } lynx_color_pairs[COLOR_PAIRS_MAX];
635 
636 /*
637  * If we find an exact match for the given default colors, force curses to use
638  * color pair 0, which corresponds to the terminal's default colors.  Normally
639  * curses assumes white-on-black, but we can override the assumption with this
640  * function.
641  */
get_color_pair(int n)642 static int get_color_pair(int n)
643 {
644 #ifdef USE_CURSES_PAIR_0
645     if ((n < (int) TABLESIZE(lynx_color_pairs))
646 	&& lynx_color_pairs[n].fg == default_fg
647 	&& lynx_color_pairs[n].bg == default_bg)
648 	return 0;
649 #endif
650     return COLOR_PAIR(n);
651 }
652 
653 /*
654  * Lynx "knows" about 16 colors.  ANSI colors (and most color terminal
655  * emulators) only go to 8, though some curses implementations (ncurses and
656  * PDCurses) handle 16.  If lynx' configuration calls for a color past the
657  * number of colors that the terminal handles (COLORS), map the extra value
658  * to bold.
659  */
660 #define is_boldc(c) ((c) > (COLORS-1))
661 #define map2bold(c) (is_boldc(c) ? ((c) & (COLORS-1)) : (c))
662 
663 /*
664  * Return the extra color as A_BOLD.
665  * If there is no extra color, return A_NORMAL.
666  */
lynx_color_cfg_attr(int code)667 static int lynx_color_cfg_attr(int code)
668 {
669     int result = A_NORMAL;
670 
671     if (code >= 0 && code < COLOR_CFG_MAX) {
672 	int fg = lynx_color_cfg[code].fg;
673 
674 	if (is_boldc(fg) && (fg & COLORS))
675 	    result = A_BOLD;
676     }
677     return result;
678 }
679 
encode_color_attr(int color_attr)680 static int encode_color_attr(int color_attr)
681 {
682     int result;
683     int code = 0;
684     int offs = 1;
685 
686     if ((unsigned) color_attr & A_BOLD)
687 	code |= 1;
688     if ((unsigned) color_attr & (A_REVERSE | A_DIM))
689 	code |= 2;
690     if ((unsigned) color_attr & A_UNDERLINE)
691 	code |= 4;
692     result = lynx_color_cfg_attr(code);
693 
694     if (code + offs < COLOR_PAIRS) {
695 	result |= get_color_pair(code + offs);
696     }
697     return result;
698 }
699 
decode_mono_code(int mono_code)700 static int decode_mono_code(int mono_code)
701 {
702     unsigned result = 0;
703 
704     if (mono_code & 1)
705 	result |= A_BOLD;
706     if (mono_code & 2)
707 	result |= A_REVERSE;
708     if (mono_code & 4)
709 	result |= A_UNDERLINE;
710 
711     return (int) result;
712 }
713 
714 /*
715  * Map the SGR attributes (0-7) into ANSI colors, modified with the actual BOLD
716  * attribute to get 16 colors.
717  */
LYgetTableAttr(void)718 int LYgetTableAttr(void)
719 {
720     int result;
721 
722     if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON) {
723 	result = encode_color_attr(Current_Attr);
724     } else {
725 	result = Current_Attr;
726     }
727     return result & ~Masked_Attr;
728 }
729 
730 #ifdef USE_COLOR_STYLE
731 /*
732  * Return a string that corresponds to the attributes that would be returned by
733  * LYgetTableAttr().
734  */
LYgetTableString(int code)735 char *LYgetTableString(int code)
736 {
737     int mask = decode_mono_code(code);
738     int second = encode_color_attr(mask);
739     int pair = PAIR_NUMBER((unsigned) second);
740     int mono = (int) ((unsigned) mask & A_ATTRIBUTES);
741     int fg = lynx_color_pairs[pair].fg;
742     int bg = lynx_color_pairs[pair].bg;
743     unsigned n;
744     char *result = 0;
745 
746     CTRACE((tfp, "LYgetTableString(%d)\n", code));
747 
748     if (fg == 0 && bg == 0) {
749 	fg = COLOR_WHITE;
750     }
751     CTRACE((tfp, "%#x -> %#x (mono %#x pair %d) fg=%d, bg=%d\n",
752 	    mask, second, mono, pair, fg, bg));
753     for (n = 0; n < TABLESIZE(Mono_Attrs); ++n) {
754 	if ((Mono_Attrs[n].code & mono) != 0) {
755 	    if (result != 0)
756 		StrAllocCat(result, "+");
757 	    StrAllocCat(result, Mono_Attrs[n].name);
758 	}
759     }
760     if (result == 0)
761 	StrAllocCopy(result, "normal");
762     StrAllocCat(result, ":");
763     StrAllocCat(result, lookup_color(fg));
764     if (bg >= 0) {
765 	StrAllocCat(result, ":");
766 	StrAllocCat(result, lookup_color(bg));
767     }
768     CTRACE((tfp, "->%s\n", result));
769     return result;
770 }
771 #endif
772 
773 /*
774  * Initialize a curses color-pair based on our configured color values.
775  */
lynx_init_color_pair(int n)776 static void lynx_init_color_pair(int n)
777 {
778 #ifdef USE_COLOR_STYLE
779     (void) n;			/* we only use lynx_color_pairs[] data */
780 #else
781     int m;
782 
783     if (lynx_called_initscr) {
784 	for (m = 0; m <= 16; m += 8) {
785 	    int pair = n + m + 1;
786 
787 	    if (pair < COLOR_PAIRS)
788 		init_pair((short) pair,
789 			  (short) map2bold(lynx_color_pairs[pair].fg),
790 			  (short) map2bold(lynx_color_pairs[pair].bg));
791 	}
792 	if (n == 0 && LYShowColor >= SHOW_COLOR_ON) {
793 	    wbkgd(LYwin, COLOR_BKGD | ' ');
794 	}
795     }
796 #endif
797 }
798 
lynx_map_color(int n)799 static void lynx_map_color(int n)
800 {
801     int j;
802 
803     CTRACE((tfp, "lynx_map_color(%d)\n", n));
804 
805     if (n + 1 < (int) TABLESIZE(lynx_color_pairs)
806 	&& n < (int) TABLESIZE(lynx_color_cfg)) {
807 	for (j = n + 1; j < COLOR_PAIRS_MAX; j += COLOR_CFG_MAX) {
808 	    lynx_color_pairs[j].fg = lynx_color_cfg[n].fg;
809 	    lynx_color_pairs[j].bg = lynx_color_cfg[n].bg;
810 	}
811 
812 	/* special case (does not apply to 3rd set) */
813 	lynx_color_pairs[n + 1 + COLOR_CFG_MAX].bg = lynx_color_cfg[0].bg;
814     }
815 
816     lynx_init_color_pair(n);
817 }
818 
819 /*
820  * Change a configured color value.  This may be called before initscr(), so
821  * we may not be able to call init_pair() to finish the change.
822  */
lynx_chg_color(int color,int fg,int bg)823 int lynx_chg_color(int color,
824 		   int fg,
825 		   int bg)
826 {
827     CTRACE((tfp, "lynx_chg_color(color=%d, fg=%d, bg=%d)\n", color, fg, bg));
828 
829     if (fg == ERR_COLOR || bg == ERR_COLOR)
830 	return -1;
831     if (color >= 0 && color < COLOR_CFG_MAX) {
832 	lynx_color_cfg[color].fg = fg;
833 	lynx_color_cfg[color].bg = bg;
834 	lynx_map_color(color);
835     } else {
836 	return -1;
837     }
838     return 0;
839 }
840 
lynx_set_color(int a)841 void lynx_set_color(int a)
842 {
843     if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON) {
844 	(void) wattrset(LYwin, (unsigned) lynx_color_cfg_attr(a)
845 			| (((a + 1) < COLOR_PAIRS)
846 			   ? (chtype) get_color_pair(a + 1)
847 			   : A_NORMAL));
848     }
849 }
850 
lynx_standout(int flag)851 void lynx_standout(int flag)
852 {
853     if (flag)
854 	LYaddAttr(A_REVERSE);
855     else
856 	LYsubAttr(A_REVERSE);
857 }
858 
lynx_init_colors(void)859 static void lynx_init_colors(void)
860 {
861     if (lynx_has_color) {
862 	size_t n;
863 
864 	CTRACE((tfp, "lynx_init_colors (default %d/%d)\n",
865 		default_fg, default_bg));
866 
867 	lynx_color_cfg[0].fg = default_fg;
868 	lynx_color_cfg[0].bg = default_bg;
869 
870 	for (n = 0; n < TABLESIZE(lynx_color_cfg); n++) {
871 	    lynx_init_color_pair((int) n);
872 	}
873     } else if (LYShowColor != SHOW_COLOR_NEVER) {
874 	LYShowColor = SHOW_COLOR_OFF;
875     }
876 }
877 
lynx_setup_colors(void)878 void lynx_setup_colors(void)
879 {
880     int n;
881 
882     CTRACE((tfp, "lynx_setup_colors\n"));
883 #ifdef USE_DEFAULT_COLORS
884     if (!LYuse_default_colors) {
885 	for (n = 0; n < COLOR_CFG_MAX; n++) {
886 	    if (lynx_color_cfg[n].dft_fg)
887 		lynx_color_cfg[n].fg = COLOR_BLACK;
888 	    if (lynx_color_cfg[n].dft_bg)
889 		lynx_color_cfg[n].bg = COLOR_WHITE;
890 	}
891     }
892 #endif
893     for (n = 0; n < COLOR_CFG_MAX; n++)
894 	lynx_map_color(n);
895 }
896 #endif /* USE_COLOR_TABLE */
897 
LYnoVideo(int a)898 void LYnoVideo(int a)
899 {
900     CTRACE((tfp, "LYnoVideo(%d)\n", a));
901 #ifdef USE_SLANG
902     if (a & 1)
903 	Masked_Attr |= (int) SLTT_BOLD_MASK;
904     if (a & 2)
905 	Masked_Attr |= (int) SLTT_REV_MASK;
906     if (a & 4)
907 	Masked_Attr |= (int) SLTT_ULINE_MASK;
908     lynx_setup_attrs();
909 #else
910 #ifdef USE_COLOR_TABLE
911     Masked_Attr = decode_mono_code(a);
912 #endif
913 #endif
914 }
915 
916 #define NEWTERM_NAME "newterm"
917 
918 #if !defined(VMS) && !defined(USE_SLANG)
919 #if defined(NCURSES) && defined(HAVE_RESIZETERM)
920 
921 static SCREEN *LYscreen = NULL;
922 
923 #define LYDELSCR()		/* ncurses does not need this */
924 
925 #elif defined(HAVE_NEWTERM) && defined(HAVE_DELSCREEN) && !defined(PDCURSES)
926 
927 static SCREEN *LYscreen = NULL;
928 
929 #define LYDELSCR() { \
930 if (recent_sizechange) { \
931     CTRACE((tfp, "Screen size: delscreen()\n")); \
932     delscreen(LYscreen); \
933     LYscreen = NULL; } }
934 
935 #else /* HAVE_NEWTERM   */
936 
937     /*
938      * If newterm is not defined, assume a curses subset which
939      * supports only initscr.  --gil
940      */
941 static WINDOW *LYscreen = NULL;
942 
943 #undef  NEWTERM_NAME
944 #define NEWTERM_NAME "initscr"
945 #undef  newterm
946 #define newterm(type, out, in) (initscr())
947 #define LYDELSCR()		/* nothing */
948 #endif /* HAVE_NEWTERM   */
949 
950 #else /* !defined(VMS) && !defined(USE_SLANG) */
951 
952     /*
953      * Provide last recourse definitions of LYscreen and LYDELSCR for
954      * stop_curses, which only tests LYscreen for zero/nonzero but
955      * never uses it as a pointer or L-value.
956      */
957 #define LYscreen TRUE
958 #define LYDELSCR()		/* nothing */
959 #endif /* !defined(VMS) && !defined(USE_SLANG) */
960 
961 #if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
962 int saved_scrsize_x = 0;
963 int saved_scrsize_y = 0;
964 
965 int saved_scrsize_x2 = 0;
966 int saved_scrsize_y2 = 0;
967 int saved_winpos_x2 = 0;
968 int saved_winpos_y2 = 0;
969 #endif
970 
971 #ifdef USE_MAXSCREEN_TOGGLE
972 static HWND currentWindowHandle = NULL;
973 static char dummyWindowTitle[256];
974 
EnumWindowsProc(HWND hwnd,LPARAM lParam)975 static int CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
976 {
977     char this_title[256];
978 
979     if (GetWindowText(hwnd, this_title, sizeof(this_title) - 1) &&
980 	(strncmp(dummyWindowTitle, this_title, 256) == 0)) {
981 	currentWindowHandle = hwnd;
982 	return FALSE;
983     }
984     return TRUE;
985 }
986 
setCurrentWindowHandle(void)987 static void setCurrentWindowHandle(void)
988 {
989     char org_title[256];
990     DWORD pid;
991     int i;
992 
993     if (currentWindowHandle != NULL) {
994 	return;
995     }
996     pid = GetCurrentProcessId();
997     sprintf(dummyWindowTitle, "Lynx for Win32 (pid=%ld)", pid);
998     GetConsoleTitle(org_title, sizeof(org_title) - 1);
999     SetConsoleTitle(dummyWindowTitle);
1000     for (i = 0; i < 10; i++) {
1001 	EnumWindows(EnumWindowsProc, (LPARAM) 0);
1002 	if (currentWindowHandle != NULL) {
1003 	    break;
1004 	}
1005 	CTRACE((tfp,
1006 		"Failed to get current window handle. Try again...(%d)\n", i));
1007 	Sleep(100);
1008     }
1009     SetConsoleTitle(org_title);
1010 }
1011 
moveWindowHXY(HWND hwnd,int x,int y)1012 static void moveWindowHXY(HWND hwnd, int x, int y)
1013 {
1014     int win_height, win_width;
1015     RECT winrect;
1016 
1017     GetWindowRect(hwnd, &winrect);
1018     win_width = winrect.right - winrect.left;
1019     win_height = winrect.bottom - winrect.top;
1020 
1021     if ((x != winrect.left) || (y != winrect.top)) {
1022 	MoveWindow(hwnd, x, y, win_width, win_height, TRUE);
1023 	CTRACE((tfp, "move window from (%d,%d) to (%d,%d).\n",
1024 		(int) winrect.left,
1025 		(int) winrect.top, x, y));
1026     }
1027 }
1028 
adjustWindowPos(void)1029 static void adjustWindowPos(void)
1030 {
1031     int disp_height, disp_width, win_height, win_width;
1032     int newwin_left, newwin_top;
1033     RECT winrect;
1034     DWORD pid;
1035 
1036     setCurrentWindowHandle();
1037     if (currentWindowHandle == NULL) {
1038 	return;
1039     }
1040     GetWindowThreadProcessId(currentWindowHandle, &pid);
1041     disp_width = GetSystemMetrics(SM_CXFULLSCREEN);
1042     disp_height = GetSystemMetrics(SM_CYFULLSCREEN);
1043     Sleep(100);			/* If not, GetWindowRect sometimes return wrong value. */
1044     GetWindowRect(currentWindowHandle, &winrect);
1045     win_width = winrect.right - winrect.left;
1046     win_height = winrect.bottom - winrect.top;
1047     CTRACE((tfp, "Display Size: (%4d,%3d)\n", disp_width, disp_height));
1048     CTRACE((tfp, "Orig WinRect: (%4d,%4d,%3d,%3d), ",
1049 	    (int) winrect.left, (int) winrect.right,
1050 	    (int) winrect.top, (int) winrect.bottom));
1051     CTRACE((tfp, "Size: (%4d,%3d)\n", win_width, win_height));
1052 
1053     newwin_left = winrect.left;
1054     newwin_top = winrect.top;
1055     if (disp_width < winrect.right) {
1056 	if (win_width <= disp_width) {
1057 	    newwin_left = disp_width - win_width;
1058 	} else {
1059 	    newwin_left = 0;
1060 	}
1061     }
1062     if (disp_height < winrect.bottom) {
1063 	if (win_height <= disp_height) {
1064 	    newwin_top = disp_height - win_height;
1065 	} else {
1066 	    newwin_top = 0;
1067 	}
1068     }
1069 
1070     moveWindowHXY(currentWindowHandle, newwin_left, newwin_top);
1071 }
1072 
maxmizeWindowSize(void)1073 void maxmizeWindowSize(void)
1074 {
1075     int disp_height, disp_width, win_height, win_width;
1076     RECT winrect;
1077     DWORD pid;
1078     int font_width, font_height;
1079 
1080     setCurrentWindowHandle();
1081     if (currentWindowHandle == NULL) {
1082 	return;
1083     }
1084     GetWindowThreadProcessId(currentWindowHandle, &pid);
1085     disp_width = GetSystemMetrics(SM_CXFULLSCREEN);
1086     disp_height = GetSystemMetrics(SM_CYFULLSCREEN);
1087     GetWindowRect(currentWindowHandle, &winrect);
1088     win_width = winrect.right - winrect.left;
1089     win_height = winrect.bottom - winrect.top;
1090     saved_winpos_x2 = winrect.left;
1091     saved_winpos_y2 = winrect.top;
1092 
1093     if ((win_width <= disp_width) && (win_height <= disp_height)) {
1094 	GetClientRect(currentWindowHandle, &winrect);
1095 	win_width = winrect.right - winrect.left;
1096 	win_height = winrect.bottom - winrect.top;
1097 	CTRACE((tfp, "Current Rect: (%4d,%4d,%3d,%3d), ",
1098 		(int) winrect.left, (int) winrect.right,
1099 		(int) winrect.top, (int) winrect.bottom));
1100 	CTRACE((tfp, "Size: (%4d,%3d)\n", win_width, win_height));
1101 
1102 	if (scrsize_x == 0) {
1103 	    scrsize_x = COLS;
1104 	    scrsize_y = LINES + 1;
1105 	}
1106 	if ((scrsize_x == 0) || (scrsize_y == 0)) {
1107 	    CTRACE((tfp, "Illegal value: scrsize_x=%d, scrsize_y=%d\n",
1108 		    scrsize_x, scrsize_y));
1109 	} else {
1110 	    font_width = win_width / scrsize_x;
1111 	    font_height = win_height / scrsize_y;
1112 	    CTRACE((tfp, "Font Size: (%2d,%2d)\n", font_width, font_height));
1113 	    if ((font_width == 0) || (font_height == 0)) {
1114 		CTRACE((tfp, "Illegal font size.\n"));
1115 	    } else {
1116 		LYcols = scrsize_x = (disp_width - 4) / font_width;
1117 		LYlines = scrsize_y = (disp_height - 32) / font_height;
1118 		LYlines--;
1119 		CTRACE((tfp, "Request maximum screen size: %dx%d\n",
1120 			scrsize_y, scrsize_x));
1121 		resize_term(scrsize_y, scrsize_x);
1122 		Sleep(100);
1123 		moveWindowHXY(currentWindowHandle, 0, 0);
1124 		LYlines = LYscreenHeight();
1125 		LYcols = LYscreenWidth();
1126 		CTRACE((tfp, "...actual maximum screen size: %dx%d\n",
1127 			LYlines, LYcols));
1128 		LYStatusLine = -1;
1129 		recent_sizechange = TRUE;
1130 	    }
1131 	}
1132     }
1133 }
1134 
recoverWindowSize(void)1135 void recoverWindowSize(void)
1136 {
1137     if ((0 < saved_scrsize_x2) && (0 < saved_scrsize_y2)) {
1138 	LYcols = scrsize_x = saved_scrsize_x2;
1139 	LYlines = scrsize_y = saved_scrsize_y2;
1140 	LYlines--;
1141 	LYStatusLine = -1;
1142 	wclear(curscr);
1143 	doupdate();
1144 	resize_term(scrsize_y, scrsize_x);
1145 
1146 	setCurrentWindowHandle();
1147 	if (currentWindowHandle != NULL) {
1148 	    Sleep(100);
1149 	    moveWindowHXY(currentWindowHandle, saved_winpos_x2, saved_winpos_y2);
1150 	}
1151 	recent_sizechange = TRUE;
1152     } else {
1153 	CTRACE((tfp, "scrsize_{xy} is not saved yet.\n"));
1154     }
1155 }
1156 #endif
1157 
1158 #if defined(USE_DEFAULT_COLORS)
restart_curses(void)1159 void restart_curses(void)
1160 {
1161     SCREEN *oldscreen = LYscreen;
1162 
1163     if (!(LYscreen = newterm(NULL, stdout, stdin))) {	/* start curses */
1164 	fprintf(tfp, "%s\n",
1165 		gettext("Terminal reinitialisation failed - unknown terminal type?"));
1166 	exit_immediately(EXIT_FAILURE);
1167     }
1168 
1169     /* force xterm mouse-mode off in the physical terminal */
1170     lynx_enable_mouse(0);
1171     keypad(LYwin, FALSE);
1172     wrefresh(LYwin);
1173 
1174     LYwin = stdscr;
1175     /* reenable xterm mouse-mode in the new screen */
1176     keypad(LYwin, TRUE);
1177     lynx_enable_mouse(1);
1178 
1179     if (-1 == lynx_initialize_keymaps()) {
1180 	endwin();
1181 	exit_immediately(EXIT_FAILURE);
1182     }
1183     if (has_colors()) {
1184 	start_color();
1185     }
1186 
1187     delscreen(oldscreen);
1188 }
1189 #endif
1190 
start_curses(void)1191 void start_curses(void)
1192 {
1193 #ifdef USE_SLANG
1194     static int slinit;
1195 
1196     if (LYCursesON) {
1197 	CTRACE((tfp, "start_curses: Hmm, already ON.\n"));
1198 	return;
1199     }
1200 
1201     if (slinit == 0) {
1202 #if defined(HAVE_TTYNAME)
1203 	if (isatty(fileno(stdout)) && LYReopenInput() < 0) {
1204 	    fprintf(stderr, "Cannot open tty input\n");
1205 	    exit_immediately(EXIT_FAILURE);
1206 	}
1207 #endif
1208 #if defined(USE_KEYMAPS)
1209 	if (-1 == lynx_initialize_keymaps())
1210 	    exit_immediately(EXIT_FAILURE);
1211 #else
1212 	SLtt_get_terminfo();
1213 #endif
1214 #if (defined(__DJGPP__) && !defined(DJGPP_KEYHANDLER)) || defined(__CYGWIN__)
1215 	SLkp_init();
1216 #endif /* __DJGPP__ && !DJGPP_KEYHANDLER */
1217 
1218 #if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
1219 #if SLANG_VERSION >= 9935
1220 	SLang_TT_Read_FD = fileno(stdin);
1221 #endif /* SLANG_VERSION >= 9935 */
1222 #endif /* REAL_UNIX_SYSTEM && !__CYGWIN__ */
1223 
1224 #if !defined(USE_KEYMAPS) && defined(ENHANCED_LINEEDIT) && defined(ESCDELAY)
1225 	/* way to get ESC that's not part of a recognized sequence through */
1226 	ESCDELAY = 2000;
1227 #endif
1228 	/*
1229 	 * Check whether a saved show_color:off override is in effect.  - kw
1230 	 */
1231 	if (LYrcShowColor == SHOW_COLOR_NEVER) {
1232 	    SLtt_Use_Ansi_Colors = 0;
1233 	}
1234 	/*
1235 	 * Check whether we're forcing color on.  - FM
1236 	 */
1237 	if ((LYShowColor > 1) && (Lynx_Color_Flags & SL_LYNX_USE_COLOR))
1238 	    SLtt_Use_Ansi_Colors = 1;
1239 	/*
1240 	 * Check whether a -nocolor override is in effect.  - kw
1241 	 */
1242 	if (Lynx_Color_Flags & SL_LYNX_OVERRIDE_COLOR)
1243 	    SLtt_Use_Ansi_Colors = 0;
1244 	/*
1245 	 * Make sure our flags are in register.  - FM
1246 	 */
1247 	if (SLtt_Use_Ansi_Colors == 1) {
1248 	    if (LYShowColor != SHOW_COLOR_ALWAYS) {
1249 		LYShowColor = SHOW_COLOR_ON;
1250 	    }
1251 	} else {
1252 	    if (LYShowColor != SHOW_COLOR_NEVER) {
1253 		LYShowColor = SHOW_COLOR_OFF;
1254 	    }
1255 	}
1256 	size_change(0);
1257 
1258 #if (defined(VMS) || defined(REAL_UNIX_SYSTEM)) && !defined(__CYGWIN__)
1259 	if ((Masked_Attr & (int) SLTT_ULINE_MASK) == 0) {
1260 	    SLtt_add_color_attribute(4, SLTT_ULINE_MASK);
1261 	    SLtt_add_color_attribute(5, SLTT_ULINE_MASK);
1262 	}
1263 	/*
1264 	 * If set, the blink escape sequence will turn on high intensity
1265 	 * background (rxvt and maybe Linux console).
1266 	 */
1267 	SLtt_Blink_Mode = term_blink_is_boldbg;
1268 #endif /* (VMS || REAL_UNIX_SYSTEM) && !__CYGWIN__  */
1269     }
1270 #ifdef __DJGPP__
1271     _eth_init();
1272 #endif /* __DJGPP__ */
1273 
1274     slinit = 1;
1275     Current_Attr = 0;
1276 #ifndef VMS
1277 #if SLANG_VERSION > 9929
1278     SLang_init_tty(-1, 0, 1);
1279 #else
1280     SLang_init_tty(3, 0, 1);
1281 #endif /* SLANG_VERSION > 9929 */
1282 #endif /* !VMS */
1283     SLsmg_init_smg();
1284     SLsmg_Display_Eight_Bit = LYlowest_eightbit[current_char_set];
1285     if (SLsmg_Display_Eight_Bit > 191)
1286 	SLsmg_Display_Eight_Bit = 191;	/* may print ctrl chars otherwise - kw */
1287     scrollok(0, 0);
1288     SLsmg_Backspace_Moves = 1;
1289 #if SLANG_VERSION > 10306
1290     SLsmg_touch_screen();
1291 #endif
1292 #ifndef VMS
1293 #if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
1294     SLtty_set_suspend_state(1);
1295 #endif /* REAL_UNIX_SYSTEM && !__CYGWIN__ */
1296 #ifdef SIGTSTP
1297     if (!no_suspend)
1298 	signal(SIGTSTP, sl_suspend);
1299 #endif /* SIGTSTP */
1300     signal(SIGINT, cleanup_sig);
1301 #endif /* !VMS */
1302 
1303     lynx_enable_mouse(1);
1304 
1305 #else /* USE_SLANG; Now using curses: */
1306     int keypad_on = 0;
1307 
1308 #ifdef VMS
1309     /*
1310      * If we are VMS then do initscr() everytime start_curses() is called!
1311      */
1312     CTRACE((tfp, "Screen size: initscr()\n"));
1313     initscr();			/* start curses */
1314 #else /* Unix: */
1315 
1316 #if defined(HAVE_TTYNAME)
1317     if (isatty(fileno(stdout)) && LYReopenInput() < 0) {
1318 	fprintf(stderr, "Cannot open tty input\n");
1319 	exit_immediately(EXIT_FAILURE);
1320     }
1321 #endif
1322 
1323 #ifdef __CYGWIN__
1324     /*
1325      * Workaround for buggy Cygwin, which breaks subprocesses of a
1326      * full-screen application (tested with cygwin dll, dated
1327      * 2002/6/23 -TD)
1328      */
1329     if (!lynx_called_initscr) {
1330 	FILE *fp = fopen("/dev/tty", "w");
1331 
1332 	if (fp != 0)
1333 	    stdout = fp;
1334     }
1335 #endif
1336 
1337     if (!LYscreen) {
1338 	/*
1339 	 * If we're not VMS then only do initscr() one time, and one time only!
1340 	 */
1341 #if defined(HAVE_NEWTERM)
1342 #if !(defined(NCURSES) && !defined(HAVE_RESIZETERM))
1343 	BOOLEAN savesize;
1344 
1345 	savesize = recent_sizechange;
1346 	size_change(0);
1347 	recent_sizechange = savesize;	/* avoid extra redraw */
1348 #if defined(__MVS__)
1349 	{
1350 	    /*
1351 	     * The requirement to do this may be a bug in OS/390.
1352 	     *
1353 	     * Put screen geometry in environment variables used by
1354 	     * XOpen curses before calling newterm().  I believe this
1355 	     * completes work left unfinished by AJL & FM -- gil
1356 	     */
1357 	    static char lines_putenv[] = "LINES=abcde", cols_putenv[] = "COLUMNS=abcde";
1358 
1359 	    sprintf(lines_putenv + 6, "%d", LYlines & 0xfff);
1360 	    sprintf(cols_putenv + 8, "%d", LYcols & 0xfff);
1361 	    putenv(lines_putenv);
1362 	    putenv(cols_putenv);
1363 	    CTRACE((tfp, "start_curses putenv %s, %s\n", lines_putenv, cols_putenv));
1364 	}
1365 #endif /* defined(__MVS__) */
1366 #endif /* !(defined(NCURSES) && defined(HAVE_RESIZETERM)) */
1367 	CTRACE((tfp, "Screen size: %s()\n", NEWTERM_NAME));
1368 	if (!(LYscreen = newterm(NULL, stdout, stdin))) {	/* start curses */
1369 	    fprintf(tfp, "%s\n",
1370 		    gettext("Terminal initialisation failed - unknown terminal type?"));
1371 	    exit_immediately(EXIT_FAILURE);
1372 	}
1373 #else
1374 	CTRACE((tfp, "Screen size: initscr()\n"));
1375 	initscr();
1376 #endif /* HAVE_NEWTERM */
1377 	lynx_called_initscr = TRUE;
1378 	LYlines = LYscreenHeight();
1379 	LYcols = LYscreenWidth();
1380 
1381 #if defined(SIGWINCH) && defined(NCURSES_VERSION)
1382 	size_change(0);
1383 	recent_sizechange = FALSE;	/* prevent mainloop drawing 1st doc twice */
1384 #endif /* SIGWINCH */
1385 	CTRACE((tfp, "Screen size is now %d x %d\n", LYlines, LYcols));
1386 
1387 #ifdef USE_CURSES_PADS
1388 	if (LYuseCursesPads) {
1389 	    CTRACE((tfp, "using curses-pads\n"));
1390 	    LYwin = newpad(LYlines, MAX_COLS);
1391 	    LYshiftWin = 0;
1392 	    LYwideLines = FALSE;
1393 	} else {
1394 	    LYwin = stdscr;
1395 	}
1396 #endif
1397 
1398 #if defined(USE_KEYMAPS) && defined(NCURSES_VERSION)
1399 #  if HAVE_KEYPAD
1400 	/* Need to switch keypad on before initializing keymaps, otherwise
1401 	   when the keypad is switched on, some keybindings may be overriden. */
1402 	keypad(LYwin, TRUE);
1403 	keypad_on = 1;
1404 #  endif /* HAVE_KEYPAD */
1405 
1406 	if (-1 == lynx_initialize_keymaps()) {
1407 	    endwin();
1408 	    exit_immediately(EXIT_FAILURE);
1409 	}
1410 #endif
1411 
1412 	/*
1413 	 * This is a workaround for a bug in SVr4 curses, observed on Solaris
1414 	 * 2.4:  if your terminal's alternate-character set contains codes in
1415 	 * the range 128-255, they'll be sign-extended in the acs_map[] table,
1416 	 * which in turn causes their values to be emitted as 255 (0xff).
1417 	 * "Fix" this by forcing the table to 8-bit codes (it has to be
1418 	 * anyway).
1419 	 */
1420 #if defined(ALT_CHAR_SET) && !defined(NCURSES_VERSION)
1421 	{
1422 	    int n;
1423 
1424 	    for (n = 0; n < 128; n++)
1425 		if (ALT_CHAR_SET[n] & 0x80) {
1426 		    ALT_CHAR_SET[n] &= 0xff;
1427 		    ALT_CHAR_SET[n] |= A_ALTCHARSET;
1428 		}
1429 	}
1430 #endif
1431 
1432 #if defined(USE_COLOR_STYLE) || defined(USE_COLOR_TABLE)
1433 	if (has_colors()) {
1434 	    lynx_has_color = TRUE;
1435 	    start_color();
1436 
1437 #ifndef COLORS
1438 	    /* map2boldc() relies on COLORS being a power of 2 */
1439 	    if (COLORS > 16)
1440 		COLORS = 16;
1441 	    if (COLORS < 8)
1442 		COLORS = 2;
1443 	    if (COLORS > 8 && COLORS != 16)
1444 		COLORS = 8;
1445 #endif
1446 
1447 #ifdef USE_DEFAULT_COLORS
1448 	    update_default_colors();
1449 	    if (LYuse_default_colors) {
1450 #if defined(EXP_ASSUMED_COLOR) && defined(USE_COLOR_TABLE)
1451 		/*
1452 		 * Adjust the color mapping table to match the ASSUMED_COLOR
1453 		 * setting in lynx.cfg
1454 		 */
1455 		if (assume_default_colors(default_fg, default_bg) != OK) {
1456 		    default_fg = COLOR_WHITE;
1457 		    default_bg = COLOR_BLACK;
1458 		}
1459 		CTRACE((tfp, "initializing default colors %d/%d\n",
1460 			default_fg, default_bg));
1461 		if (default_fg >= 0 || default_bg >= 0) {
1462 		    unsigned n;
1463 
1464 		    for (n = 0; n < TABLESIZE(lynx_color_cfg); n++) {
1465 			if (default_fg >= 0 && lynx_color_cfg[n].fg < 0)
1466 			    lynx_color_cfg[n].fg = default_fg;
1467 			if (default_bg >= 0 && lynx_color_cfg[n].bg < 0)
1468 			    lynx_color_cfg[n].bg = default_bg;
1469 			CTRACE((tfp, "color_cfg[%u] = %d/%d\n", n,
1470 				lynx_color_cfg[n].fg,
1471 				lynx_color_cfg[n].bg));
1472 		    }
1473 		    lynx_setup_colors();
1474 		}
1475 #else
1476 #if defined(HAVE_USE_DEFAULT_COLORS)
1477 		if (!default_color_reset) {
1478 		    if (lynx_called_initscr) {
1479 			if (LYuse_default_colors && (use_default_colors() == OK)) {
1480 			    default_fg = DEFAULT_COLOR;
1481 			    default_bg = DEFAULT_COLOR;
1482 			} else {
1483 			    default_fg = COLOR_WHITE;
1484 			    default_bg = COLOR_BLACK;
1485 			    default_color_reset = TRUE;
1486 			}
1487 		    }
1488 		}
1489 #endif /* HAVE_USE_DEFAULT_COLORS */
1490 #endif /* EXP_ASSUMED_COLOR */
1491 	    }
1492 #endif /* USE_DEFAULT_COLORS */
1493 	}
1494 #endif /* USE_COLOR_STYLE || USE_COLOR_TABLE */
1495 
1496 #ifdef USE_COLOR_STYLE
1497 	/* Curses forgets color settings when we call delscreen() */
1498 	if (non_empty(lynx_lss_file) && LYCanReadFile(lynx_lss_file)) {
1499 	    style_readFromFile(lynx_lss_file);
1500 	}
1501 	parse_userstyles();
1502 #endif
1503 #ifdef USE_COLOR_TABLE
1504 	lynx_init_colors();
1505 #endif
1506     }
1507 #ifdef __DJGPP__
1508     _eth_init();
1509 #endif /* __DJGPP__ */
1510 #endif /* not VMS */
1511 
1512 #ifdef VMS
1513     crmode();
1514     raw();
1515 #else
1516 #ifdef HAVE_CBREAK
1517     cbreak();
1518 #else
1519     crmode();
1520 #endif /* HAVE_CBREAK */
1521     signal(SIGINT, cleanup_sig);
1522 #endif /* VMS */
1523 
1524     noecho();
1525 
1526 #ifdef HAVE_KEYPAD
1527     if (!keypad_on)
1528 	keypad(LYwin, TRUE);
1529 #endif /* HAVE_KEYPAD */
1530 
1531     lynx_enable_mouse(1);
1532 
1533     fflush(stdin);
1534     fflush(stdout);
1535     fflush(stderr);
1536 #endif /* USE_SLANG */
1537 
1538 #if defined(WIN_EX)
1539     LYclear();
1540 #endif
1541 
1542 #if defined(USE_BLINK) && defined(__EMX__)
1543     if (term_blink_is_boldbg)	/* Now actually make it so! */
1544 	make_blink_boldbg();
1545 #endif
1546 
1547     LYCursesON = TRUE;
1548 #if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
1549     if ((scrsize_x != 0) && (scrsize_y != 0)) {
1550 	if (saved_scrsize_x == 0) {
1551 	    saved_scrsize_x = COLS;
1552 	    saved_scrsize_y = LINES + 1;
1553 	}
1554 	CTRACE((tfp, "resize_term: x=%d, y=%d\n", scrsize_x, scrsize_y));
1555 	CTRACE((tfp, "saved terminal size: x=%d, y=%d\n", saved_scrsize_x, saved_scrsize_y));
1556 	resize_term(scrsize_y, scrsize_x);
1557 	LYlines = LYscreenHeight();
1558 	LYcols = LYscreenWidth();
1559 	LYStatusLine = -1;
1560 	LYclear();
1561 #ifdef _WINDOWS
1562 	adjustWindowPos();
1563 #endif
1564     }
1565     if (saved_scrsize_x2 == 0) {
1566 	if (saved_scrsize_x == 0) {
1567 	    saved_scrsize_x2 = COLS;
1568 	    saved_scrsize_y2 = LINES + 1;
1569 	} else {
1570 	    saved_scrsize_x2 = scrsize_x;
1571 	    saved_scrsize_y2 = scrsize_y;
1572 	}
1573     }
1574 #endif
1575     CTRACE((tfp, "start_curses: done.\n"));
1576 }				/* end of start_curses() */
1577 
lynx_enable_mouse(int state)1578 void lynx_enable_mouse(int state)
1579 {
1580 #ifdef USE_MOUSE
1581 /***********************************************************************/
1582 
1583 #if defined(WIN_EX)
1584 /* modify lynx_enable_mouse() for pdcurses configuration so that mouse support
1585    is disabled unless -use_mouse is specified
1586 */
1587     HANDLE hConIn = INVALID_HANDLE_VALUE;
1588 
1589     hConIn = GetStdHandle(STD_INPUT_HANDLE);
1590     if (LYUseMouse == 0) {
1591 	SetConsoleMode(hConIn, ENABLE_WINDOW_INPUT);
1592 	FlushConsoleInputBuffer(hConIn);
1593 	return;
1594     }
1595 #endif
1596 
1597     (void) state;
1598 
1599     if (LYUseMouse == 0)
1600 	return;
1601 
1602 #if defined(USE_SLANG)
1603     SLtt_set_mouse_mode(state, 0);
1604     SLtt_flush_output();
1605 #else
1606 
1607 #if defined(WIN_EX) && defined(PDCURSES)
1608     if (state) {
1609 	SetConsoleMode(hConIn, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
1610 	FlushConsoleInputBuffer(hConIn);
1611     }
1612 #else
1613 #if defined(NCURSES)
1614     if (state) {
1615 	/* Compensate for small value of maxclick in ncurses.  */
1616 	static int was = 0;
1617 
1618 	if (!was) {
1619 	    int old = mouseinterval(-1);
1620 
1621 	    was++;
1622 	    if (old < 200)	/* Default 166 */
1623 		mouseinterval(300);
1624 	}
1625 	/* Inform ncurses which mouse events we're interested in.
1626 	 * We shouldn't need to include BUTTONn_PRESSED and BUTTONn_RELEASED
1627 	 * events, since ncurses should translate them to click events. - kw
1628 	 * However, if we do not include them, then ncurses effectively
1629 	 * ignores mouseinterval(), thus translates *any* sequence of
1630 	 * press/release to a click, which leads to inconveniences.
1631 	 * We special-case these events in LYStrings.c.
1632 	 */
1633 	mousemask(BUTTON_CTRL | BUTTON_ALT
1634 		  | BUTTON1_PRESSED | BUTTON1_RELEASED
1635 		  | BUTTON1_CLICKED
1636 		  | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
1637 		  | BUTTON2_PRESSED | BUTTON2_RELEASED
1638 		  | BUTTON2_CLICKED
1639 		  | BUTTON3_PRESSED | BUTTON3_RELEASED
1640 		  | BUTTON3_CLICKED
1641 		  | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED
1642 #if NCURSES_MOUSE_VERSION >= 2
1643 		  | BUTTON4_PRESSED | BUTTON4_RELEASED
1644 		  | BUTTON4_CLICKED
1645 		  | BUTTON4_DOUBLE_CLICKED | BUTTON4_TRIPLE_CLICKED
1646 		  | BUTTON5_PRESSED | BUTTON5_RELEASED
1647 		  | BUTTON5_CLICKED
1648 		  | BUTTON5_DOUBLE_CLICKED | BUTTON5_TRIPLE_CLICKED
1649 #endif
1650 		  ,NULL);
1651     } else
1652 	mousemask(0, NULL);
1653 #endif /* NCURSES */
1654 #endif /* WIN_EX and PDCURSES */
1655 
1656 #if defined(PDCURSES)
1657     if (state)
1658 	mouse_set(
1659 		     BUTTON1_CLICKED | BUTTON1_PRESSED | BUTTON1_RELEASED |
1660 		     BUTTON2_CLICKED | BUTTON2_PRESSED | BUTTON2_RELEASED |
1661 		     BUTTON3_CLICKED | BUTTON3_PRESSED | BUTTON3_RELEASED);
1662 #endif
1663 #endif /* NOT USE_SLANG */
1664 
1665 /***********************************************************************/
1666 #endif /* USE_MOUSE */
1667 }
1668 
1669 /*
1670  * SVr4 curses (and ncurses) initialize the terminal I/O to raw mode, and
1671  * simulate other modes in the library.  This means that when running, it
1672  * simulates the OCRNL setting.  Normally that is not a problem.  However, when
1673  * spawning a subprocess (e.g., xli), the subprocess may write to the screen.
1674  * Fine so far - curses resets the terminal I/O to the normal state on exit.
1675  * But the subprocess' messages can still be coming to the screen when lynx
1676  * returns to the screen mode.  This function delays restoring OCRNL until
1677  * after the first getch() call.
1678  *
1679  * The OCRNL setting is controlled by nl()/nonl() of course - but we do not
1680  * want to give up that optimization since it would be a bit slower.  (Note -
1681  * slang does not use this optimization; if it did, the same screen glitch
1682  * would occur).
1683  *
1684  * FIXME:  for simplicity, only ncurses is implemented here - the TTY and
1685  * SET_TTY definitions are ncurses-specific.  The same effect could be done for
1686  * other curses implementations, since the "cur_term->Nttyb" part is common to
1687  * SVr4 curses.
1688  */
lynx_nl2crlf(int normal GCC_UNUSED)1689 void lynx_nl2crlf(int normal GCC_UNUSED)
1690 {
1691 #if defined(NCURSES_VERSION_PATCH) && defined(SET_TTY) && defined(TERMIOS) && defined(ONLCR)
1692     static TTY saved_tty;
1693     static int did_save = FALSE;
1694     static int waiting = FALSE;
1695     static int can_fix = TRUE;
1696 
1697     if (!did_save) {
1698 	if (cur_term == 0) {
1699 	    can_fix = FALSE;
1700 	} else {
1701 	    saved_tty = cur_term->Nttyb;
1702 	    did_save = TRUE;
1703 #if NCURSES_VERSION_PATCH < 20010529
1704 	    /* workaround for optimizer bug with nonl() */
1705 	    if ((tigetstr("cud1") != 0 && *tigetstr("cud1") == '\n')
1706 		|| (tigetstr("ind") != 0 && *tigetstr("ind") == '\n'))
1707 		can_fix = FALSE;
1708 #endif
1709 	}
1710     }
1711     if (can_fix) {
1712 	if (normal) {
1713 	    if (!waiting) {
1714 		cur_term->Nttyb.c_oflag |= ONLCR;
1715 		waiting = TRUE;
1716 		nonl();
1717 	    }
1718 	} else {
1719 	    if (waiting) {
1720 		cur_term->Nttyb = saved_tty;
1721 		SET_TTY(fileno(stdout), &saved_tty);
1722 		waiting = FALSE;
1723 		nl();
1724 		LYrefresh();
1725 	    }
1726 	}
1727     }
1728 #endif
1729 }
1730 
stop_curses(void)1731 void stop_curses(void)
1732 {
1733     if (LYCursesON) {
1734 #ifdef USE_COLOR_STYLE
1735 	FreeCachedStyles();
1736 #endif
1737 	echo();
1738     }
1739 #if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
1740     resetty();
1741 #endif
1742 
1743 #ifdef __DJGPP__
1744     _eth_release();
1745 #endif /* __DJGPP__ */
1746 
1747 /* ifdefs for non-Unix curses or slang */
1748 #if defined(__MINGW32__)
1749     chtype bb;
1750 
1751     bb = getbkgd(stdscr);
1752     bkgdset(0);
1753     clear();
1754     refresh();
1755     bkgdset(bb);
1756 #if defined(PDCURSES)
1757     endwin();
1758 #endif /* PDCURSES */
1759 
1760 #elif defined(DOSPATH) && !(defined(USE_SLANG) || defined(_WIN_CC))
1761 
1762 #if defined(PDCURSES)
1763     endwin();
1764 #endif /* PDCURSES */
1765 
1766 #ifdef __DJGPP__
1767     ScreenClear();
1768 #elif !defined(PDCURSES) /* some flavor of win32?  */
1769     clrscr();
1770 #endif /* win32 */
1771 
1772 #else /* Unix, etc */
1773 
1774     if (LYCursesON == TRUE) {
1775 	lynx_nl2crlf(TRUE);
1776 	lynx_enable_mouse(0);
1777 	if (LYscreen || lynx_called_initscr) {
1778 	    endwin();		/* stop curses */
1779 	    LYDELSCR();
1780 	}
1781     } else {
1782 #ifdef SH_EX
1783 	int i;
1784 
1785 	for (i = 0; i <= 3; i++) {
1786 	    printf("\r\n");
1787 	}
1788 #endif
1789     }
1790 
1791     fflush(stdout);
1792 #endif /* ifdefs for non-Unix curses or slang */
1793     fflush(stderr);
1794 
1795     LYCursesON = FALSE;
1796     CTRACE((tfp, "stop_curses: done.\n"));
1797 
1798 #if defined(SIGTSTP) && defined(USE_SLANG)
1799 #ifndef VMS
1800     if (!no_suspend)
1801 	signal(SIGTSTP, SIG_DFL);
1802 #endif /* !VMS */
1803 #endif /* SIGTSTP && USE_SLANG */
1804 
1805 #ifndef VMS
1806     signal(SIGINT, SIG_DFL);
1807 #endif /* !VMS */
1808 }
1809 
1810 #ifdef VMS
1811 
1812 #ifdef USE_SLANG
1813 extern void longname(char *, char *);
1814 #endif /* USE_SLANG */
1815 
1816 /*
1817  * Check terminal type, start curses & setup terminal.
1818  */
setup(char * terminal)1819 BOOLEAN setup(char *terminal)
1820 {
1821     int c;
1822     int status;
1823     char *dummy = 0, *cp, term[81];
1824 
1825     /*
1826      * If the display was not set by a command line option then see if it is
1827      * available from the environment.
1828      */
1829     if ((cp = LYgetXDisplay()) != 0) {
1830 	StrAllocCopy(x_display, cp);
1831     } else {
1832 	FREE(x_display);
1833     }
1834 
1835     /*
1836      * Get terminal type, and convert to lower case.
1837      */
1838     term[0] = '\0';
1839     longname(dummy, term);
1840     if (term[0] == '\0' && (form_get_data || form_post_data)) {
1841 	/*
1842 	 * Some yoyo used these under conditions which require -dump, so force
1843 	 * that mode here.  - FM
1844 	 */
1845 	dump_output_immediately = TRUE;
1846 	LYcols = DFT_COLS;
1847 	if (keypad_mode == NUMBERS_AS_ARROWS)
1848 	    keypad_mode = LINKS_ARE_NUMBERED;
1849 	status = mainloop();
1850 	exit_immediately(status);
1851     }
1852     LYLowerCase(term);
1853 
1854     printf("%s%s\n", gettext("Terminal ="), term);
1855     if ((strlen(term) < 5) ||
1856 	StrNCmp(term, "vt", 2) || !isdigit(term[2])) {
1857 	printf("%s\n",
1858 	       gettext("You must use a vt100, 200, etc. terminal with this program."));
1859 	printf(CONFIRM_PROCEED, "n/y");
1860 	c = getchar();
1861 	if (c != 'y' && c != 'Y') {
1862 	    printf("\n");
1863 	    return (FALSE);
1864 	}
1865 	strcpy(term, "vt100");
1866     }
1867 
1868     ttopen();
1869     start_curses();
1870 
1871     LYlines = LYscreenHeight();
1872     LYcols = LYscreenWidth();
1873 
1874     return (TRUE);
1875 }
1876 
1877 #else /* Not VMS: */
1878 
1879 /*
1880  * Check terminal type, start curses & setup terminal.
1881  */
setup(char * terminal)1882 BOOLEAN setup(char *terminal)
1883 {
1884     char *term_putenv = NULL;
1885     char *buffer = NULL;
1886     char *cp;
1887 
1888     /*
1889      * If the display was not set by a command line option then see if it is
1890      * available from the environment .
1891      */
1892     if ((cp = LYgetXDisplay()) != NULL) {
1893 	StrAllocCopy(x_display, cp);
1894     } else {
1895 	FREE(x_display);
1896     }
1897 
1898     if (terminal != NULL) {
1899 	HTSprintf0(&term_putenv, "TERM=%.106s", terminal);
1900 	(void) putenv(term_putenv);
1901     }
1902 
1903     /*
1904      * Query the terminal type.
1905      */
1906     if (dumbterm(LYGetEnv("TERM"))) {
1907 	printf("\n\n  %s\n\n", gettext("Your Terminal type is unknown!"));
1908 	printf("  %s [vt100] ", gettext("Enter a terminal type:"));
1909 
1910 	if (LYSafeGets(&buffer, stdin) != 0) {
1911 	    LYTrimLeading(buffer);
1912 	    LYTrimTrailing(buffer);
1913 	}
1914 
1915 	if (isEmpty(buffer))
1916 	    StrAllocCopy(buffer, "vt100");
1917 
1918 	HTSprintf0(&term_putenv, "TERM=%.106s", buffer);
1919 	FREE(buffer);
1920 
1921 	(void) putenv(term_putenv);
1922 	printf("\n%s %s\n", gettext("TERMINAL TYPE IS SET TO"),
1923 	       LYGetEnv("TERM"));
1924 	LYSleepMsg();
1925     }
1926 
1927     start_curses();
1928 
1929 #ifdef HAVE_TTYTYPE
1930     /*
1931      * Account for lossage on the 'sun' terminal type (80x24) Sun text console
1932      * driver.  It only supports reverse video, but all SGR sequences produce
1933      * that same reverse video, and the terminfo entry lists different SGRs for
1934      * 'bold' and 'rev'.  As a result, the current link is indistinguishable
1935      * from all other links.  The workaround here is to disable the 'rev'
1936      * capability.
1937      */
1938     if ((StrNCmp((const char *) ttytype, "sun", 3) == 0)) {
1939 	LYnoVideo(2);
1940     }
1941 #endif /* HAVE_TTYTYPE */
1942 
1943     LYlines = LYscreenHeight();
1944     LYcols = LYscreenWidth();
1945 
1946     return (1);
1947 }
1948 
dumbterm(char * terminal)1949 static int dumbterm(char *terminal)
1950 {
1951     int dumb = FALSE;
1952 
1953     /*
1954      * Began checking for terminal == NULL in case that TERM environment
1955      * variable is not set.  Thanks to Dick Wesseling (ftu@fi.ruu.nl).
1956      */
1957     if (terminal == NULL ||
1958 	!strcasecomp(terminal, "network") ||
1959 	!strcasecomp(terminal, "unknown") ||
1960 	!strcasecomp(terminal, "dialup") ||
1961 	!strcasecomp(terminal, "dumb") ||
1962 	!strcasecomp(terminal, "switch") ||
1963 	!strcasecomp(terminal, "ethernet"))
1964 	dumb = TRUE;
1965     return (dumb);
1966 }
1967 
1968 #ifdef FANCY_CURSES
1969 #ifndef USE_COLOR_STYLE
1970 #ifdef USE_COLOR_TABLE
LYsetWAttr(WINDOW * win)1971 static void LYsetWAttr(WINDOW * win)
1972 {
1973     (void) wattrset(win, LYgetTableAttr());
1974 }
1975 
LYaddWAttr(WINDOW * win,int a)1976 void LYaddWAttr(WINDOW * win, int a)
1977 {
1978     Current_Attr |= a;
1979     LYsetWAttr(win);
1980 }
1981 
LYaddAttr(int a)1982 void LYaddAttr(int a)
1983 {
1984     LYaddWAttr(LYwin, a);
1985 }
1986 
LYsubWAttr(WINDOW * win,int a)1987 void LYsubWAttr(WINDOW * win, int a)
1988 {
1989     Current_Attr &= ~a;
1990     LYsetWAttr(win);
1991 }
1992 
LYsubAttr(int a)1993 void LYsubAttr(int a)
1994 {
1995     LYsubWAttr(LYwin, a);
1996 }
1997 #endif /* USE_COLOR_TABLE */
1998 #endif /* !USE_COLOR_STYLE */
1999 #endif /* FANCY_CURSES */
2000 #endif /* VMS */
2001 
2002 /* Use this rather than the 'wprintw()' function to write a blank-padded
2003  * string to the given window, since someone's asserted that printw doesn't
2004  * handle 8-bit characters unlike addstr (though more info would be useful).
2005  *
2006  * We're blank-filling so that with SVr4 curses, it'll show the background
2007  * color to a uniform width in the popup-menu.
2008  */
2009 #ifndef USE_SLANG
LYpaddstr(WINDOW * the_window,int width,const char * the_string)2010 void LYpaddstr(WINDOW * the_window, int width, const char *the_string)
2011 {
2012     int y, x1, x2;
2013     int length = (int) strlen(the_string);
2014 
2015 #ifdef WIDEC_CURSES
2016     int actual = (int) LYstrCells(the_string);
2017 #endif
2018 
2019     getyx(the_window, y, x1);
2020     (void) y;
2021     if (width + x1 > LYcolLimit)
2022 	width = LYcolLimit - x1;
2023 #ifdef WIDEC_CURSES
2024     if (actual > width) {
2025 	actual = width;
2026 	/* FIXME: a binary search might be faster */
2027 	while (LYstrExtent(the_string, length, length) > actual) {
2028 	    --length;
2029 	}
2030     }
2031 #endif
2032     LYwaddnstr(the_window, the_string, (size_t) length);
2033     getyx(the_window, y, x2);
2034     width -= (x2 - x1);
2035     while (width-- > 0)
2036 	waddstr(the_window, " ");
2037 }
2038 
2039 /*
2040  * Work around limitation of curses' order-of-refresh by setting a pointer to
2041  * the topmost window that should be displayed.
2042  *
2043  * FIXME: the associated call on 'keypad()' is not needed for Unix, but
2044  * something in the OS/2 EMX port requires it.
2045  */
2046 static WINDOW *my_subwindow;
2047 
LYsubwindow(WINDOW * param)2048 void LYsubwindow(WINDOW * param)
2049 {
2050     if (param != 0) {
2051 	my_subwindow = param;
2052 #if defined(NCURSES) || defined(PDCURSES)
2053 	keypad(my_subwindow, TRUE);
2054 #if defined(USE_COLOR_STYLE)
2055 	LynxWChangeStyle(my_subwindow, s_menu_bg, STACK_ON);
2056 	{
2057 	    long b = LYgetattrs(my_subwindow);
2058 
2059 	    wbkgd(my_subwindow, (chtype) (b | ' '));
2060 	}
2061 	LynxWChangeStyle(my_subwindow, s_menu_bg, STACK_OFF);
2062 #elif defined(HAVE_GETBKGD) /* not defined in ncurses 1.8.7 */
2063 	wbkgd(my_subwindow, getbkgd(LYwin));
2064 #endif
2065 #endif
2066 	scrollok(my_subwindow, TRUE);
2067     } else {
2068 	touchwin(LYwin);
2069 	delwin(my_subwindow);
2070 	my_subwindow = 0;
2071     }
2072 }
2073 
LYtopwindow(void)2074 WINDOW *LYtopwindow(void)
2075 {
2076     return (my_subwindow ? my_subwindow : LYwin);
2077 }
2078 #endif
2079 
LYstartPopup(int * top_y,int * left_x,int * height,int * width)2080 WINDOW *LYstartPopup(int *top_y,
2081 		     int *left_x,
2082 		     int *height,
2083 		     int *width)
2084 {
2085     WINDOW *form_window = 0;
2086 
2087 #ifdef USE_SLANG
2088     static WINDOW fake_window;
2089 
2090     if (*left_x < 1 || (*left_x + *width + 4) >= LYcolLimit) {
2091 	*left_x = 1;
2092 	*width = LYcolLimit - 5;
2093     }
2094 
2095     SLsmg_fill_region(*top_y,
2096 		      *left_x - 1,
2097 		      (unsigned) *height,
2098 		      (unsigned) *width + 4,
2099 		      ' ');
2100     form_window = &fake_window;
2101     form_window->top_y = *top_y;
2102     form_window->left_x = *left_x;
2103     form_window->height = *height;
2104     form_window->width = *width;
2105 #else
2106     if (*left_x > 0 && (*left_x + *width + 4) < LYcolLimit)
2107 	form_window = newwin(*height, *width + 4, *top_y, *left_x - 1);
2108     if (form_window == 0) {
2109 	if (*width > LYcolLimit - 4) {
2110 	    *width = LYcolLimit - 4;
2111 	    *left_x = 1;
2112 	} else {
2113 	    *left_x = LYcolLimit - 4 - *width;
2114 	    if (*left_x <= 0)
2115 		*left_x = 1;
2116 	}
2117 	form_window = newwin(*height, *width + 4, *top_y, *left_x - 1);
2118     }
2119     if (form_window == 0) {
2120 	HTAlert(POPUP_FAILED);
2121     } else {
2122 	LYsubwindow(form_window);
2123     }
2124 #endif /* USE_SLANG */
2125     return form_window;
2126 }
2127 
LYstartTargetEmphasis(void)2128 void LYstartTargetEmphasis(void)
2129 {
2130 #ifdef USE_COLOR_STYLE
2131     if (s_whereis != NOSTYLE) {
2132 	curses_style(s_whereis, STACK_ON);
2133 	return;
2134     }
2135 #endif
2136 #if defined(FANCY_CURSES) || defined(USE_SLANG)
2137     lynx_start_bold();
2138     lynx_start_reverse();
2139 #endif /* FANCY_CURSES || USE_SLANG */
2140     lynx_start_underline();
2141 }
2142 
LYstopTargetEmphasis(void)2143 void LYstopTargetEmphasis(void)
2144 {
2145 #ifdef USE_COLOR_STYLE
2146     if (s_whereis != NOSTYLE) {
2147 	curses_style(s_whereis, STACK_OFF);
2148 	return;
2149     }
2150 #endif
2151     lynx_stop_underline();
2152 #if defined(FANCY_CURSES) || defined(USE_SLANG)
2153     lynx_stop_reverse();
2154     lynx_stop_bold();
2155 #endif /* FANCY_CURSES || USE_SLANG */
2156 }
2157 
2158 /*
2159  * Accommodate the different flavors of touchline
2160  */
LYtouchline(int row)2161 void LYtouchline(int row)
2162 {
2163 #if defined(HAVE_WREDRAWLN) && !defined(NCURSES_VERSION)
2164     wredrawln(LYwin, row, 1);
2165 #else
2166 #if defined(HAVE_TOUCHLINE)
2167     /* touchline() is not available on VMS before version 7.0, and then only on
2168      * Alpha, since prior ports of curses were broken.  BSD touchline() has a
2169      * 4th parameter since it is used internally by touchwin().
2170      */
2171 #if defined(HAVE_BSD_TOUCHLINE)
2172     touchline(LYwin, row, 0, COLS);
2173 #else
2174     touchline(LYwin, row, 1);
2175 #endif
2176 #else
2177 #if !defined(USE_SLANG)
2178     touchwin(LYwin);
2179 #else
2180     SLsmg_touch_lines(row, 1);
2181 #endif
2182 #endif
2183 #endif
2184 }
2185 
2186 /*
2187  * Wrapper for waddnstr().
2188  */
LYwaddnstr(WINDOW * w GCC_UNUSED,const char * src,size_t len)2189 void LYwaddnstr(WINDOW * w GCC_UNUSED,
2190 		const char *src,
2191 		size_t len)
2192 {
2193     int y0, x0;
2194     int y, x;
2195     size_t inx;
2196 
2197 #ifdef USE_CURSES_PADS
2198     /*
2199      * If we've configured to use pads for left/right scrolling, that can
2200      * interfere with calls to this function that assume they're wrapping.
2201      * Writing to a pad which is wider than the screen will simply not wrap.
2202      *
2203      * Link-highlighting uses wrapping.  You can see this by viewing the
2204      * options screen in a terminal which is narrower than 80 columns.
2205      *
2206      * Check for that case, and use curses' wrapping in a derived window to
2207      * simplify things, e.g., in case the string contains multibyte or
2208      * multicolumn characters.
2209      */
2210     getyx(LYwin, y0, x0);
2211 
2212     if (LYuseCursesPads
2213 	&& (LYwin == w)
2214 	&& (LYshiftWin == 0)
2215 	&& LYwideLines == FALSE
2216 	&& ((int) len > (LYcolLimit - x0))
2217 	&& (y0 >= 0)
2218 	&& (x0 >= 0)
2219 	&& (x0 < LYcolLimit)) {
2220 	WINDOW *sub = derwin(LYwin, LYlines, LYcolLimit, 0, 0);
2221 
2222 	if (sub != 0) {
2223 	    wmove(sub, y0, x0);
2224 	    LYwideLines = TRUE;
2225 	    LYwaddnstr(sub, src, len);
2226 	    getyx(sub, y0, x0);
2227 	    delwin(sub);
2228 	    wmove(LYwin, y0, x0);
2229 	}
2230 	LYwideLines = FALSE;
2231 
2232 	return;
2233     }
2234 #endif
2235     /*
2236      * We only want to trace this function for the color-style code.  It would
2237      * be too much logging if not needed.
2238      */
2239 #ifdef USE_COLOR_STYLE
2240     if (TRACE) {
2241 	LYGetYX(y, x);
2242 	CTRACE2(TRACE_STYLE, (tfp, "[%2d,%2d] LYwaddnstr(%.*s, %u)\n",
2243 			      y, x, (int) len, src, (unsigned) len));
2244     }
2245 #endif
2246     LYGetYX(y0, x0);
2247 
2248     for (inx = 0; inx < len; ++inx) {
2249 	/*
2250 	 * Do tab-expansion relative to the base of the string (rather than
2251 	 * the screen) so that tabs in a TEXTAREA will look right.
2252 	 */
2253 	if (src[inx] == '\t') {
2254 	    LYGetYX(y, x);
2255 	    while ((++x - x0) % 8)
2256 		waddch(w, ' ');
2257 	    waddch(w, ' ');
2258 	} else {
2259 	    waddch(w, UCH(src[inx]));
2260 	}
2261     }
2262 }
2263 
2264 /*
2265  * Determine the number of cells the given string would take up on the screen,
2266  * limited (in the case of wide characters) by the maxCells parameter.
2267  *
2268  * If the returnCellNum parameter is TRUE, return the number of cells;
2269  * otherwise, return the length (limited by the len parameter) of the prefix of
2270  * the string that fits in maxCells cells.
2271  */
2272 static
LYstrExtent0(const char * string,int len,int maxCells GCC_UNUSED,int retCellNum GCC_UNUSED)2273 int LYstrExtent0(const char *string,
2274 		 int len,
2275 		 int maxCells GCC_UNUSED,
2276 		 int retCellNum GCC_UNUSED)
2277 {
2278     int used = (len < 0 ? (int) strlen(string) : len);
2279     int result = used;
2280 
2281 #ifdef WIDEC_CURSES
2282     if (used > 0 && lynx_called_initscr) {
2283 	static WINDOW *fake_win;
2284 	static int fake_max;
2285 
2286 	if (fake_max < maxCells) {
2287 	    fake_max = (maxCells + 1) * 2;
2288 	    if (fake_win != 0) {
2289 		delwin(fake_win);
2290 		fake_win = 0;
2291 	    }
2292 	}
2293 	if (fake_win == 0) {
2294 	    fake_win = newwin(2, fake_max, 0, 0);
2295 	}
2296 	if (fake_win != 0) {
2297 	    int new_x = 0;
2298 	    int new_y = 0;
2299 	    int x = 0;
2300 	    int n;
2301 
2302 	    wmove(fake_win, 0, 0);
2303 	    for (n = 0; n < used; ++n) {
2304 		if (IsNormalChar(string[n])) {
2305 		    waddch(fake_win, UCH(string[n]));
2306 		    getyx(fake_win, new_y, new_x);
2307 		    if (new_y > 0 || new_x > maxCells)
2308 			break;
2309 		    x = new_x;
2310 		}
2311 	    }
2312 	    result = (retCellNum ? x : n);
2313 	}
2314     }
2315 #endif
2316     return result;
2317 }
2318 
2319 /*
2320  * Determine the number of cells the given string would take up on the screen,
2321  * limited by the maxCells parameter.  This is used for constructing aligned
2322  * text in the options and similar forms.
2323  *
2324  * FIXME: make this account for wrapping, too.
2325  * FIXME: make this useful for "lynx -dump", which hasn't initialized curses.
2326  */
LYstrExtent(const char * string,int len,int maxCells)2327 int LYstrExtent(const char *string, int len, int maxCells)
2328 {
2329     int result = LYstrExtent0(string, len, maxCells, TRUE);
2330 
2331     return (result > maxCells ? maxCells : result);
2332 }
2333 
2334 /*
2335  * Return the number of cells in the first 'len' bytes of the string.
2336  *
2337  * This relies upon the coincidence that multicell characters use at least as
2338  * many bytes as cells.  But we have to account for tab, which can use 8, and
2339  * control characters which use 2.
2340  */
LYstrExtent2(const char * string,int len)2341 int LYstrExtent2(const char *string, int len)
2342 {
2343     return LYstrExtent(string, len, 8 * len);
2344 }
2345 
2346 /*
2347  * Determine the longest prefix of a string that fits in a given number of
2348  * cells and return its length.
2349  */
LYstrFittable(const char * string,int maxCells)2350 int LYstrFittable(const char *string, int maxCells)
2351 {
2352     return LYstrExtent0(string, -1, maxCells, FALSE);
2353 }
2354 
2355 /*
2356  * Returns the total number of cells that the string would use.
2357  */
LYstrCells(const char * string)2358 int LYstrCells(const char *string)
2359 {
2360     return LYstrExtent2(string, (int) strlen(string));
2361 }
2362 
2363 #ifdef VMS
2364 /*
2365  *	Cut-down termio --
2366  *		Do character-oriented stream input for Jeff.
2367  *		Code ripped off from Micro-Emacs 3.7 by Daniel Lawrence.
2368  *
2369  *		Ever-so-slightly modified by Kathryn Huxtable.	29-Jan-1991.
2370  *		Cut down for Lou.  8 Sep 1992.
2371  *		Cut down farther for Lou.  19 Apr 1993.
2372  *			We don't set PASSALL or PASTHRU since we don't
2373  *			want to block CTRL/C, CTRL/Y, CTRL/S or CTRL/Q.
2374  *			Simply setting NOECHO and doing timed reads
2375  *			is sufficient.
2376  *		Further mods by Fote.  29-June-1993
2377  *			ttopen() and ttclose() are now terminal initialization
2378  *			 and restoration procedures, called once at startup
2379  *			 and at exit, respectively, of the LYNX image.
2380  *			ttclose() should be called before an exit from LYNX
2381  *			 no matter how the exit is invoked.
2382  *			setup(terminal) does the ttopen().
2383  *			cleanup() calls cleanup_files() and ttclose().
2384  *			ttgetc() now handles NOECHO and NOFLITR (instead of
2385  *			 setting the terminal itself to NOECHO in ttopen()).
2386  *			VMSsignal() added for handling both Ctrl-C *and* Ctrl-Y
2387  *			 interrupts, and disabling system response to Ctrl-T.
2388  *		Further mods by Fote.  15-Dec-1993
2389  *			Added edit handler in ttopen() which will invoke
2390  *			 VMSexit() and behave intelligently on ACCVIO's.
2391  *		Further mods by Fote.  29-Dec-1993
2392  *			Simplified ttgetc().
2393  *		Further mods by Fote.  16-Jan-1994
2394  *			Added code in ttopen() which will invoke VMSVersion()
2395  *			 to get the version of VMS as VersionVMS for use by
2396  *			 by new or modified interrupt or spawning routines.
2397  *		Further mods by Fote.  27-Jan-1994
2398  *			Added back a typeahead() which supports 'z' or 'Z' as
2399  *			an "Zap transfer" command via HTCheckForInterrupt()
2400  *			in LYUtils.c.
2401  */
2402 
2403 #include <descrip.h>
2404 #include <iodef.h>
2405 #include <ssdef.h>
2406 #include <msgdef.h>
2407 #include <ttdef.h>
2408 #include <tt2def.h>
2409 #include <libclidef.h>
2410 #include <lib$routines.h>
2411 #include <starlet.h>
2412 #include <clidef.h>
2413 #include <syidef.h>
2414 #ifdef signal
2415 #undef signal
2416 #endif /* signal */
2417 #include <signal.h>
2418 #ifdef system
2419 #undef system
2420 #endif /* system */
2421 #include <processes.h>
2422 #include <LYVMSdef.h>
2423 
2424 #define EFN	0		/* Event flag                   */
2425 
2426 static unsigned char buffer[20];	/* Input buffer                 */
2427 static int in_pos, in_len;	/* For escape sequences         */
2428 static int oldmode[3];		/* Old TTY mode bits            */
2429 static int newmode[3];		/* New TTY mode bits            */
2430 static short iochan;		/* TTY I/O channel              */
2431 static $DESCRIPTOR(term_nam_dsc, "TT");		/* Descriptor for iochan        */
2432 static unsigned long mask = LIB$M_CLI_CTRLY | LIB$M_CLI_CTRLT;	/* ^Y and ^T */
2433 static unsigned long old_msk;	/* Saved control mask           */
2434 static short trap_flag = FALSE;	/* TRUE if AST is set           */
2435 BOOLEAN DidCleanup = FALSE;	/* Exit handler flag            */
2436 static char VersionVMS[20];	/* Version of VMS               */
2437 
VMSVersion(char * VerString,int VerLen)2438 int VMSVersion(char *VerString,
2439 	       int VerLen)
2440 {
2441     unsigned long status, itm_cod = SYI$_VERSION;
2442     int i, verlen = 0;
2443     struct dsc$descriptor version;
2444     char *m;
2445 
2446     version.dsc$a_pointer = VerString;
2447     version.dsc$w_length = VerLen - 1;
2448     version.dsc$b_dtype = DSC$K_DTYPE_B;
2449     version.dsc$b_class = DSC$K_CLASS_S;
2450 
2451     status = lib$getsyi(&itm_cod, 0, &version, &verlen, 0, 0);
2452     if (!(status & 1) || verlen == 0)
2453 	return 0;
2454 
2455     /*
2456      * Cut out trailing spaces
2457      */
2458     for (m = VerString + verlen, i = verlen - 1; i > 0 && VerString[i] == ' '; --i)
2459 	*(--m) = '\0';
2460 
2461     return strlen(VerString) + 1;	/* Transmit ending 0 too */
2462 }
2463 
VMSexit(void)2464 void VMSexit(void)
2465 {
2466     /*
2467      * If we get here and DidCleanup is not set, it was via an ACCVIO, or
2468      * outofmemory forced exit, so make *sure* we attempt a cleanup and reset
2469      * the terminal.
2470      */
2471     if (!DidCleanup) {
2472 	if (LYOutOfMemory == FALSE) {
2473 	    fprintf(stderr,
2474 		    gettext("\nA Fatal error has occurred in %s Ver. %s\n"),
2475 		    LYNX_NAME, LYNX_VERSION);
2476 	    fprintf(stderr,
2477 		    gettext("\nPlease notify your system administrator to confirm a bug, and if\n\
2478 confirmed, to notify the lynx-dev list.  Bug reports should have concise\n\
2479 descriptions of the command and/or URL which causes the problem, the\n\
2480 operating system name with version number, the TCPIP implementation, the\n\
2481 TRACEBACK if it can be captured, and any other relevant information.\n"));
2482 
2483 	    if (LYTraceLogFP == NULL) {
2484 		fprintf(stderr, RETURN_TO_CLEANUP);
2485 		(void) getchar();
2486 	    }
2487 	} else if (LYCursesON) {
2488 	    HTAlert(MEMORY_EXHAUSTED_ABORT);
2489 	}
2490 	cleanup();
2491     }
2492     if (LYOutOfMemory == TRUE) {
2493 	printf("\r\n%s\r\n\r\n", MEMORY_EXHAUSTED_ABORT);
2494 	fflush(stdout);
2495 	fflush(stderr);
2496     }
2497 }
2498 
2499 /*
2500  *	TTOPEN --
2501  *		This function is called once to set up the terminal
2502  *		device streams.  It translates TT until it finds
2503  *		the terminal, then assigns a channel to it, sets it
2504  *		to EDIT, and sets up the Ctrl-C and Ctrl-Y interrupt
2505  *		handling.
2506  */
ttopen(void)2507 int ttopen(void)
2508 {
2509     int iosb[2];
2510     int status;
2511     static unsigned long condition;
2512     static struct _exit_block {
2513 	unsigned long forward;
2514 	unsigned long address;
2515 	unsigned long zero;
2516 	unsigned long condition;
2517     } exit_handler_block;
2518 
2519     status = sys$assign(&term_nam_dsc, &iochan, 0, 0);
2520     if (status != SS$_NORMAL)
2521 	exit_immediately(status);
2522 
2523     status = sys$qiow(EFN, iochan, IO$_SENSEMODE, &iosb, 0, 0,
2524 		      &oldmode, sizeof(oldmode), 0, 0, 0, 0);
2525     if (status != SS$_NORMAL)
2526 	exit_immediately(status);
2527 
2528     status = iosb[0] & 0xFFFF;
2529     if (status != SS$_NORMAL)
2530 	exit_immediately(status);
2531 
2532     newmode[0] = oldmode[0];
2533     newmode[1] = oldmode[1];
2534     newmode[2] = oldmode[2] | TT2$M_EDIT;
2535 
2536     status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
2537 		      &newmode, sizeof(newmode), 0, 0, 0, 0);
2538     if (status != SS$_NORMAL)
2539 	exit_immediately(status);
2540 
2541     status = iosb[0] & 0xFFFF;
2542     if (status != SS$_NORMAL)
2543 	exit_immediately(status);
2544 
2545     /*
2546      * Declare the exit handler block.
2547      */
2548     exit_handler_block.forward = 0;
2549     exit_handler_block.address = (unsigned long) &VMSexit;
2550     exit_handler_block.zero = 0;
2551     exit_handler_block.condition = (unsigned long) &condition;
2552     status = sys$dclexh(&exit_handler_block);
2553     if (status != SS$_NORMAL)
2554 	exit_immediately(status);
2555 
2556     /*
2557      * Set the AST.
2558      */
2559     lib$disable_ctrl(&mask, &old_msk);
2560     trap_flag = TRUE;
2561     status = sys$qiow(EFN, iochan,
2562 		      IO$_SETMODE | IO$M_CTRLCAST | IO$M_CTRLYAST,
2563 		      &iosb, 0, 0,
2564 		      &cleanup_sig, SIGINT, 0, 0, 0, 0);
2565     if (status != SS$_NORMAL) {
2566 	lib$enable_ctrl(&old_msk);
2567 	exit_immediately(status);
2568     }
2569 
2570     /*
2571      * Get the version of VMS.
2572      */
2573     if (VMSVersion(VersionVMS, 20) < 3)
2574 	/*
2575 	 * Load zeros on error.
2576 	 */
2577 	strcpy(VersionVMS, "V0.0-0");
2578 
2579     return (0);
2580 }				/*  ttopen  */
2581 
2582 /*
2583  *	TTCLOSE --
2584  *		This function gets called just before we go back home
2585  *		to the command interpreter.  It puts the terminal back
2586  *		in a reasonable state.
2587  */
ttclose(void)2588 int ttclose(void)
2589 {
2590     int status;
2591     int iosb[1];
2592 
2593     status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
2594 		      &oldmode, sizeof(oldmode), 0, 0, 0, 0);
2595 
2596     if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
2597 	exit_immediately(status);
2598 
2599     if (trap_flag) {
2600 	status = sys$dassgn(iochan);
2601 	status = lib$enable_ctrl(&old_msk);
2602 	trap_flag = FALSE;
2603     }
2604     return (0);
2605 }				/*  ttclose  */
2606 
2607 /*
2608  *	TTGETC --
2609  *		Read a character from the terminal, with NOECHO and NOFILTR.
2610  */
ttgetc(void)2611 int ttgetc(void)
2612 {
2613     int status;
2614     unsigned short iosb[4];
2615 
2616     if (in_pos < in_len)
2617 	return (buffer[in_pos++]);
2618 
2619     status = sys$qiow(EFN, iochan,
2620 		      IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR,
2621 		      &iosb, 0, 0,
2622 		      &buffer, 1, 0, 0, 0, 0);
2623     if ((status & 1) == 1)
2624 	status = iosb[0];
2625     if (status == SS$_PARTESCAPE) {
2626 	/*
2627 	 * Escape sequence in progress.  Fake a successful read.
2628 	 */
2629 	status = 1;
2630     }
2631     if ((status & 1) != 1 && status != SS$_DATAOVERUN)
2632 	exit_immediately(status);
2633     in_pos = 1;
2634     in_len = iosb[1] + iosb[3];
2635     return (buffer[0]);
2636 }
2637 
2638 /*
2639  *	TYPEAHEAD -- Fote Macrides 27-Jan-1994
2640  *		Check whether a keystroke has been entered, and return
2641  *		 it, or -1 if none was entered.
2642  */
typeahead(void)2643 int typeahead(void)
2644 {
2645     int status;
2646     unsigned short iosb[4];
2647 
2648     if (dump_output_immediately)
2649 	return -1;
2650 
2651     if (in_pos < in_len)
2652 	return (buffer[in_pos++]);
2653 
2654   again:
2655     status = sys$qiow(EFN, iochan,
2656 		      IO$_READVBLK | IO$M_TIMED | IO$M_NOECHO | IO$M_NOFILTR,
2657 		      &iosb, 0, 0,
2658 		      &buffer, 1, 0, 0, 0, 0);
2659     if ((status & 1) == 1)
2660 	status = iosb[0];
2661     if (status == SS$_PARTESCAPE) {
2662 	/*
2663 	 * Escape sequence in progress, finish reading it.
2664 	 */
2665 	goto again;
2666     }
2667 
2668     in_pos = 1;
2669     in_len = iosb[1] + iosb[3];
2670     if (status == SS$_TIMEOUT || status == SS$_DATAOVERUN)
2671 	return (-1);
2672     return (buffer[0]);
2673 }
2674 
2675 /*
2676  *	VMSSIGNAL -- Fote Macrides 29-Jun-1993
2677  *		Sets up AST for both Ctrl-C and Ctrl-Y, with system response
2678  *		 to Ctrl-T disabled.  If called with a sig other than SIGINT,
2679  *		 it will use the C library's system(sig, func).
2680  *		The equivalent of VMSsignal(SIGINT, cleanup_sig) is done on
2681  *		 intialization by ttopen(), so don't do it again.
2682  *		VMSsignal(SIGINT, SIG_DFL) is treated as a call to ttclose().
2683  *		Call VMSsignal(SIGINT, SIG_IGN) before system() calls to
2684  *		 enable Ctrl-C and Ctrl-Y in the subprocess, and then call
2685  *		 VMSsignal(SIG_INT, cleanup_sig) on return from the subprocess.
2686  *		For funcs which set flags and do not invoke an exit from
2687  *		 LYNX, the func should reassert itself.
2688  *		The VMS signal() calls do not fully emulate the Unix calls,
2689  *		 and VMSsignal() is just a "helper", also not a full emulation.
2690  */
2691 
VMSsignal(int sig,void (* func)())2692 void VMSsignal(int sig,
2693 	       void (*func) ())
2694 {
2695     int status;
2696     short iosb[4];
2697     static int SIG_IGN_flag;
2698 
2699     /*
2700      * Pass all signals other than SIGINT to signal().
2701      * Also pass SIGINT to signal() if we're dumping.
2702      */
2703     if (sig != SIGINT || dump_output_immediately) {
2704 	signal(sig, func);
2705 	return;
2706     }
2707 
2708     /*
2709      * If func is SIG_DFL, treat it as ttclose().
2710      */
2711     if (func == SIG_DFL) {
2712 	ttclose();
2713 	return;
2714     }
2715 
2716     /*
2717      * Clear any previous AST.
2718      */
2719     if (trap_flag) {
2720 	status = sys$dassgn(iochan);
2721 	status = lib$enable_ctrl(&old_msk);
2722 	trap_flag = FALSE;
2723     }
2724 
2725     /*
2726      * If func is SIG_IGN, leave the TT channel closed and the system response
2727      * to interrupts enabled for system() calls.
2728      */
2729     if (func == SIG_IGN)
2730 	return;
2731 
2732     /*
2733      * If we get to here, we have a LYNX func, so set the AST.
2734      */
2735     lib$disable_ctrl(&mask, &old_msk);
2736     trap_flag = TRUE;
2737     status = sys$assign(&term_nam_dsc, &iochan, 0, 0);
2738     status = sys$qiow(EFN, iochan,
2739 		      IO$_SETMODE | IO$M_CTRLCAST | IO$M_CTRLYAST,
2740 		      &iosb, 0, 0,
2741 		      func, SIGINT, 0, 0, 0, 0);
2742 
2743 }				/* VMSsignal */
2744 
2745 /*
2746  * DCLspawn_exception, spawn_DCLprocess, DCLsystem -- F.Macrides 16-Jan-1994
2747  *	Exception-handler routines for regulating interrupts and enabling
2748  *	Control-T during spawns.  Includes TRUSTED flag for versions of VMS
2749  *	which require it in captive accounts.  This code should be used
2750  *	instead of the VAXC or DECC system(), by including LYUtils.h in
2751  *	modules which have system() calls.  It helps ensure that we return
2752  *	to Lynx instead of breaking out to DCL if a user issues interrupts
2753  *	or generates an ACCVIO during spawns.
2754  */
2755 #ifdef __DECC
DCLspawn_exception(void * sigarr,void * mecharr)2756 static unsigned int DCLspawn_exception(void *sigarr,
2757 				       void *mecharr)
2758 #else
2759 static int DCLspawn_exception(void *sigarr,
2760 			      void *mecharr)
2761 #endif				/* __DECC */
2762 {
2763     int status;
2764 
2765     status = lib$sig_to_ret(sigarr, mecharr);
2766     return (SS$_UNWIND);
2767 }
2768 
spawn_DCLprocess(char * command)2769 static int spawn_DCLprocess(char *command)
2770 {
2771     int status;
2772     unsigned long Status = 0;
2773 
2774     /*
2775      * Keep DECC from complaining.
2776      */
2777     struct dsc$descriptor_s command_desc;
2778 
2779     command_desc.dsc$w_length = strlen(command);
2780     command_desc.dsc$b_class = DSC$K_CLASS_S;
2781     command_desc.dsc$b_dtype = DSC$K_DTYPE_T;
2782     command_desc.dsc$a_pointer = command;
2783 
2784     VAXC$ESTABLISH(DCLspawn_exception);
2785 
2786 #ifdef __ALPHA /** OpenVMS/AXP lacked the TRUSTED flag before v6.1 **/
2787     if (VersionVMS[1] > '6' ||
2788 	(VersionVMS[1] == '6' && VersionVMS[2] == '.' &&
2789 	 VersionVMS[3] >= '1'))
2790 #else
2791     if (VersionVMS[1] >= '6')
2792 #endif /* __ALPHA */
2793     {
2794 	/*
2795 	 * Include TRUSTED flag.
2796 	 */
2797 	unsigned long trusted = CLI$M_TRUSTED;
2798 
2799 	status = lib$spawn(&command_desc, 0, 0, &trusted,
2800 			   0, 0, &Status);
2801 	/*
2802 	 * If it was invalid, try again without the flag.
2803 	 */
2804 	if (status == LIB$_INVARG)
2805 	    status = lib$spawn(&command_desc, 0, 0, 0,
2806 			       0, 0, &Status);
2807     } else
2808 	status = lib$spawn(&command_desc, 0, 0, 0,
2809 			   0, 0, &Status);
2810     /*
2811      * Return -1 on error.
2812      */
2813     if ((status & 1) != 1 || (Status & 1) != 1)
2814 	return (-1);
2815     /*
2816      * Return 0 on success.
2817      */
2818     return (0);
2819 }
2820 
DCLsystem(char * command)2821 int DCLsystem(char *command)
2822 {
2823     int status;
2824 
2825     VMSsignal(SIGINT, SIG_IGN);
2826     status = spawn_DCLprocess(command);
2827     VMSsignal(SIGINT, cleanup_sig);
2828     /*
2829      * Returns 0 on success, -1 any error.
2830      */
2831     return (status);
2832 }
2833 #endif /* VMS */
2834 
2835 /*
2836  * Return the physical screen dimensions that we're allowed to use.
2837  */
LYscreenHeight(void)2838 int LYscreenHeight(void)
2839 {
2840     int result = LINES;
2841 
2842     if (result <= 0)
2843 	result = DFT_ROWS;
2844     return result;
2845 }
2846 
LYscreenWidth(void)2847 int LYscreenWidth(void)
2848 {
2849     int result = COLS;
2850 
2851 #if defined(PDCURSES_EXP) && defined(WIN_EX) && defined(CJK_EX)		/* 1999/08/26 (Thu) 17:53:38 */
2852     {
2853 	extern int current_codepage;	/* PDCurses lib. */
2854 
2855 	if (current_codepage == 932)
2856 	    result--;
2857     }
2858 #endif
2859     if (result <= 0)
2860 	result = DFT_COLS;
2861     return result;
2862 }
2863 
2864 /*
2865  * Set the window's background color (make the pad's color agree), e.g., when
2866  * we have just parsed it from the config file, or after clearing the screen.
2867  */
LYnormalColor(void)2868 void LYnormalColor(void)
2869 {
2870 #if defined(USE_COLOR_STYLE) && defined(USE_CURSES_PADS)
2871     if (LYwin != stdscr) {
2872 	int color = displayStyles[DSTYLE_NORMAL].color;
2873 
2874 	if (color >= 0) {
2875 	    wbkgd(LYwin, (chtype) (color | ' '));
2876 	    LYrefresh();
2877 	}
2878     }
2879 #endif
2880 }
2881 
2882 /*
2883  * The functions ifdef'd with USE_CURSES_PADS are implemented that way so we
2884  * don't break the slang configuration.
2885  */
LYclear(void)2886 void LYclear(void)
2887 {
2888 #ifdef USE_CURSES_PADS
2889     wclear(LYwin);
2890 #else
2891     clear();
2892 #endif
2893     LYnormalColor();
2894 }
2895 
LYclrtoeol(void)2896 void LYclrtoeol(void)
2897 {
2898 #ifdef USE_CURSES_PADS
2899     wclrtoeol(LYwin);
2900 #else
2901     clrtoeol();
2902 #endif
2903 }
2904 
LYerase(void)2905 void LYerase(void)
2906 {
2907 #ifdef USE_CURSES_PADS
2908     werase(LYwin);
2909 #else
2910     erase();
2911 #endif
2912     LYnormalColor();
2913 }
2914 
LYmove(int y,int x)2915 void LYmove(int y, int x)
2916 {
2917 #ifdef USE_CURSES_PADS
2918     wmove(LYwin, y, x);
2919 #else
2920     move(y, x);
2921 #endif
2922 }
2923 
LYrefresh(void)2924 void LYrefresh(void)
2925 {
2926 #ifdef USE_CURSES_PADS
2927     if (LYwin != stdscr) {
2928 	/*
2929 	 * Workaround for special case where lynx is prompting for a mailto,
2930 	 * and has a subject line that is wider than the screen.  The
2931 	 * wnoutrefresh() call resets newscr's position to match stdscr's,
2932 	 * which happens to be the window's origin because we were not updating
2933 	 * that, and other stray wmoves in lynx fail because the coordinate
2934 	 * is on/after the right margin.  Force things to look ok here.
2935 	 */
2936 	int y, x;
2937 
2938 	getyx(LYwin, y, x);
2939 	if (y < 0)
2940 	    y = 0;
2941 	if (x < 0)
2942 	    x = 0;
2943 	if (x > LYcolLimit)
2944 	    x = LYcolLimit;
2945 	wmove(stdscr, y, x);
2946 
2947 	wnoutrefresh(stdscr);
2948 	pnoutrefresh(LYwin, 0, LYshiftWin, 0, 0, LYlines, LYscreenWidth() - 1);
2949 
2950 	/*
2951 	 * Keep a popup window visible.  This can happen if the user presses
2952 	 * '/' to do a search within a popup.
2953 	 */
2954 	if (my_subwindow != 0) {
2955 	    touchwin(my_subwindow);
2956 	    wnoutrefresh(my_subwindow);
2957 	}
2958 	doupdate();
2959     } else {
2960 	refresh();
2961     }
2962 #else
2963     refresh();
2964 #endif
2965 }
2966 
lynx_force_repaint(void)2967 void lynx_force_repaint(void)
2968 {
2969     clearok(curscr, TRUE);
2970 }
2971 
lynx_start_title_color(void)2972 void lynx_start_title_color(void)
2973 {
2974 #ifdef SH_EX
2975     lynx_start_reverse();
2976 #endif
2977 }
2978 
lynx_stop_title_color(void)2979 void lynx_stop_title_color(void)
2980 {
2981 #ifdef SH_EX
2982     lynx_stop_reverse();
2983 #endif
2984 }
2985 
lynx_start_link_color(int flag,int pending)2986 void lynx_start_link_color(int flag,
2987 			   int pending)
2988 {
2989     if (flag) {
2990 	/* makes some terminals work wrong because
2991 	 * they can't handle two attributes at the
2992 	 * same time
2993 	 */
2994 	/* lynx_start_bold();  */
2995 	lynx_start_reverse();
2996 #if defined(USE_SLANG)
2997 #ifndef __DJGPP__
2998 	if (SLtt_Use_Ansi_Colors)
2999 #endif /* !__DJGPP__ */
3000 	    lynx_start_underline();
3001 #endif /* USE_SLANG */
3002 #if defined(FANCY_CURSES) && defined(COLOR_CURSES)
3003 	if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
3004 	    lynx_start_underline();
3005 #endif /* USE_SLANG */
3006     } else {
3007 	lynx_start_bold();
3008 	/*
3009 	 * Make sure when flag is OFF that "unhighlighted" links will be
3010 	 * underlined if appropriate.  - LE & FM
3011 	 */
3012 	if (pending)
3013 	    lynx_start_underline();
3014     }
3015 }
3016 
lynx_stop_link_color(int flag,int pending GCC_UNUSED)3017 void lynx_stop_link_color(int flag,
3018 			  int pending GCC_UNUSED)
3019 {
3020 #ifdef USE_COLOR_STYLE
3021     LynxChangeStyle(flag == TRUE ? s_alink : s_a, ABS_OFF);
3022 #else
3023     if (flag) {
3024 	lynx_stop_reverse();
3025 #if defined(USE_SLANG)
3026 #ifndef __DJGPP__
3027 	if (SLtt_Use_Ansi_Colors)
3028 #endif /* !__DJGPP__ */
3029 	    lynx_stop_underline();
3030 #endif /* USE_SLANG */
3031 #if defined(FANCY_CURSES) && defined(COLOR_CURSES)
3032 	if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
3033 	    lynx_stop_underline();
3034 #endif /* FANCY_CURSES && COLOR_CURSES */
3035     } else {
3036 	lynx_stop_bold();
3037 	/*
3038 	 * If underlining was turned on above, turn it off.  - LE & FM
3039 	 */
3040 	if (pending)
3041 	    lynx_stop_underline();
3042     }
3043 #endif
3044 }
3045 
3046 /* FIXME: consider inlining these */
3047 
lynx_stop_target_color(void)3048 void lynx_stop_target_color(void)
3049 {
3050     lynx_stop_underline();
3051     lynx_stop_reverse();
3052     lynx_stop_bold();
3053 }
3054 
lynx_start_target_color(void)3055 void lynx_start_target_color(void)
3056 {
3057     lynx_start_bold();
3058     lynx_start_reverse();
3059     lynx_start_underline();
3060 }
3061 
lynx_start_status_color(void)3062 void lynx_start_status_color(void)
3063 {
3064 #if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
3065     if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
3066 	lynx_set_color(2);
3067     else
3068 #endif
3069 	lynx_start_reverse();
3070 }
3071 
lynx_stop_status_color(void)3072 void lynx_stop_status_color(void)
3073 {
3074 #if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
3075     if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
3076 	lynx_set_color(0);
3077     else
3078 #endif
3079 	lynx_stop_reverse();
3080 }
3081 
lynx_start_h1_color(void)3082 void lynx_start_h1_color(void)
3083 {
3084     if (bold_H1 || bold_headers)
3085 	lynx_start_bold();
3086 }
3087 
lynx_stop_h1_color(void)3088 void lynx_stop_h1_color(void)
3089 {
3090     if (bold_H1 || bold_headers)
3091 	lynx_stop_bold();
3092 }
3093 
lynx_start_prompt_color(void)3094 void lynx_start_prompt_color(void)
3095 {
3096     lynx_start_reverse();
3097 }
3098 
lynx_stop_prompt_color(void)3099 void lynx_stop_prompt_color(void)
3100 {
3101     lynx_stop_reverse();
3102 }
3103 
lynx_start_radio_color(void)3104 void lynx_start_radio_color(void)
3105 {
3106     lynx_start_bold();
3107 }
3108 
lynx_stop_radio_color(void)3109 void lynx_stop_radio_color(void)
3110 {
3111     lynx_stop_bold();
3112 }
3113 
lynx_stop_all_colors(void)3114 void lynx_stop_all_colors(void)
3115 {
3116     lynx_stop_underline();
3117     lynx_stop_reverse();
3118     lynx_stop_bold();
3119 }
3120 
3121 /*
3122  * Wrappers for LYUnderlineLinks flag.
3123  */
lynx_start_bold(void)3124 void lynx_start_bold(void)
3125 {
3126     start_bold();
3127 }
3128 
lynx_start_reverse(void)3129 void lynx_start_reverse(void)
3130 {
3131     start_reverse();
3132 }
3133 
lynx_start_underline(void)3134 void lynx_start_underline(void)
3135 {
3136     start_underline();
3137 }
3138 
lynx_stop_bold(void)3139 void lynx_stop_bold(void)
3140 {
3141     stop_bold();
3142 }
3143 
lynx_stop_reverse(void)3144 void lynx_stop_reverse(void)
3145 {
3146     stop_reverse();
3147 }
3148 
lynx_stop_underline(void)3149 void lynx_stop_underline(void)
3150 {
3151     stop_underline();
3152 }
3153 
LYSetDisplayLines(void)3154 void LYSetDisplayLines(void)
3155 {
3156     if (!no_title) {
3157 	if (user_mode == NOVICE_MODE)
3158 	    display_lines = LYlines - 4;
3159 	else
3160 	    display_lines = LYlines - 2;
3161     } else if (user_mode == NOVICE_MODE) {
3162 	display_lines = LYlines - 3;
3163     } else {
3164 	display_lines = LYlines - 1;
3165     }
3166 }
3167 
3168 /*
3169  * If LYShowCursor is ON, move the cursor to the left of the current option, so
3170  * that blind users, who are most likely to have LYShowCursor ON, will have
3171  * its string spoken or passed to the braille interface as each option is made
3172  * current.  Otherwise, move it to the bottom, right column of the screen, to
3173  * "hide" the cursor as for the main document, and let sighted users rely on
3174  * the current option's highlighting or color without the distraction of a
3175  * blinking cursor in the window.  - FM
3176  */
LYstowCursor(WINDOW * win,int row,int col)3177 void LYstowCursor(WINDOW * win, int row, int col)
3178 {
3179     if (LYShowCursor) {
3180 	wmove(win, row, col);
3181     } else {
3182 	LYHideCursor();
3183     }
3184 #ifdef USE_SLANG
3185     SLsmg_refresh();
3186 #else
3187     wrefresh(win);
3188 #endif /* USE_SLANG  */
3189 }
3190 
3191 #if defined(USE_BLINK) && defined(__EMX__)	/* Can't put it earler due to BOOLEAN conflict */
3192 #  define BOOLEAN os2BOOLEAN
3193 #  define INCL_VIO
3194 #  include "os2.h"
make_blink_boldbg(void)3195 static void make_blink_boldbg(void)
3196 {
3197     VIOINTENSITY buf;		/* VIO windows have it anyway, */
3198 
3199     /* but FS session need a switch */
3200     buf.cb = sizeof(buf);
3201     buf.type = 2;		/* VIOINTENSITY request */
3202     buf.fs = 1;			/* Intensity == boldbg */
3203     VioSetState(&buf, 0);
3204 }
3205 #endif
3206 
3207 #if defined(HAVE_WATTR_GET)
3208 /*
3209  * getattrs() is not in X/Open curses, but it is more convenient than this.
3210  */
LYgetattrs(WINDOW * win)3211 long LYgetattrs(WINDOW * win)
3212 {
3213     long result;
3214 
3215 #if ( defined(HAVE_GETATTRS) && ( !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR < 5 ) )
3216 
3217     result = getattrs(win);
3218 #else
3219     attr_t attrs = 0;
3220     short pair = 0;
3221 
3222     /*
3223      * FIXME: this ignores the color-pair, which for most implementations is
3224      * not stored in the attribute value.
3225      */
3226     (void) wattr_get(win, &attrs, &pair, NULL);
3227     result = (long) attrs;
3228 #endif
3229     return result;
3230 }
3231 #endif /* HAVE_WATTR_GET */
3232 
3233 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH > 20021012
3234 #ifndef HAVE_USE_LEGACY_CODING
3235 /*
3236  * Between ncurses 5.3 and 5.4 as part of fixes for wide-character mode, the
3237  * locale support no longer allows characters in the range 128-159 to be
3238  * treated as printable characters.  Here is a workaround to fool
3239  * waddch_nosync() into treating "all" 8-bit characters as printable.
3240  */
unctrl(chtype ch)3241 NCURSES_CONST char *unctrl(chtype ch)
3242 {
3243     static char result[3];
3244     unsigned data = (unsigned char) ch;
3245 
3246     if (data < 32) {
3247 	result[0] = '^';
3248 	result[1] = ch | '@';
3249 	result[2] = 0;
3250     } else if (data == 127) {
3251 	result[0] = '^';
3252 	result[1] = '?';
3253 	result[2] = 0;
3254     } else {
3255 	result[0] = data;
3256 	result[1] = 0;
3257     }
3258     return result;
3259 }
3260 #endif /* HAVE_USE_LEGACY_CODING */
3261 #endif
3262