"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.2/src/curses.c" (9 Dec 2022, 26542 Bytes) of package /linux/misc/tin-2.6.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "curses.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.6.0_vs_2.6.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : curses.c
    4  *  Author    : D. Taylor & I. Lea
    5  *  Created   : 1986-01-01
    6  *  Updated   : 2021-09-20
    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 
   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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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  */
  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
  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
  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
  703 set_xclick_off(
  704     void)
  705 {
  706     if (tinrc.use_mouse)
  707         xclick(FALSE);
  708 }
  709 
  710 
  711 void
  712 cursoron(
  713     void)
  714 {
  715     if (_cursoron)
  716         tputs(_cursoron, 1, outchar);
  717 }
  718 
  719 
  720 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
  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_t) 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 && col < LEN) {
  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
  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_t) 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_t) (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
  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
  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
 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 = (int) 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
 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
 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 = (int) 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 = (wchar_t) 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 = (int) read(0, mbs + 1, (size_t) 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, (size_t) 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 = (wchar_t) 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 */