1 /*
2  *  Project   : tin - a Usenet reader
3  *  Module    : curses.c
4  *  Author    : D. Taylor & I. Lea
5  *  Created   : 1986-01-01
6  *  Updated   : 2019-03-12
7  *  Notes     : This is a screen management library borrowed with permission
8  *              from the Elm mail system. This library was hacked to provide
9  *              what tin needs.
10  *  Copyright : Copyright (c) 1986-99 Dave Taylor & Iain Lea
11  *              The Elm Mail System  -  @Revision: 2.1 $   $State: Exp @
12  */
13 
14 #ifndef TIN_H
15 #	include "tin.h"
16 #endif /* !TIN_H */
17 #ifndef TCURSES_H
18 #	include "tcurses.h"
19 #endif /* !TCURSES_H */
20 #ifndef TNNTP_H
21 #	include "tnntp.h"
22 #endif /* !TNNTP_H */
23 #ifndef TIN_MISSING_FD_H
24 #	include <missing_fd.h>
25 #endif /* TIN_MISSING_FD_H */
26 
27 /* local prototype */
28 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
29 	static wint_t convert_c2wc (const char *input);
30 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
31 
32 #ifdef USE_CURSES
33 
34 #	define ReadCh cmdReadCh
35 #	if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
36 #		define ReadWch cmdReadWch
37 #	endif /* MULTIBYTE_ABLE && !NO_LOCALE */
38 #	define get_arrow_key cmd_get_arrow_key
39 
my_dummy(void)40 void my_dummy(void) { }	/* ANSI C requires non-empty file */
41 t_bool have_linescroll = TRUE;	/* USE_CURSES always allows line scrolling */
42 
43 #else	/* !USE_CURSES */
44 
45 #	ifndef ns32000
46 #	undef	sinix
47 #	endif /* !ns32000 */
48 
49 #	define DEFAULT_LINES_ON_TERMINAL	24
50 #	define DEFAULT_COLUMNS_ON_TERMINAL	80
51 
52 int cLINES = DEFAULT_LINES_ON_TERMINAL - 1;
53 int cCOLS = DEFAULT_COLUMNS_ON_TERMINAL;
54 int _hp_glitch = FALSE;		/* stdout not erased by overwriting on HP terms */
55 static int _inraw = FALSE;	/* are we IN rawmode? */
56 static int xclicks = FALSE;	/* do we have an xterm? */
57 t_bool have_linescroll = FALSE;
58 
59 #	define TTYIN	0
60 
61 #	if defined(HAVE_TERMIOS_H) && defined(HAVE_TCGETATTR) && defined(HAVE_TCSETATTR)
62 #		ifdef HAVE_IOCTL_H
63 #			include <ioctl.h>
64 #		else
65 #			ifdef HAVE_SYS_IOCTL_H
66 #				include <sys/ioctl.h>
67 #			endif /* HAVE_SYS_IOCTL_H */
68 #		endif /* HAVE_IOCTL_H */
69 #		if !defined(sun) || !defined(NL0)
70 #			include <termios.h>
71 #		endif /* !sun || !NL0 */
72 #		define USE_POSIX_TERMIOS 1
73 #		define TTY struct termios
74 #	else
75 #		ifdef HAVE_TERMIO_H
76 #			include <termio.h>
77 #			define USE_TERMIO 1
78 #			define TTY struct termio
79 #		else
80 #			ifdef HAVE_SGTTY_H
81 #				include <sgtty.h>
82 #				define USE_SGTTY 1
83 #				define TTY struct sgttyb
84 /*
85 	#			else
86 	#				error "No termios.h, termio.h or sgtty.h found"
87 */
88 #			endif /* HAVE_SGTTY_H */
89 #		endif /* HAVE_TERMIO_H */
90 #	endif /* HAVE_TERMIOS_H && HAVE_TCGETATTR && HAVE_TCSETATTR */
91 
92 static TTY _raw_tty, _original_tty;
93 
94 
95 #	ifndef USE_SGTTY
96 #		define USE_SGTTY 0
97 #	endif /* !USE_SGTTY */
98 
99 #	ifndef USE_POSIX_TERMIOS
100 #		define USE_POSIX_TERMIOS 0
101 #		ifndef USE_TERMIO
102 #			define USE_TERMIO 0
103 #		endif /* !USE_TERMIO */
104 #	endif /* !USE_POSIX_TERMIOS */
105 
106 static char *_clearscreen, *_moveto, *_cleartoeoln, *_cleartoeos,
107 			*_setinverse, *_clearinverse, *_setunderline, *_clearunderline,
108 			*_xclickinit, *_xclickend, *_cursoron, *_cursoroff,
109 			*_terminalinit, *_terminalend, *_keypadlocal, *_keypadxmit,
110 			*_scrollregion, *_scrollfwd, *_scrollback,
111 			*_reset, *_reversevideo, *_blink, *_dim, *_bold;
112 
113 static int _columns, _line, _lines, _colors;
114 
115 #	undef SET_TTY
116 #	undef GET_TTY
117 #	if USE_POSIX_TERMIOS
118 #		define SET_TTY(arg) tcsetattr(TTYIN, TCSANOW, arg)
119 #		define GET_TTY(arg) tcgetattr(TTYIN, arg)
120 #	else
121 #		if USE_TERMIO
122 #			define SET_TTY(arg) ioctl(TTYIN, TCSETAW, arg)
123 #			define GET_TTY(arg) ioctl(TTYIN, TCGETA, arg)
124 #		else
125 #			if USE_SGTTY
126 #				define SET_TTY(arg) stty(TTYIN, arg)
127 #				define GET_TTY(arg) gtty(TTYIN, arg)
128 /*
129 	#			else
130 	#				error "No termios.h, termio.h or sgtty.h found"
131 */
132 #			endif /* USE_SGTTY */
133 #		endif /* USE_TERMIO */
134 #	endif /* USE_POSIX_TERMIOS */
135 
136 #	if 0
137 	static int in_inverse;			/* 1 when in inverse, 0 otherwise */
138 #	endif /* 0 */
139 
140 /*
141  * Local prototypes
142  */
143 static int input_pending(int delay);
144 static void ScreenSize(int *num_lines, int *num_columns);
145 static void xclick(int state);
146 
147 
148 /*
149  * returns the number of lines and columns on the display.
150  */
151 static void
ScreenSize(int * num_lines,int * num_columns)152 ScreenSize(
153 	int *num_lines,
154 	int *num_columns)
155 {
156 	if (!_lines)
157 		_lines = DEFAULT_LINES_ON_TERMINAL;
158 	if (!_columns)
159 		_columns = DEFAULT_COLUMNS_ON_TERMINAL;
160 
161 	*num_lines = _lines - 1;		/* assume index from zero */
162 	*num_columns = _columns;		/* assume index from one */
163 }
164 
165 
166 /*
167  * get screen size from termcap entry & setup sizes
168  */
169 void
setup_screen(void)170 setup_screen(
171 	void)
172 {
173 	_line = 1;
174 	ScreenSize(&cLINES, &cCOLS);
175 	cmd_line = FALSE;
176 	Raw(TRUE);
177 #	ifdef HAVE_COLOR
178 	bcol(tinrc.col_back);
179 #	endif /* HAVE_COLOR */
180 	set_win_size(&cLINES, &cCOLS);
181 }
182 
183 
184 #	ifdef USE_TERMINFO
185 #		define CAPNAME(a,b)       b
186 #		define dCAPNAME(a,b)      strcpy(_terminal, b)
187 #		define TGETSTR(b,bufp)    tigetstr(b)
188 #		define TGETNUM(b)         tigetnum(b) /* may be tigetint() */
189 #		define TGETFLAG(b)        tigetflag(b)
190 #		define NO_CAP(s)          (s == 0 || s == (char *) -1)
191 #		if !defined(HAVE_TIGETNUM) && defined(HAVE_TIGETINT)
192 #			define tigetnum tigetint
193 #		endif /* !HAVE_TIGETNUM && HAVE_TIGETINT */
194 #	else /* USE_TERMCAP */
195 #		undef USE_TERMCAP
196 #		define USE_TERMCAP 1
197 #		define CAPNAME(a,b)       a
198 #		define dCAPNAME(a,b)      a
199 #		define TGETSTR(a, bufp)   tgetstr(a, bufp)
200 #		define TGETNUM(a)         tgetnum(a)
201 #		define TGETFLAG(a)        tgetflag(a)
202 #		define NO_CAP(s)          (s == 0)
203 #	endif /* USE_TERMINFO */
204 
205 #	ifdef HAVE_TPARM
206 #		define TFORMAT(fmt, a, b) tparm(fmt, b, a)
207 #	else
208 #		define TFORMAT(fmt, a, b) tgoto(fmt, b, a)
209 #	endif /* HAVE_TPARM */
210 
211 #	ifdef HAVE_EXTERN_TCAP_PC
212 	extern char PC;			/* used in 'tputs()' */
213 #	endif /* HAVE_EXTERN_TCAP_PC */
214 
215 int
get_termcaps(void)216 get_termcaps(
217 	void)
218 {
219 	static struct {
220 		char **value;
221 		char capname[6];
222 	} table[] = {
223 		{ &_clearinverse,	CAPNAME("se", "rmso") },
224 		{ &_clearscreen,	CAPNAME("cl", "clear") },
225 		{ &_cleartoeoln,	CAPNAME("ce", "el") },
226 		{ &_cleartoeos,		CAPNAME("cd", "ed") },
227 		{ &_clearunderline,	CAPNAME("ue", "rmul") },
228 		{ &_cursoroff,		CAPNAME("vi", "civis") },
229 		{ &_cursoron,		CAPNAME("ve", "cnorm") },
230 		{ &_keypadlocal,	CAPNAME("ke", "rmkx") },
231 		{ &_keypadxmit,		CAPNAME("ks", "smkx") },
232 		{ &_moveto,		CAPNAME("cm", "cup") },
233 		{ &_scrollback,		CAPNAME("sr", "ri") },
234 		{ &_scrollfwd,		CAPNAME("sf", "ind") },
235 		{ &_scrollregion,	CAPNAME("cs", "csr") },
236 		{ &_setinverse,		CAPNAME("so", "smso") },
237 		{ &_setunderline,	CAPNAME("us", "smul") },
238 		{ &_terminalend,	CAPNAME("te", "rmcup") },
239 		{ &_terminalinit,	CAPNAME("ti", "smcup") },
240 		/* extra caps needed for word highlighting */
241 		{ &_reset,		CAPNAME("me", "sgr0") },
242 		{ &_reversevideo,	CAPNAME("mr", "rev") },
243 		{ &_blink,		CAPNAME("mb", "blink") },
244 		{ &_dim,		CAPNAME("mh", "dim") },
245 		{ &_bold,		CAPNAME("mb", "bold") },
246 	};
247 
248 	static char _terminal[1024];		/* Storage for terminal entry */
249 
250 #	if defined(USE_TERMCAP)
251 	static char _capabilities[1024];	/* String for cursor motion */
252 	static char *ptr = _capabilities;	/* for buffering */
253 #		if defined(HAVE_EXTERN_TCAP_PC)
254 	char *t;
255 #		endif /* HAVE_EXTERN_TCAP_PC */
256 #	endif /* USE_TERMCAP */
257 
258 	char the_termname[40], *p;
259 	unsigned n;
260 
261 	if ((p = getenv("TERM")) == NULL) {
262 		my_fprintf(stderr, _(txt_no_term_set), tin_progname);
263 		return FALSE;
264 	}
265 
266 	my_strncpy(the_termname, p, sizeof(the_termname) - 1);
267 
268 #	ifdef USE_TERMINFO
269 	setupterm(the_termname, fileno(stdout), (int *) 0);
270 #	else
271 	if (tgetent(_terminal, the_termname) != 1) {
272 		my_fprintf(stderr, _(txt_cannot_get_term_entry), tin_progname);
273 		return FALSE;
274 	}
275 #	endif /* USE_TERMINFO */
276 
277 	/* load in all those pesky values */
278 	for (n = 0; n < ARRAY_SIZE(table); n++) {
279 		*(table[n].value) = TGETSTR(table[n].capname, &ptr);
280 	}
281 	_lines = TGETNUM(dCAPNAME("li", "lines"));
282 	_colors = TGETNUM(dCAPNAME("Co", "colors"));
283 	_columns = TGETNUM(dCAPNAME("co", "cols"));
284 	_hp_glitch = TGETFLAG(dCAPNAME("xs", "xhp"));
285 
286 #	if defined(USE_TERMCAP) && defined(HAVE_EXTERN_TCAP_PC)
287 	t = TGETSTR(CAPNAME("pc", "pad"), &p);
288 	if (t != 0)
289 		PC = *t;
290 #	endif /* USE_TERMCAP && HAVE_EXTERN_TCAP_PC */
291 
292 	if (STRCMPEQ(the_termname, "xterm")) {
293 		static char x_init[] = "\033[?9h";
294 		static char x_end[] = "\033[?9l";
295 		xclicks = TRUE;
296 		_xclickinit = x_init;
297 		_xclickend = x_end;
298 	}
299 
300 	if (NO_CAP(_clearscreen)) {
301 		my_fprintf(stderr, _(txt_no_term_clearscreen), tin_progname);
302 		return FALSE;
303 	}
304 	if (NO_CAP(_moveto)) {
305 		my_fprintf(stderr, _(txt_no_term_cursor_motion), tin_progname);
306 		return FALSE;
307 	}
308 	if (NO_CAP(_cleartoeoln)) {
309 		my_fprintf(stderr, _(txt_no_term_clear_eol), tin_progname);
310 		return FALSE;
311 	}
312 	if (NO_CAP(_cleartoeos)) {
313 		my_fprintf(stderr, _(txt_no_term_clear_eos), tin_progname);
314 		return FALSE;
315 	}
316 	if (_lines == -1)
317 		_lines = DEFAULT_LINES_ON_TERMINAL;
318 	if (_columns == -1)
319 		_columns = DEFAULT_COLUMNS_ON_TERMINAL;
320 
321 	if (_lines < MIN_LINES_ON_TERMINAL || _columns < MIN_COLUMNS_ON_TERMINAL) {
322 		my_fprintf(stderr, _(txt_screen_too_small), tin_progname);
323 		return FALSE;
324 	}
325 	/*
326 	 * kludge to workaround no inverse
327 	 */
328 	if (NO_CAP(_setinverse)) {
329 		_setinverse = _setunderline;
330 		_clearinverse = _clearunderline;
331 		if (NO_CAP(_setinverse))
332 			tinrc.draw_arrow = 1;
333 	}
334 	if (NO_CAP(_scrollregion) || NO_CAP(_scrollfwd) || NO_CAP(_scrollback))
335 		have_linescroll = FALSE;
336 	else
337 		have_linescroll = TRUE;
338 	return TRUE;
339 }
340 
341 
342 int
InitScreen(void)343 InitScreen(
344 	void)
345 {
346 	InitWin();
347 #	ifdef HAVE_COLOR
348 	postinit_colors(MAX(_colors, MAX_COLOR + 1)); /* postinit_colors(_colors) would be correct */
349 #	endif /* HAVE_COLOR */
350 	return TRUE;
351 }
352 
353 
354 void
InitWin(void)355 InitWin(
356 	void)
357 {
358 	if (_terminalinit) {
359 		tputs(_terminalinit, 1, outchar);
360 		my_flush();
361 	}
362 	set_keypad_on();
363 	set_xclick_on();
364 }
365 
366 
367 void
EndWin(void)368 EndWin(
369 	void)
370 {
371 	if (_terminalend) {
372 		tputs(_terminalend, 1, outchar);
373 		my_flush();
374 	}
375 	set_keypad_off();
376 	set_xclick_off();
377 }
378 
379 
380 void
set_keypad_on(void)381 set_keypad_on(
382 	void)
383 {
384 #	ifdef HAVE_KEYPAD
385 	if (tinrc.use_keypad && _keypadxmit) {
386 		tputs(_keypadxmit, 1, outchar);
387 		my_flush();
388 	}
389 #	endif /* HAVE_KEYPAD */
390 }
391 
392 
393 void
set_keypad_off(void)394 set_keypad_off(
395 	void)
396 {
397 #	ifdef HAVE_KEYPAD
398 	if (tinrc.use_keypad && _keypadlocal) {
399 		tputs(_keypadlocal, 1, outchar);
400 		my_flush();
401 	}
402 #	endif /* HAVE_KEYPAD */
403 }
404 
405 
406 /*
407  * clear the screen
408  */
409 void
ClearScreen(void)410 ClearScreen(
411 	void)
412 {
413 #	ifdef HAVE_COLOR
414 	fcol(tinrc.col_normal);
415 	bcol(tinrc.col_back);
416 #	endif /* HAVE_COLOR */
417 	tputs(_clearscreen, 1, outchar);
418 	my_flush();		/* clear the output buffer */
419 	_line = 1;
420 }
421 
422 
423 #	ifdef HAVE_COLOR
424 void
reset_screen_attr(void)425 reset_screen_attr(
426 	void)
427 {
428 	if (!NO_CAP(_reset)) {
429 		tputs(_reset, 1, outchar);
430 		my_flush();
431 	}
432 }
433 #	endif /* HAVE_COLOR */
434 
435 
436 /*
437  *  move cursor to the specified row column on the screen.
438  *  0,0 is the top left!
439  */
440 void
MoveCursor(int row,int col)441 MoveCursor(
442 	int row,
443 	int col)
444 {
445 	char *stuff;
446 
447 	stuff = tgoto(_moveto, col, row);
448 	tputs(stuff, 1, outchar);
449 	my_flush();
450 	_line = row + 1;
451 }
452 
453 
454 /*
455  * clear to end of line
456  */
457 void
CleartoEOLN(void)458 CleartoEOLN(
459 	void)
460 {
461 	tputs(_cleartoeoln, 1, outchar);
462 	my_flush();	/* clear the output buffer */
463 }
464 
465 
466 /*
467  * clear to end of screen
468  */
469 void
CleartoEOS(void)470 CleartoEOS(
471 	void)
472 {
473 	int i;
474 
475 	if (_cleartoeos) {
476 		tputs(_cleartoeos, 1, outchar);
477 	} else {
478 		for (i = _line - 1; i < _lines; i++) {
479 			MoveCursor(i, 0);
480 			CleartoEOLN();
481 		}
482 	}
483 	my_flush();	/* clear the output buffer */
484 }
485 
486 
487 static int _topscrregion, _bottomscrregion;
488 
489 void
SetScrollRegion(int topline,int bottomline)490 SetScrollRegion(
491 	int topline,
492 	int bottomline)
493 {
494 	char *stuff;
495 
496 	if (!have_linescroll)
497 		return;
498 	if (_scrollregion) {
499 		stuff = TFORMAT(_scrollregion, topline, bottomline);
500 		tputs(stuff, 1, outchar);
501 		_topscrregion = topline;
502 		_bottomscrregion = bottomline;
503 	}
504 	my_flush();
505 }
506 
507 
508 void
ScrollScreen(int lines_to_scroll)509 ScrollScreen(
510 	int lines_to_scroll)
511 {
512 	int i;
513 
514 	if (!have_linescroll || (lines_to_scroll == 0))
515 		return;
516 	if (lines_to_scroll < 0) {
517 		if (_scrollback) {
518 			i = lines_to_scroll;
519 			while (i++) {
520 				MoveCursor(_topscrregion, 0);
521 				tputs(_scrollback, 1, outchar);
522 			}
523 		}
524 	} else
525 		if (_scrollfwd) {
526 			i = lines_to_scroll;
527 			while (i--) {
528 				MoveCursor(_bottomscrregion, 0);
529 				tputs(_scrollfwd, 1, outchar);
530 			}
531 		}
532 	my_flush();
533 }
534 
535 
536 /*
537  * set inverse video mode
538  */
539 void
StartInverse(void)540 StartInverse(
541 	void)
542 {
543 /*	in_inverse = 1; */
544 	if (_setinverse && tinrc.inverse_okay) {
545 #	ifdef HAVE_COLOR
546 		if (use_color) {
547 			bcol(tinrc.col_invers_bg);
548 			fcol(tinrc.col_invers_fg);
549 		} else {
550 			tputs(_setinverse, 1, outchar);
551 		}
552 #	else
553 		tputs(_setinverse, 1, outchar);
554 #	endif /* HAVE_COLOR */
555 	}
556 	my_flush();
557 }
558 
559 
560 /*
561  * compliment of startinverse
562  */
563 void
EndInverse(void)564 EndInverse(
565 	void)
566 {
567 /*	in_inverse = 0; */
568 	if (_clearinverse && tinrc.inverse_okay) {
569 #	ifdef HAVE_COLOR
570 		if (use_color) {
571 			fcol(tinrc.col_normal);
572 			bcol(tinrc.col_back);
573 		} else {
574 			tputs(_clearinverse, 1, outchar);
575 		}
576 #	else
577 		tputs(_clearinverse, 1, outchar);
578 #	endif /* HAVE_COLOR */
579 	}
580 	my_flush();
581 }
582 
583 
584 #	if 0 /* doesn't work correct with ncurses4.x */
585 /*
586  * toggle inverse video mode
587  */
588 void
589 ToggleInverse(
590 	void)
591 {
592 	if (!in_inverse)
593 		StartInverse();
594 	else
595 		EndInverse();
596 }
597 #	endif /* 0 */
598 
599 
600 /*
601  * returns either 1 or 0, for ON or OFF
602  */
603 int
RawState(void)604 RawState(
605 	void)
606 {
607 	return _inraw;
608 }
609 
610 
611 /* SunOS-3.5 - FIXME! */
612 #if defined(sun) || defined(__sun) && (!defined(__SVR4) || !defined(__svr4__)) && defined(BSD) && BSD < 199306
613 #	ifndef ECHO
614 #		define ECHO		0x00000008 /* echo input */
615 #	endif /* !ECHO */
616 #	ifndef CBREAK
617 #		define CBREAK	0x00000002 /* half-cooked mode */
618 #	endif /* !CBREAK */
619 #endif /* sun || __sun && (!__SVR4 || !__svr4__) && BSD && BSD < 199306 */
620 /*
621  * state is either TRUE or FALSE, as indicated by call
622  */
623 void
Raw(int state)624 Raw(
625 	int state)
626 {
627 	if (!state && _inraw) {
628 		SET_TTY(&_original_tty);
629 		_inraw = 0;
630 	} else if (state && !_inraw) {
631 		GET_TTY(&_original_tty);
632 		GET_TTY(&_raw_tty);
633 #	if USE_SGTTY
634 		_raw_tty.sg_flags &= ~(ECHO | CRMOD);	/* echo off */
635 		_raw_tty.sg_flags |= CBREAK;		/* raw on */
636 #	else
637 #		ifdef __FreeBSD__
638 		cfmakeraw(&_raw_tty);
639 		_raw_tty.c_lflag |= ISIG;		/* for ^Z */
640 #		else
641 		_raw_tty.c_lflag &= ~(ICANON | ECHO);	/* noecho raw mode */
642 		_raw_tty.c_cc[VMIN] = '\01';	/* minimum # of chars to queue */
643 		_raw_tty.c_cc[VTIME] = '\0';	/* minimum time to wait for input */
644 #		endif /* __FreeBSD__ */
645 #	endif /* USE_SGTTY */
646 		SET_TTY(&_raw_tty);
647 		_inraw = 1;
648 	}
649 }
650 
651 
652 /*
653  *  output a character. From tputs... (Note: this CANNOT be a macro!)
654  */
OUTC_FUNCTION(outchar)655 OUTC_FUNCTION(
656 	outchar)
657 {
658 #	ifdef OUTC_RETURN
659 	return fputc(c, stdout);
660 #	else
661 	(void) fputc(c, stdout);
662 #	endif /* OUTC_RETURN */
663 }
664 
665 
666 /*
667  * setup to monitor mouse buttons if running in a xterm
668  */
669 static void
xclick(int state)670 xclick(
671 	int state)
672 {
673 	static int prev_state = 999;
674 
675 	if (xclicks && prev_state != state) {
676 		if (state) {
677 			tputs(_xclickinit, 1, outchar);
678 		} else {
679 			tputs(_xclickend, 1, outchar);
680 		}
681 		my_flush();
682 		prev_state = state;
683 	}
684 }
685 
686 
687 /*
688  * switch on monitoring of mouse buttons
689  */
690 void
set_xclick_on(void)691 set_xclick_on(
692 	void)
693 {
694 	if (tinrc.use_mouse)
695 		xclick(TRUE);
696 }
697 
698 
699 /*
700  * switch off monitoring of mouse buttons
701  */
702 void
set_xclick_off(void)703 set_xclick_off(
704 	void)
705 {
706 	if (tinrc.use_mouse)
707 		xclick(FALSE);
708 }
709 
710 
711 void
cursoron(void)712 cursoron(
713 	void)
714 {
715 	if (_cursoron)
716 		tputs(_cursoron, 1, outchar);
717 }
718 
719 
720 void
cursoroff(void)721 cursoroff(
722 	void)
723 {
724 	if (_cursoroff)
725 		tputs(_cursoroff, 1, outchar);
726 }
727 
728 
729 /*
730  * Inverse 'size' chars at (row,col)
731  */
732 void
highlight_string(int row,int col,int size)733 highlight_string(
734 	int row,
735 	int col,
736 	int size)
737 {
738 	char output[LEN];
739 
740 	my_strncpy(output, &(screen[row].col[col]), size);
741 
742 #	if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
743 	/*
744 	 * In a multibyte locale we get byte offsets instead of character
745 	 * offsets; calculate now the correct starting column
746 	 */
747 	if (col > 0) {
748 		char tmp[LEN];
749 		wchar_t *wtmp;
750 
751 		my_strncpy(tmp, &(screen[row].col[0]), sizeof(tmp) - 1);
752 		tmp[col] = '\0';
753 		if ((wtmp = char2wchar_t(tmp)) != NULL) {
754 			col = wcswidth(wtmp, wcslen(wtmp) + 1);
755 			free(wtmp);
756 		}
757 	}
758 #	endif /* MULTIBYTE_ABLE && !NO_LOCALE */
759 
760 	MoveCursor(row, col);
761 	StartInverse();
762 	my_fputs(output, stdout);
763 	EndInverse();
764 	my_flush();
765 
766 	stow_cursor();
767 }
768 
769 
770 /*
771  * Color 'size' chars at (row,col) with 'color' and handle marks
772  */
773 void
word_highlight_string(int row,int col,int size,int color)774 word_highlight_string(
775 	int row,
776 	int col,
777 	int size,
778 	int color)
779 {
780 	/*
781 	 * Mapping of the tinrc.mono_mark* values to the corresponding escape sequences
782 	 */
783 	char *attributes[MAX_ATTR + 1];
784 	char *dest, *src;
785 	char output[LEN];
786 	int byte_offset = col;
787 	int wsize = size;
788 #	if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
789 	wchar_t *wtmp;
790 #	endif /* MULTIBYTE_ABLE && !NO_LOCALE */
791 
792 	attributes[0] = _reset;	/* Normal */
793 	attributes[1] = _setinverse;	/* Best highlighting */
794 	attributes[2] = _setunderline;	/* Underline */
795 	attributes[3] = _reversevideo;	/* Reverse video */
796 	attributes[4] = _blink;	/* Blink */
797 	attributes[5] = _dim;	/* Dim */
798 	attributes[6] = _bold;	/* Bold */
799 
800 	my_strncpy(output, &(screen[row].col[byte_offset]), size);
801 
802 #	if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
803 	/*
804 	 * In a multibyte locale we get byte offsets instead of character
805 	 * offsets; calculate now the correct starting column and
806 	 * width
807 	 */
808 	if (byte_offset > 0) {
809 		char tmp[LEN];
810 
811 		my_strncpy(tmp, &(screen[row].col[0]), sizeof(tmp) - 1);
812 		tmp[byte_offset] = '\0';
813 		if ((wtmp = char2wchar_t(tmp)) != NULL) {
814 			col = wcswidth(wtmp, wcslen(wtmp) + 1);
815 			free(wtmp);
816 		}
817 	}
818 	if ((wtmp = char2wchar_t(output)) != NULL) {
819 		wsize = wcswidth(wtmp, wcslen(wtmp) + 1);
820 		free(wtmp);
821 	}
822 #	endif /* MULTIBYTE_ABLE && !NO_LOCALE */
823 
824 	MoveCursor(row, col);
825 
826 	/* safeguard against bogus regexps */
827 	if ((output[0] == '*' && output[size - 1] == '*') ||
828 		 (output[0] == '/' && output[size - 1] == '/') ||
829 		 (output[0] == '_' && output[size - 1] == '_') ||
830 		 (output[0] == '-' && output[size - 1] == '-')) {
831 
832 		switch (tinrc.word_h_display_marks) {
833 			case 0: /* remove marks */
834 				MoveCursor(row, col + wsize - 2);
835 				CleartoEOLN();
836 				my_fputs(&(screen[row].col[byte_offset + size]), stdout);
837 				output[0] = output[size - 1] = ' ';
838 				str_trim(output);
839 				strncpy(&(screen[row].col[byte_offset]), output, size - 2);
840 				src = &(screen[row].col[byte_offset + size]);
841 				dest = &(screen[row].col[byte_offset + size - 2]);
842 				while (*src)
843 					*dest++ = *src++;
844 				*dest++ = '\0';
845 				MoveCursor(row, col);
846 				break;
847 
848 			case 2: /* print space */
849 				MoveCursor(row, col + wsize - 1);
850 				my_fputs(" ", stdout);
851 				MoveCursor(row, col);
852 				my_fputs(" ", stdout);
853 				output[0] = output[size - 1] = ' ';
854 				str_trim(output);
855 				screen[row].col[byte_offset] = ' ';
856 				screen[row].col[byte_offset + size - 1] = ' ';
857 				break;
858 
859 			default:	/* print mark (case 1) */
860 				break;
861 		}
862 	}
863 #	ifdef HAVE_COLOR
864 	if (use_color)
865 		fcol(color);
866 	else
867 #	endif /* HAVE_COLOR */
868 		if (color > 0 && color <= MAX_ATTR && !NO_CAP(attributes[color]))
869 			tputs(attributes[color], 1, outchar);
870 	my_fputs(output, stdout);
871 	my_flush();
872 #	ifdef HAVE_COLOR
873 	if (use_color)
874 		fcol(tinrc.col_text);
875 	else
876 #	endif /* HAVE_COLOR */
877 		if (!NO_CAP(_reset))
878 			tputs(_reset, 1, outchar);
879 	stow_cursor();
880 }
881 #endif /* USE_CURSES */
882 
883 
884 /*
885  * input_pending() waits for input during time given
886  * by delay in msec. The original behavior of input_pending()
887  * (in art.c's threading code) is delay=0
888  */
889 static int
input_pending(int delay)890 input_pending(
891 	int delay)
892 {
893 #if 0
894 	int ch;
895 
896 	nodelay(stdscr, TRUE);
897 	if ((ch = getch()) != ERR)
898 		ungetch(ch);
899 	nodelay(stdscr, FALSE);
900 	return (ch != ERR);
901 
902 #else
903 
904 #	ifdef HAVE_SELECT
905 	int fd = STDIN_FILENO;
906 	fd_set fdread;
907 	struct timeval tvptr;
908 
909 	FD_ZERO(&fdread);
910 
911 	tvptr.tv_sec = 0;
912 	tvptr.tv_usec = delay * 100;
913 
914 	FD_SET(fd, &fdread);
915 
916 #		ifdef HAVE_SELECT_INTP
917 	if (select(1, (int *) &fdread, NULL, NULL, &tvptr))
918 #		else
919 	if (select(1, &fdread, NULL, NULL, &tvptr))
920 #		endif /* HAVE_SELECT_INTP */
921 	{
922 		if (FD_ISSET(fd, &fdread))
923 			return TRUE;
924 	}
925 #	endif /* HAVE_SELECT */
926 
927 #	if defined(HAVE_POLL) && !defined(HAVE_SELECT)
928 	static int Timeout;
929 	static long nfds = 1;
930 	static struct pollfd fds[] = {{ STDIN_FILENO, POLLIN, 0 }};
931 
932 	Timeout = delay;
933 	if (poll(fds, nfds, Timeout) < 0) /* Error on poll */
934 		return FALSE;
935 
936 	switch (fds[0].revents) {
937 		case POLLIN:
938 			return TRUE;
939 		/*
940 		 * Other conditions on the stream
941 		 */
942 		case POLLHUP:
943 		case POLLERR:
944 		default:
945 			return FALSE;
946 	}
947 #	endif /* HAVE_POLL && !HAVE_SELECT */
948 
949 #endif /* 0 */
950 
951 	return FALSE;
952 }
953 
954 
955 #if defined(HAVE_USLEEP) || defined(HAVE_SELECT) || defined(HAVE_POLL)
956 #	define wait_a_while(i) \
957 	while (!input_pending(0) \
958 		&& i < ((VT_ESCAPE_TIMEOUT * 1000) / SECOND_CHARACTER_DELAY))
959 #endif /* HAVE_USLEEP || HAVE_SELECT || HAVE_POLL */
960 int
get_arrow_key(int prech)961 get_arrow_key(
962 	int prech)
963 {
964 	int ch;
965 	int ch1;
966 #if defined(HAVE_USLEEP) || defined(HAVE_SELECT) || defined(HAVE_POLL)
967 	int i = 0;
968 #endif /* HAVE_USLEEP || HAVE_SELECT || HAVE_POLL */
969 
970 	if (!input_pending(0)) {
971 #ifdef HAVE_USLEEP
972 		wait_a_while(i) {
973 			usleep((unsigned long) (SECOND_CHARACTER_DELAY * 1000));
974 			i++;
975 		}
976 #else	/* !HAVE_USLEEP */
977 #	ifdef HAVE_SELECT
978 		struct timeval tvptr;
979 
980 		wait_a_while(i) {
981 			tvptr.tv_sec = 0;
982 			tvptr.tv_usec = SECOND_CHARACTER_DELAY * 1000;
983 			select(0, NULL, NULL, NULL, &tvptr);
984 			i++;
985 		}
986 #	else /* !HAVE_SELECT */
987 #		ifdef HAVE_POLL
988 		struct pollfd fds[1];
989 
990 		wait_a_while(i) {
991 			poll(fds, 0, SECOND_CHARACTER_DELAY);
992 			i++;
993 		}
994 #		else /* !HAVE_POLL */
995 		(void) sleep(1);
996 #		endif /* HAVE_POLL */
997 #	endif /* HAVE_SELECT */
998 #endif /* HAVE_USLEEP */
999 		if (!input_pending(0))
1000 			return prech;
1001 	}
1002 	ch = ReadCh();
1003 	if (ch == '[' || ch == 'O')
1004 		ch = ReadCh();
1005 
1006 	switch (ch) {
1007 		case 'A':
1008 		case 'i':
1009 #ifdef QNX42
1010 		case 0xA1:
1011 #endif /* QNX42 */
1012 			return KEYMAP_UP;
1013 
1014 		case 'B':
1015 #ifdef QNX42
1016 		case 0xA9:
1017 #endif /* QNX42 */
1018 			return KEYMAP_DOWN;
1019 
1020 		case 'D':
1021 #ifdef QNX42
1022 		case 0xA4:
1023 #endif /* QNX42 */
1024 			return KEYMAP_LEFT;
1025 
1026 		case 'C':
1027 #ifdef QNX42
1028 		case 0xA6:
1029 #endif /* QNX42 */
1030 			return KEYMAP_RIGHT;
1031 
1032 		case 'I':		/* ansi  PgUp */
1033 		case 'V':		/* at386 PgUp */
1034 		case 'S':		/* 97801 PgUp */
1035 		case 'v':		/* emacs style */
1036 #ifdef QNX42
1037 		case 0xA2:
1038 #endif /* QNX42 */
1039 			return KEYMAP_PAGE_UP;
1040 
1041 		case 'G':		/* ansi  PgDn */
1042 		case 'U':		/* at386 PgDn */
1043 		case 'T':		/* 97801 PgDn */
1044 #ifdef QNX42
1045 		case 0xAA:
1046 #endif /* QNX42 */
1047 			return KEYMAP_PAGE_DOWN;
1048 
1049 		case 'H':		/* at386 Home */
1050 #ifdef QNX42
1051 		case 0xA0:
1052 #endif /* QNX42 */
1053 			return KEYMAP_HOME;
1054 
1055 		case 'F':		/* ansi  End */
1056 		case 'Y':		/* at386 End */
1057 #ifdef QNX42
1058 		case 0xA8:
1059 #endif /* QNX42 */
1060 			return KEYMAP_END;
1061 
1062 		case '2':		/* vt200 Ins */
1063 			(void) ReadCh();	/* eat the ~ */
1064 			return KEYMAP_INS;
1065 
1066 		case '3':		/* vt200 Del */
1067 			(void) ReadCh();	/* eat the ~ */
1068 			return KEYMAP_DEL;
1069 
1070 		case '5':		/* vt200 PgUp */
1071 			(void) ReadCh();	/* eat the ~ (interesting use of words :) */
1072 			return KEYMAP_PAGE_UP;
1073 
1074 		case '6':		/* vt200 PgUp */
1075 			(void) ReadCh();	/* eat the ~ */
1076 			return KEYMAP_PAGE_DOWN;
1077 
1078 		case '1':		/* vt200 PgUp */
1079 			ch = ReadCh(); /* eat the ~ */
1080 			if (ch == '5') {	/* RS/6000 PgUp is 150g, PgDn is 154g */
1081 				ch1 = ReadCh();
1082 				(void) ReadCh();
1083 				if (ch1 == '0')
1084 					return KEYMAP_PAGE_UP;
1085 				if (ch1 == '4')
1086 					return KEYMAP_PAGE_DOWN;
1087 			}
1088 			return KEYMAP_HOME;
1089 
1090 		case '4':		/* vt200 PgUp */
1091 			(void) ReadCh();	/* eat the ~ */
1092 			return KEYMAP_END;
1093 
1094 		case 'M':		/* xterminal button press */
1095 			xmouse = ReadCh() - ' ';	/* button */
1096 			xcol = ReadCh() - '!';		/* column */
1097 			xrow = ReadCh() - '!';		/* row */
1098 			return KEYMAP_MOUSE;
1099 
1100 		default:
1101 			return KEYMAP_UNKNOWN;
1102 	}
1103 }
1104 
1105 
1106 /*
1107  * The UNIX version of ReadCh, termcap version
1108  */
1109 int
ReadCh(void)1110 ReadCh(
1111 	void)
1112 {
1113 	int result;
1114 #ifndef READ_CHAR_HACK
1115 	char ch;
1116 #endif /* !READ_CHAR_HACK */
1117 
1118 	fflush(stdout);
1119 #ifdef READ_CHAR_HACK
1120 #	undef getc
1121 	while ((result = getc(stdin)) == EOF) {
1122 		if (feof(stdin))
1123 			break;
1124 
1125 #	ifdef EINTR
1126 		if (ferror(stdin) && errno != EINTR)
1127 #	else
1128 		if (ferror(stdin))
1129 #	endif /* EINTR */
1130 			break;
1131 
1132 		clearerr(stdin);
1133 	}
1134 
1135 	return ((result == EOF) ? EOF : result & 0xFF);
1136 
1137 #else
1138 #	ifdef EINTR
1139 
1140 	allow_resize(TRUE);
1141 	while ((result = read(0, &ch, 1)) < 0 && errno == EINTR) {		/* spin on signal interrupts */
1142 		if (need_resize) {
1143 			handle_resize((need_resize == cRedraw) ? TRUE : FALSE);
1144 			need_resize = cNo;
1145 		}
1146 	}
1147 	allow_resize(FALSE);
1148 #	else
1149 	result = read(0, &ch, 1);
1150 #	endif /* EINTR */
1151 
1152 	return ((result <= 0) ? EOF : ch & 0xFF);
1153 
1154 #endif /* READ_CHAR_HACK */
1155 }
1156 
1157 
1158 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1159 /*
1160  * A helper function for ReadWch()
1161  * converts the read input to a wide-char
1162  */
1163 static wint_t
convert_c2wc(const char * input)1164 convert_c2wc(
1165 	const char *input)
1166 {
1167 	int res;
1168 	wchar_t wc;
1169 
1170 	res = mbtowc(&wc, input, MB_CUR_MAX);
1171 	if (res == -1)
1172 		return WEOF;
1173 	else
1174 		return (wint_t) wc;
1175 }
1176 
1177 
1178 wint_t
ReadWch(void)1179 ReadWch(
1180 	void)
1181 {
1182 	char *mbs = my_malloc(MB_CUR_MAX + 1);
1183 	int result, to_read;
1184 	wchar_t wch;
1185 
1186 	fflush(stdout);
1187 
1188 	/*
1189 	 * Independent of the pressed key and the used charset we have to
1190 	 * read at least one byte. The further processing is depending of
1191 	 * the read byte and the used charset.
1192 	 */
1193 #	ifdef EINTR
1194 	allow_resize(TRUE);
1195 	while ((result = read(0, mbs, 1)) < 0 && errno == EINTR) { /* spin on signal interrupts */
1196 		if (need_resize) {
1197 			handle_resize((need_resize == cRedraw) ? TRUE : FALSE);
1198 			need_resize = cNo;
1199 		}
1200 	}
1201 	allow_resize(FALSE);
1202 #	else
1203 	result = read(0, mbs, 1);
1204 #	endif /* EINTR */
1205 
1206 	if (result <= 0) {
1207 		free(mbs);
1208 		return WEOF;
1209 	}
1210 
1211 	/* Begin of an ESC-sequence. Let get_arrow_key() figure out which it is */
1212 	if (mbs[0] == ESC) {
1213 		free(mbs);
1214 		return (wint_t) ESC;
1215 	}
1216 
1217 	/*
1218 	 * In an one-byte charset we don't need to read further chars but
1219 	 * we still need to convert the char to a correct wide-char
1220 	 */
1221 	if (MB_CUR_MAX == 1) {
1222 		mbs[1] = '\0';
1223 		wch = convert_c2wc(mbs);
1224 		free(mbs);
1225 
1226 		return (wint_t) wch;
1227 	}
1228 
1229 	/*
1230 	 * we use a multi-byte charset
1231 	 */
1232 
1233 	/*
1234 	 * UTF-8
1235 	 */
1236 	if (IS_LOCAL_CHARSET("UTF-8")) {
1237 		int ch = mbs[0] & 0xFF;
1238 
1239 		/* determine the count of bytes we have still have to read */
1240 		if (ch <= 0x7F) {
1241 			/* ASCII char */
1242 			to_read = 0;
1243 		} else if (ch >= 0xC2 && ch <= 0xDF) {
1244 			/* 2-byte sequence */
1245 			to_read = 1;
1246 		} else if (ch >= 0xE0 && ch <= 0xEF) {
1247 			/* 3-byte sequence */
1248 			to_read = 2;
1249 		} else if (ch >= 0xF0 && ch <= 0xF4) {
1250 			/* 4-byte sequence */
1251 			to_read = 3;
1252 		} else {
1253 			/* invalid sequence */
1254 			free(mbs);
1255 			return WEOF;
1256 		}
1257 
1258 		/* read the missing bytes of the multi-byte sequence */
1259 		if (to_read > 0) {
1260 #	ifdef EINTR
1261 			allow_resize(TRUE);
1262 			while ((result = read(0, mbs + 1, to_read)) < 0 && errno == EINTR) { /* spin on signal interrupts */
1263 				if (need_resize) {
1264 					handle_resize((need_resize == cRedraw) ? TRUE : FALSE);
1265 					need_resize = cNo;
1266 				}
1267 			}
1268 			allow_resize(FALSE);
1269 #	else
1270 			result = read(0, mbs + 1, to_read);
1271 #	endif /* EINTR */
1272 			if (result < 0) {
1273 				free(mbs);
1274 
1275 				return WEOF;
1276 			}
1277 		}
1278 		mbs[to_read + 1] = '\0';
1279 		wch = convert_c2wc(mbs);
1280 		free (mbs);
1281 
1282 		return (wint_t) wch;
1283 
1284 	}
1285 
1286 	/* FIXME: add support for other multi-byte charsets */
1287 
1288 	free(mbs);
1289 	return WEOF;
1290 }
1291 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1292