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