/* term.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "env.h" #include "util.h" #include "util2.h" #include "final.h" #include "help.h" #include "hash.h" #include "cache.h" #include "opt.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "intrp.h" #include "init.h" #include "art.h" #include "rt-select.h" #ifdef SCORE #include "score.h" /* for sc_lookahead */ #endif #ifdef SCAN #include "scan.h" #include "sdisp.h" #endif #ifdef SCAN_ART #include "scanart.h" #endif #ifdef USE_TK #include "tkstuff.h" #endif #include "univ.h" #include "color.h" #include "INTERN.h" #include "term.h" #include "term.ih" #ifdef u3b2 #undef TIOCGWINSZ #endif #undef USETITE /* use terminal init/exit seqences (not recommended) */ #undef USEKSKE /* use keypad start/end sequences */ char tcarea[TCSIZE]; /* area for "compiled" termcap strings */ static KEYMAP* topmap INIT(NULL); static char* lines_export = NULL; static char* cols_export = NULL; static int leftcost, upcost; static bool got_a_char = FALSE; /* TRUE if we got a char since eating */ /* guarantee capability pointer != NULL */ /* (I believe terminfo will ignore the &tmpaddr argument.) */ char* tgetstr(); #define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr) /* terminal initialization */ void term_init() { savetty(); /* remember current tty state */ #ifdef I_TERMIO outspeed = _tty.c_cflag & CBAUD; /* for tputs() */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ if (GT = ((_tty.c_oflag & TABDLY) != TAB3)) /* we have tabs, so that's OK */; else _tty.c_oflag &= ~TAB3; /* turn off kernel tabbing -- done in rn */ #else /* !I_TERMIO */ # ifdef I_TERMIOS outspeed = cfgetospeed(&_tty); /* for tputs() (output) */ ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */ KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */ #if 0 _tty.c_oflag &= ~OXTABS; /* turn off kernel tabbing-done in rn */ #endif # else /* !I_TERMIOS */ # ifdef I_SGTTY outspeed = _tty.sg_ospeed; /* for tputs() */ ERASECH = _tty.sg_erase; /* for finish_command() */ KILLCH = _tty.sg_kill; /* for finish_command() */ if (GT = ((_tty.sg_flags & XTABS) != XTABS)) /* we have tabs, so that's OK */; else _tty.sg_flags &= ~XTABS; # else /* !I_SGTTY */ # ifdef MSDOS outspeed = B19200; ERASECH = '\b'; KILLCH = Ctl('u'); GT = 1; # else ..."Don't know how to initialize the terminal!" # endif /* !MSDOS */ # endif /* !I_SGTTY */ # endif /* !I_TERMIOS */ #endif /* !I_TERMIO */ /* The following could be a table but I can't be sure that there isn't */ /* some degree of sparsity out there in the world. */ switch (outspeed) { /* 1 second of padding */ #ifdef BEXTA case BEXTA: just_a_sec = 1920; break; #else #ifdef B19200 case B19200: just_a_sec = 1920; break; #endif #endif case B9600: just_a_sec = 960; break; case B4800: just_a_sec = 480; break; case B2400: just_a_sec = 240; break; case B1800: just_a_sec = 180; break; case B1200: just_a_sec = 120; break; case B600: just_a_sec = 60; break; case B300: just_a_sec = 30; break; /* do I really have to type the rest of this??? */ case B200: just_a_sec = 20; break; case B150: just_a_sec = 15; break; case B134: just_a_sec = 13; break; case B110: just_a_sec = 11; break; case B75: just_a_sec = 8; break; case B50: just_a_sec = 5; break; default: just_a_sec = 960; break; /* if we are running detached I */ } /* don't want to know about it! */ } #ifdef PENDING # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS) int devtty; # endif #endif /* set terminal characteristics */ void term_set(tcbuf) char* tcbuf; /* temp area for "uncompiled" termcap entry */ { char* tmpaddr; /* must not be register */ register char* tmpstr; char* s; int status; #ifdef TIOCGWINSZ struct winsize winsize; #endif #ifdef PENDING #if !defined (FIONREAD) && !defined (HAS_RDCHK) && !defined(MSDOS) /* do no delay reads on something that always gets closed on exit */ devtty = fileno(stdin); if (isatty(devtty)) { devtty = open("/dev/tty",0); if (devtty < 0) { printf(cantopen,"/dev/tty") FLUSH; finalize(1); } fcntl(devtty,F_SETFL,O_NDELAY); } #endif #endif /* get all that good termcap stuff */ #ifdef HAS_TERMLIB #ifdef MSDOS BC = "\b"; UP = "\033[A"; CR = "\r"; VB = nullstr; CL = "\033[H\033[2J"; CE = "\033[K"; TI = nullstr; TE = nullstr; KS = nullstr; KE = nullstr; CM = "\033[%d;%dH"; HO = "\033[H"; IL = nullstr; /*$$*/ CD = nullstr; /*$$*/ SO = "\033[7m"; SE = "\033[m"; US = "\033[7m"; UE = "\033[m"; UC = nullstr; set_lines_and_cols(); AM = TRUE; #else s = getenv("TERM"); status = tgetent(tcbuf,s? s : "dumb"); /* get termcap entry */ if (status < 1) { printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH; finalize(1); } tmpaddr = tcarea; /* set up strange tgetstr pointer */ s = Tgetstr("pc"); /* get pad character */ PC = *s; /* get it where tputs wants it */ if (!tgetflag("bs")) { /* is backspace not used? */ BC = Tgetstr("bc"); /* find out what is */ if (BC == nullstr) { /* terminfo grok's 'bs' but not 'bc' */ BC = Tgetstr("le"); if (BC == nullstr) BC = "\b"; /* better than nothing... */ } } else BC = "\b"; /* make a backspace handy */ UP = Tgetstr("up"); /* move up a line */ CL = Tgetstr("cl"); /* get clear string */ CE = Tgetstr("ce"); /* clear to end of line string */ TI = Tgetstr("ti"); /* initialize display */ TE = Tgetstr("te"); /* reset display */ KS = Tgetstr("ks"); /* enter `keypad transmit' mode */ KE = Tgetstr("ke"); /* exit `keypad transmit' mode */ HO = Tgetstr("ho"); /* home cursor */ IL = Tgetstr("al"); /* insert (add) line */ CM = Tgetstr("cm"); /* cursor motion */ CD = Tgetstr("cd"); /* clear to end of display */ if (!*CE) CE = CD; SO = Tgetstr("so"); /* begin standout */ SE = Tgetstr("se"); /* end standout */ if ((SG = tgetnum("sg"))<0) SG = 0; /* blanks left by SG, SE */ US = Tgetstr("us"); /* start underline */ UE = Tgetstr("ue"); /* end underline */ if ((UG = tgetnum("ug"))<0) UG = 0; /* blanks left by US, UE */ if (*US) UC = nullstr; /* UC must not be NULL */ else UC = Tgetstr("uc"); /* underline a character */ if (!*US && !*UC) { /* no underline mode? */ US = SO; /* substitute standout mode */ UE = SE; UG = SG; } LINES = tgetnum("li"); /* lines per page */ COLS = tgetnum("co"); /* columns on page */ #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { LINES = ws.ws_row; COLS = ws.ws_col; } } #endif AM = tgetflag("am"); /* terminal wraps automatically? */ XN = tgetflag("xn"); /* then eats next newline? */ VB = Tgetstr("vb"); if (!*VB) VB = "\007"; CR = Tgetstr("cr"); if (!*CR) { if (tgetflag("nc") && *UP) { CR = safemalloc((MEM_SIZE)strlen(UP)+2); sprintf(CR,"%s\r",UP); } else CR = "\r"; } #ifdef TIOCGWINSZ if (ioctl(1, TIOCGWINSZ, &winsize) >= 0) { if (winsize.ws_row > 0) LINES = winsize.ws_row; if (winsize.ws_col > 0) COLS = winsize.ws_col; } # endif #endif if (!*UP) /* no UP string? */ marking = 0; /* disable any marking */ if (*CM || *HO) can_home = TRUE; if (!*CD || !can_home) /* can we CE, CD, and home? */ erase_each_line = FALSE; /* no, so disable use of clear eol */ if (muck_up_clear) /* this is for weird HPs */ CL = "\n\n\n\n"; leftcost = strlen(BC); upcost = strlen(UP); #else /* !HAS_TERMLIB */ ..."Don't know how to set the terminal!" #endif /* !HAS_TERMLIB */ termlib_init(); line_col_calcs(); noecho(); /* turn off echo */ crmode(); /* enter cbreak mode */ sprintf(buf, "%d", LINES); lines_export = export("LINES",buf); sprintf(buf, "%d", COLS); cols_export = export("COLUMNS",buf); mac_init(tcbuf); } #ifdef MSDOS static void set_lines_and_cols() { gotoxy(132,1); if (wherex() == 132) COLS = 132; else COLS = 80; gotoxy(1,50); if (wherey() == 50) LINES = 50; else { gotoxy(1,43); if (wherey() == 50) LINES = 50; else LINES = 25; } } #endif void set_macro(seq,def) char* seq; /* input sequence of keys */ char* def; /* definition */ { mac_line(def,seq,0); /* check for common (?) brain damage: ku/kd/etc sequence may be the * cursor move sequence instead of the input sequence. * (This happens on the local xterm definitions.) * Try to recognize and adjust for this case. */ if (seq[0] == '\033' && seq[1] == '[' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = 'O'; mac_line(def,lbuf,0); } if (seq[0] == '\033' && seq[1] == 'O' && seq[2]) { char lbuf[LBUFLEN]; /* copy of possibly non-writable string */ strcpy(lbuf,seq); lbuf[1] = '['; mac_line(def,lbuf,0); } } char* up[] = { "^@", /* '(' at article or pager, '[' in thread sel, 'p' otherwise */ "%(%m=[ap]?\\(:%(%m=t?[:p))", /* '(' at article or pager, '[' in thread sel, 'p' otherwise */ "%(%m=[ap]?\\(:%(%m=t?[:p))" }; char* down[] = { "^@", /* ')' at article or pager, ']' in thread sel, 'n' otherwise */ "%(%m=[ap]?\\):%(%m=t?]:n))", /* ')' at article or pager, ']' in thread sel, 'n' otherwise */ "%(%m=[ap]?\\):%(%m=t?]:n))" }; char* left[] = { "^@", /* '[' at article or pager, 'Q' otherwise */ "%(%m=[ap]?\\[:Q)", /* '[' at article or pager, '<' otherwise */ "%(%m=[ap]?\\[:<)" }; char* right[] = { "^@", /* ']' at article or pager, CR otherwise */ "%(%m=[ap]?\\]:^j)", /* CR at newsgroups, ']' at article or pager, '>' otherwise */ "%(%m=n?^j:%(%m=[ap]?\\]:>))" }; /* Turn the arrow keys into macros that do some basic trn functions. ** Code provided by Clifford Adams. */ void arrow_macros(tmpbuf) char* tmpbuf; { #ifdef HAS_TERMLIB char lbuf[256]; /* should be long enough */ #ifndef MSDOS char* tmpaddr = tmpbuf; #endif register char* tmpstr; /* If arrows are defined as single keys, we probably don't * want to redefine them. (The tvi912c defines kl as ^H) */ #ifdef MSDOS strcpy(lbuf,"\035\110"); #else strcpy(lbuf,Tgetstr("ku")); /* up */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,up[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\120"); #else strcpy(lbuf,Tgetstr("kd")); /* down */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,down[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\113"); #else strcpy(lbuf,Tgetstr("kl")); /* left */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,left[auto_arrow_macros]); #ifdef MSDOS strcpy(lbuf,"\035\115"); #else strcpy(lbuf,Tgetstr("kr")); /* right */ #endif if ((int)strlen(lbuf) > 1) set_macro(lbuf,right[auto_arrow_macros]); if (*lbuf == '\033') set_macro("\033\033", "\033"); #endif } static void mac_init(tcbuf) char* tcbuf; { char tmpbuf[1024]; if (auto_arrow_macros) arrow_macros(tmpbuf); if (!use_threads || (tmpfp = fopen(filexp(getval("TRNMACRO",TRNMACRO)),"r")) == NULL) tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r"); if (tmpfp) { while (fgets(tcbuf,TCBUF_SIZE,tmpfp) != NULL) mac_line(tcbuf,tmpbuf,sizeof tmpbuf); fclose(tmpfp); } } void mac_line(line,tmpbuf,tbsize) char* line; char* tmpbuf; int tbsize; { register char* s; register char* m; register KEYMAP* curmap; register int ch; register int garbage = 0; static char override[] = "\nkeymap overrides string\n"; if (topmap == NULL) topmap = newkeymap(); if (*line == '#' || *line == '\n') return; if (line[ch = strlen(line)-1] == '\n') line[ch] = '\0'; /* A 0 length signifies we already parsed the macro into tmpbuf, ** so line is just the definition. */ if (tbsize) m = dointerp(tmpbuf,tbsize,line," \t",(char*)NULL); else m = line; if (!*m) return; while (*m == ' ' || *m == '\t') m++; for (s=tmpbuf,curmap=topmap; *s; s++) { ch = *s & 0177; if (s[1] == '+' && isdigit(s[2])) { s += 2; garbage = (*s & KM_GMASK) << KM_GSHIFT; } else garbage = 0; if (s[1]) { if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) { if (tbsize) { fputs(override,stdout) FLUSH; termdown(2); } free(curmap->km_ptr[ch].km_str); curmap->km_ptr[ch].km_str = NULL; } curmap->km_type[ch] = KM_KEYMAP + garbage; if (curmap->km_ptr[ch].km_km == NULL) curmap->km_ptr[ch].km_km = newkeymap(); curmap = curmap->km_ptr[ch].km_km; } else { if (tbsize && (curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP) { fputs(override,stdout) FLUSH; termdown(2); } else { curmap->km_type[ch] = KM_STRING + garbage; curmap->km_ptr[ch].km_str = savestr(m); } } } } static KEYMAP* newkeymap() { register int i; register KEYMAP* map; #ifndef lint map = (KEYMAP*)safemalloc(sizeof(KEYMAP)); #else map = NULL; #endif /* lint */ for (i = 127; i >= 0; i--) { map->km_ptr[i].km_km = NULL; map->km_type[i] = KM_NOTHIN; } return map; } void show_macros() { char prebuf[64]; if (topmap != NULL) { print_lines("Macros:\n",STANDOUT); *prebuf = '\0'; show_keymap(topmap,prebuf); } else { print_lines("No macros defined.\n", NOMARKING); } } static void show_keymap(curmap,prefix) register KEYMAP* curmap; char* prefix; { register int i; register char* next = prefix + strlen(prefix); register int kt; for (i = 0; i < 128; i++) { if ((kt = curmap->km_type[i]) != 0) { if (i < ' ') sprintf(next,"^%c",i+64); else if (i == ' ') strcpy(next,"\\040"); else if (i == 127) strcpy(next,"^?"); else sprintf(next,"%c",i); if ((kt >> KM_GSHIFT) & KM_GMASK) { sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK); strcat(next,cmd_buf); } switch (kt & KM_TMASK) { case KM_NOTHIN: sprintf(cmd_buf,"%s %c\n",prefix,i); print_lines(cmd_buf,NOMARKING); break; case KM_KEYMAP: show_keymap(curmap->km_ptr[i].km_km, prefix); break; case KM_STRING: sprintf(cmd_buf,"%s %s\n",prefix,curmap->km_ptr[i].km_str); print_lines(cmd_buf,NOMARKING); break; case KM_BOGUS: sprintf(cmd_buf,"%s BOGUS\n",prefix); print_lines(cmd_buf,STANDOUT); break; } } } } void set_mode(new_gmode, new_mode) char_int new_gmode; char_int new_mode; { if (gmode != new_gmode || mode != new_mode) { gmode = new_gmode; mode = new_mode; xmouse_check(); } } /* routine to pass to tputs */ int putchr(ch) register char_int ch; { putchar(ch); #ifdef lint ch = '\0'; ch = ch; #endif return 0; } int not_echoing = 0; void hide_pending() { not_echoing = 1; pushchar(0200); } bool finput_pending(check_term) bool_int check_term; { while (nextout != nextin) { if (circlebuf[nextout] != '\200') return 1; switch (not_echoing) { case 0: return 1; case 1: nextout++; nextout %= PUSHSIZE; not_echoing = 0; break; default: circlebuf[nextout] = '\n'; not_echoing = 0; return 1; } } #ifdef PENDING #ifdef USE_TK if (check_term && ttk_running) { ttk_do_waiting_events(); /* Update screen, process events. */ if (ttk_keys && *ttk_keys) return 1; } #endif if (check_term) { # ifdef FIONREAD int iocount; ioctl(0, FIONREAD, &iocount); return iocount; # else /* !FIONREAD */ # ifdef HAS_RDCHK return rdchk(0); # else /* !HAS_RDCHK */ # ifdef MSDOS return kbhit(); # else /* !MSDOS */ return circfill(); # endif /* !MSDOS */ # endif /* !HAS_RDCHK */ # endif /* !FIONREAD */ } # endif /* !PENDING */ return 0; } /* input the 2nd and succeeding characters of a multi-character command */ /* returns TRUE if command finished, FALSE if they rubbed out first character */ int buflimit = LBUFLEN; bool finish_command(donewline) int donewline; { register char* s; char gmode_save = gmode; s = buf; if (s[1] != FINISHCMD) /* someone faking up a command? */ return TRUE; set_mode('i',mode); if (not_echoing) not_echoing = 2; do { s = edit_buf(s, buf); if (s == buf) { /* entire string gone? */ fflush(stdout); /* return to single char command mode */ set_mode(gmode_save,mode); return FALSE; } if (s - buf == buflimit) break; fflush(stdout); getcmd(s); if (errno || *s == '\f') { *s = Ctl('r'); /* force rewrite on CONT */ } } while (*s != '\r' && *s != '\n'); /* until CR or NL (not echoed) */ mouse_is_down = FALSE; while (s[-1] == ' ') s--; *s = '\0'; /* terminate the string nicely */ if (donewline) newline(); set_mode(gmode_save,mode); return TRUE; /* retrn success */ } static int echo_char(ch) char_int ch; { if (((Uchar)ch & 0x7F) < ' ') { putchar('^'); putchar((ch & 0x7F) | 64); return 2; } if (ch == '\177') { putchar('^'); putchar('?'); return 2; } putchar(ch); return 1; } static bool screen_is_dirty; /*$$ remove this? */ /* Process the character *s in the buffer buf returning the new 's' */ char* edit_buf(s, cmd) register char* s; char* cmd; { static bool quoteone = FALSE; if (quoteone) { quoteone = FALSE; if (s != buf) goto echo_it; } if (*s == '\033') { /* substitution desired? */ #ifdef ESCSUBS char tmpbuf[4], *cpybuf; tmpbuf[0] = '%'; read_tty(&tmpbuf[1],1); #ifdef RAWONLY tmpbuf[1] &= 0177; #endif tmpbuf[2] = '\0'; if (tmpbuf[1] == 'h') { (void) help_subs(); *s = '\0'; reprint(); } else if (tmpbuf[1] == '\033') { *s = '\0'; cpybuf = savestr(buf); interpsearch(buf, sizeof buf, cpybuf, cmd); free(cpybuf); s = buf + strlen(buf); reprint(); } else { interpsearch(s, sizeof buf - (s-buf), tmpbuf, cmd); fputs(s,stdout); s += strlen(s); } #else notincl("^["); *s = '\0'; reprint(); #endif return s; } else if (*s == ERASECH) { /* they want to rubout a char? */ if (s != buf) { rubout(); s--; /* discount the char rubbed out */ if (!AT_NORM_CHAR(s)) rubout(); } return s; } else if (*s == KILLCH) { /* wipe out the whole line? */ while (s != buf) { /* emulate that many ERASEs */ rubout(); s--; if (!AT_NORM_CHAR(s)) rubout(); } return s; } #ifdef WORDERASE else if (*s == Ctl('w')) { /* wipe out one word? */ if (s == buf) return s; *s-- = ' '; while (!isspace(*s) || isspace(s[1])) { rubout(); if (!AT_NORM_CHAR(s)) rubout(); if (s == buf) return buf; s--; } return s+1; } #endif else if (*s == Ctl('r')) { *s = '\0'; reprint(); return s; } else if (*s == Ctl('v')) { putchar('^'); backspace(); fflush(stdout); getcmd(s); } else if (*s == '\\') quoteone = TRUE; echo_it: if (!not_echoing) echo_char(*s); return s+1; } bool finish_dblchar() { bool ret; int buflimit_save = buflimit; int not_echoing_save = not_echoing; buflimit = 2; ret = finish_command(FALSE); buflimit = buflimit_save; not_echoing = not_echoing_save; return ret; } /* discard any characters typed ahead */ void eat_typeahead() { static double last_time = 0.; double this_time = current_time(); /* do not eat typeahead while creating virtual group */ if (univ_ng_virtflag) return; /* Don't eat twice before getting a character */ if (!got_a_char) return; got_a_char = FALSE; /* cancel only keyboard stuff */ if (!allow_typeahead && !mouse_is_down && !macro_pending() && this_time - last_time > 0.3) { #ifdef PENDING register KEYMAP* curmap = topmap; Uchar lc; int i, j; for (j = 0; input_pending(); ) { errno = 0; if (read_tty(&buf[j],1) < 0) { if (errno && errno != EINTR) { perror(readerr); sig_catcher(0); } continue; } lc = *(Uchar*)buf; if ((lc & 0200) || curmap == NULL) { curmap = topmap; j = 0; continue; } j++; for (i = (curmap->km_type[lc] >> KM_GSHIFT) & KM_GMASK; i; i--) { if (!input_pending()) goto dbl_break; read_tty(&buf[j++],1); } switch (curmap->km_type[lc] & KM_TMASK) { case KM_STRING: /* a string? */ case KM_NOTHIN: /* no entry? */ curmap = topmap; j = 0; continue; case KM_KEYMAP: /* another keymap? */ curmap = curmap->km_ptr[lc].km_km; break; } } dbl_break: if (j) { /* Don't delete a partial macro sequence */ buf[j] = '\0'; pushstring(buf,0); } #else /* this is probably v7 */ # ifdef I_SGTTY ioctl(_tty_ch,TIOCSETP,&_tty); # else # ifdef I_TERMIO ioctl(_tty_ch,TCSETAW,&_tty); # else # ifdef I_TERMIOS tcsetattr(_tty_ch,TCSAFLUSH,&_tty); # else ..."Don't know how to eat typeahead!" # endif # endif # endif #endif } last_time = this_time; } void save_typeahead(buf, len) char* buf; int len; { int cnt; while (input_pending()) { cnt = read_tty(buf, len); buf += cnt; len -= cnt; } *buf = '\0'; } void settle_down() { dingaling(); fflush(stdout); /*sleep(1);*/ nextout = nextin; /* empty circlebuf */ not_echoing = 0; eat_typeahead(); } #ifdef SUPPORT_NNTP bool ignore_EINTR = FALSE; Signal_t alarm_catcher(signo) int signo; { /*printf("\n*** In alarm catcher **\n"); $$*/ ignore_EINTR = TRUE; check_datasrcs(); sigset(SIGALRM,alarm_catcher); (void) alarm(DATASRC_ALARM_SECS); } #endif /* read a character from the terminal, with multi-character pushback */ int read_tty(addr,size) char* addr; int size; { if (macro_pending()) { *addr = circlebuf[nextout++]; nextout %= PUSHSIZE; return 1; } #ifdef USE_TK if (ttk_running) { ttk_wait_for_input(); /* handle events until input is available */ if (ttk_keys && *ttk_keys) { int len = strlen(ttk_keys); if (size > len) size = len; strncpy(addr, ttk_keys, size); /* return the first bit */ if (len > size) pushstring(ttk_keys+size,0); /* and push the rest */ free(ttk_keys); /* every byte counts... */ /* A plain NULL pointer will not work -- it is "\0" in TCL */ ttk_keys = savestr(nullstr); return size; } } #endif #ifdef MSDOS *addr = getch(); if (*addr == '\0') *addr = Ctl('\035'); size = 1; #else size = read(0,addr,size); #endif #ifdef RAWONLY *addr &= 0177; #endif got_a_char = TRUE; return size; } #ifdef PENDING # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS) int circfill() { register int Howmany; errno = 0; Howmany = read(devtty,circlebuf+nextin,1); if (Howmany < 0 && (errno == EAGAIN || errno == EINTR)) Howmany = 0; if (Howmany) { nextin += Howmany; nextin %= PUSHSIZE; } return Howmany; } # endif /* FIONREAD */ #endif /* PENDING */ void pushchar(c) char_int c; { nextout--; if (nextout < 0) nextout = PUSHSIZE - 1; if (nextout == nextin) { fputs("\npushback buffer overflow\n",stdout) FLUSH; sig_catcher(0); } circlebuf[nextout] = c; } /* print an underlined string, one way or another */ void underprint(s) register char* s; { assert(UC); if (*UC) { /* char by char underline? */ while (*s) { if (!AT_NORM_CHAR(s)) { putchar('^'); backspace();/* back up over it */ underchar();/* and do the underline */ putchar((*s & 0x7F) | 64); backspace();/* back up over it */ underchar();/* and do the underline */ } else { putchar(*s); backspace();/* back up over it */ underchar();/* and do the underline */ } s++; } } else { /* start and stop underline */ underline(); /* start underlining */ while (*s) echo_char(*s++); un_underline(); /* stop underlining */ } } /* keep screen from flashing strangely on magic cookie terminals */ #ifdef NOFIREWORKS void no_sofire() { /* should we disable fireworks? */ if (!(fire_is_out & STANDOUT) && (term_line|term_col)==0 && *UP && *SE) { newline(); un_standout(); up_line(); carriage_return(); } } #endif #ifdef NOFIREWORKS void no_ulfire() { /* should we disable fireworks? */ if (!(fire_is_out & UNDERLINE) && (term_line|term_col)==0 && *UP && *US) { newline(); un_underline(); up_line(); carriage_return(); } } #endif /* get a character into a buffer */ void getcmd(whatbuf) register char* whatbuf; { register KEYMAP* curmap; register int i; bool no_macros; int times = 0; /* loop detector */ #ifdef SUPPORT_NNTP if (!input_pending()) { sigset(SIGALRM,alarm_catcher); (void) alarm(DATASRC_ALARM_SECS); } #endif tryagain: curmap = topmap; #ifdef OLD_RN_WAY no_macros = (whatbuf != buf && !macro_pending()); #else no_macros = (whatbuf != buf && !xmouse_is_on); #endif for (;;) { int_count = 0; errno = 0; #ifdef SUPPORT_NNTP ignore_EINTR = FALSE; #endif if (read_tty(whatbuf,1) < 0) { if (!errno) errno = EINTR; if (errno == EINTR) { #ifdef SUPPORT_NNTP if (ignore_EINTR) continue; (void) alarm(0); #endif return; } perror(readerr); sig_catcher(0); } lastchar = *(Uchar*)whatbuf; if (lastchar & 0200 || no_macros) { *whatbuf &= 0177; goto got_canonical; } if (curmap == NULL) goto got_canonical; for (i = (curmap->km_type[lastchar] >> KM_GSHIFT) & KM_GMASK; i; i--) read_tty(&whatbuf[i],1); switch (curmap->km_type[lastchar] & KM_TMASK) { case KM_NOTHIN: /* no entry? */ if (curmap == topmap) /* unmapped canonical */ goto got_canonical; settle_down(); goto tryagain; case KM_KEYMAP: /* another keymap? */ curmap = curmap->km_ptr[lastchar].km_km; assert(curmap != NULL); break; case KM_STRING: /* a string? */ pushstring(curmap->km_ptr[lastchar].km_str,0200); if (++times > 20) { /* loop? */ fputs("\nmacro loop?\n",stdout); termdown(2); settle_down(); } no_macros = FALSE; goto tryagain; } } got_canonical: /* This hack is for mouse support */ if (xmouse_is_on && *whatbuf == Ctl('c')) { mouse_input(whatbuf+1); times = 0; goto tryagain; } #ifdef I_SGTTY if (*whatbuf == '\r') *whatbuf = '\n'; #endif if (whatbuf == buf) whatbuf[1] = FINISHCMD; /* tell finish_command to work */ #ifdef SUPPORT_NNTP (void) alarm(0); #endif } void pushstring(str,bits) char* str; char_int bits; { register int i; char tmpbuf[PUSHSIZE]; register char* s = tmpbuf; assert(str != NULL); interp(tmpbuf,PUSHSIZE,str); for (i = strlen(s)-1; i >= 0; i--) pushchar(s[i] ^ bits); } int get_anything() { char tmpbuf[64]; char mode_save = mode; reask_anything: unflush_output(); /* disable any ^O in effect */ color_object(COLOR_MORE, 1); #ifdef VERBOSE IF(verbose) fputs("[Type space to continue] ",stdout); ELSE #endif #ifdef TERSE fputs("[MORE] ",stdout); #endif color_pop(); /* of COLOR_MORE */ fflush(stdout); eat_typeahead(); if (int_count) return -1; cache_until_key(); set_mode(gmode, 'K'); getcmd(tmpbuf); set_mode(gmode,mode_save); if (errno || *tmpbuf == '\f') { newline(); /* if return from stop signal */ goto reask_anything; /* give them a prompt again */ } if (*tmpbuf == 'h') { #ifdef VERBOSE IF(verbose) fputs("\nType q to quit or space to continue.\n",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\nq to quit, space to continue.\n",stdout) FLUSH; #endif termdown(2); goto reask_anything; } else if (*tmpbuf != ' ' && *tmpbuf != '\n') { erase_line(0); /* erase the prompt */ return *tmpbuf == 'q' ? -1 : *tmpbuf; } if (*tmpbuf == '\n') { page_line = LINES - 1; erase_line(0); } else { page_line = 1; if (erase_screen) /* -e? */ clear(); /* clear screen */ else erase_line(0); /* erase the prompt */ } return 0; } int pause_getcmd() { char mode_save = mode; unflush_output(); /* disable any ^O in effect */ color_object(COLOR_CMD, 1); #ifdef VERBOSE IF(verbose) fputs("[Type space or a command] ",stdout); ELSE #endif #ifdef TERSE fputs("[CMD] ",stdout); #endif color_pop(); /* of COLOR_CMD */ fflush(stdout); eat_typeahead(); if (int_count) return -1; cache_until_key(); set_mode(gmode,'K'); getcmd(buf); set_mode(gmode,mode_save); if (errno || *buf == '\f') return 0; /* if return from stop signal */ if (*buf != ' ') { erase_line(0); /* erase the prompt */ return *buf; } return 0; } void in_char(prompt, newmode, dflt) char* prompt; char_int newmode; char* dflt; { char mode_save = mode; char gmode_save = gmode; char* s; int newlines; for (newlines = 0, s = prompt; *s; s++) { if (*s == '\n') newlines++; } reask_in_char: unflush_output(); /* disable any ^O in effect */ printf("%s [%s] ", prompt, dflt); fflush(stdout); termdown(newlines); eat_typeahead(); set_mode('p',newmode); getcmd(buf); if (errno || *buf == '\f') { newline(); /* if return from stop signal */ goto reask_in_char; /* give them a prompt again */ } setdef(buf,dflt); set_mode(gmode_save,mode_save); } void in_answer(prompt, newmode) char* prompt; char_int newmode; { char mode_save = mode; char gmode_save = gmode; reask_in_answer: unflush_output(); /* disable any ^O in effect */ fputs(prompt,stdout); fflush(stdout); eat_typeahead(); set_mode('i',newmode); reinp_in_answer: getcmd(buf); if (errno || *buf == '\f') { newline(); /* if return from stop signal */ goto reask_in_answer; /* give them a prompt again */ } if (*buf == ERASECH) goto reinp_in_answer; if (*buf != '\n' && *buf != ' ') { if (!finish_command(FALSE)) goto reinp_in_answer; } else buf[1] = '\0'; newline(); set_mode(gmode_save,mode_save); } /* If this takes more than one line, return FALSE */ bool in_choice(prompt, value, choices, newmode) char* prompt; char* value; char* choices; char_int newmode; { char mode_save = mode; char gmode_save = gmode; char* cp; char* bp; char* s; char* prefix = NULL; int len, number_was = -1, any_val_OK = 0, value_changed; char tmpbuf[80], prefixes[80]; unflush_output(); /* disable any ^O in effect */ eat_typeahead(); set_mode('c',newmode); screen_is_dirty = FALSE; cp = choices; if (*cp == '[') { for (s = prefixes, cp++; *cp != ']'; ) { if (*cp == '/') { *s++ = '\0'; cp++; } else *s++ = *cp++; } *s++ = '\0'; *s = '\0'; if (*++cp == ' ') cp++; } else *prefixes = '\0'; for (s = tmpbuf; *cp; ) { if (*cp == '/') { *s++ = '\0'; cp++; } else if (*cp == '<') { do { *s++ = *cp; } while (*cp++ != '>'); any_val_OK = 1; /* flag that '<' was found */ } else *s++ = *cp++; } cp = s; *s++ = '\0'; *s = '\0'; strcpy(buf,value); reask_in_choice: len = strlen(buf); bp = buf; if (*prefixes != '\0') { s = prefix; for (prefix = prefixes; *prefix; prefix += strlen(prefix)) { if (*prefix == *buf) break; } if (*prefix) { for (bp = buf; *bp && *bp != ' '; bp++) ; while (*bp == ' ') bp++; } else prefix = NULL; value_changed = prefix != s; } else { prefix = NULL; value_changed = 0; } s = cp; for (;;) { cp += strlen(cp) + 1; if (!*cp) cp = tmpbuf; if (*cp == '<' && (*buf == '<' || cp[1] != '#' || isdigit(*buf) || !*s)) { prefix = NULL; break; } if (s == cp) { if (!value_changed) { if (prefix) prefix = NULL; else dingaling(); } break; } if (!*bp || strnEQ(cp,bp,any_val_OK? len : 1)) break; } if (*cp == '<') { if (*buf == '<' || cp[1] == '#') { if (number_was >= 0) sprintf(buf, "%d", number_was); else { for (s = buf; isdigit(*s); s++) ; *s = '\0'; } } } else { if (prefix) { sprintf(buf, "%s ", prefix); strcat(buf,cp); } else strcpy(buf,cp); } s = buf + strlen(buf); carriage_return(); erase_line(0); fputs(prompt,stdout); fputs(buf,stdout); len = strlen(prompt); number_was = -1; reinp_in_choice: if ((s-buf) + len >= COLS) screen_is_dirty = TRUE; fflush(stdout); getcmd(s); if (errno || *s == '\f') /* if return from stop signal */ *s = '\n'; if (*s != '\n') { char ch = *s; if (*cp == '<' && ch != '\t' && (ch != ' ' || buf != s)) { if (cp[1] == '#') { s = edit_buf(s, (char*)NULL); if (s != buf) { if (isdigit(s[-1])) goto reinp_in_choice; else number_was = atoi(buf); } } else { s = edit_buf(s, (char*)NULL); goto reinp_in_choice; } } *s = '\0'; for (s = buf; *s && *s != ' '; s++) ; if (*s == ' ') s++; if (ch == ' ' || ch == '\t') { if (prefix) *s = '\0'; else *buf = '\0'; } else { char ch1 = buf[0]; if (prefix) { if (ch == ch1) ch = *s; else { ch1 = ch; ch = buf[0]; } } sprintf(buf,"%c %c",ch == ERASECH || ch == KILLCH? '<' : ch, ch1); } goto reask_in_choice; } *s = '\0'; set_mode(gmode_save,mode_save); return !screen_is_dirty; } int print_lines(what_to_print,hilite) char* what_to_print; int hilite; { register char* s; register int i; for (s=what_to_print; *s; ) { i = check_page_line(); if (i) return i; if (hilite == STANDOUT) { #ifdef NOFIREWORKS if (erase_screen) no_sofire(); #endif standout(); } else if (hilite == UNDERLINE) { #ifdef NOFIREWORKS if (erase_screen) no_ulfire(); #endif underline(); } for (i = 0; i < COLS; i++) { if (!*s) break; if (AT_NORM_CHAR(s)) putchar(*s); else if (*s == '\t') { putchar(*s); i = ((i+8) & ~7) - 1; } else if (*s == '\n') { i = 32000; } else { i++; putchar('^'); putchar(*s + 64); } s++; } if (i) { if (hilite == STANDOUT) un_standout(); else if (hilite == UNDERLINE) un_underline(); if (AM && i == COLS) fflush(stdout); else newline(); } } return 0; } int check_page_line() { if (page_line < 0) return -1; if (page_line >= LINES || int_count) { register int cmd = -1; if (int_count || (cmd = get_anything())) { page_line = -1; /* disable further printing */ if (cmd > 0) pushchar(cmd); return cmd; } } page_line++; return 0; } void page_start() { page_line = 1; if (erase_screen) clear(); else newline(); } void errormsg(str) char* str; { if (gmode == 's') { if (str != msg) strcpy(msg,str); error_occurred = TRUE; } else if (*str) { printf("\n%s\n", str) FLUSH; termdown(2); } } void warnmsg(str) char* str; { if (gmode != 's') { printf("\n%s\n", str) FLUSH; termdown(2); pad(just_a_sec/3); } } void pad(num) int num; { register int i; for (i = num; i; i--) putchar(PC); fflush(stdout); } /* echo the command just typed */ #ifdef VERIFY void printcmd() { if (verify && buf[1] == FINISHCMD) { if (!AT_NORM_CHAR(buf)) { putchar('^'); putchar((*buf & 0x7F) | 64); backspace(); backspace(); } else { putchar(*buf); backspace(); } fflush(stdout); } } #endif void rubout() { backspace(); /* do the old backspace, */ putchar(' '); /* space, */ backspace(); /* backspace trick */ } void reprint() { register char* s; fputs("^R\n",stdout) FLUSH; termdown(1); for (s = buf; *s; s++) echo_char(*s); screen_is_dirty = TRUE; } void erase_line(to_eos) bool_int to_eos; { carriage_return(); if (to_eos) clear_rest(); else erase_eol(); carriage_return(); /* Resets kernel's tab column counter to 0 */ fflush(stdout); } void home_cursor() { char* tgoto(); if (!*HO) { /* no home sequence? */ if (!*CM) { /* no cursor motion either? */ fputs("\n\n\n", stdout); termdown(3); return; /* forget it. */ } tputs(tgoto(CM, 0, 0), 1, putchr); /* go to home via CM */ } else { /* we have home sequence */ tputs(HO, 1, putchr); /* home via HO */ } carriage_return(); /* Resets kernel's tab column counter to 0 */ term_line = term_col = 0; } void goto_xy(to_col,to_line) int to_col; int to_line; { char* tgoto(); char* str; int cmcost, xcost, ycost; if (term_col == to_col && term_line == to_line) return; if (*CM && !muck_up_clear) { str = tgoto(CM,to_col,to_line); cmcost = strlen(str); } else { str = NULL; cmcost = 9999; } if ((ycost = (to_line - term_line)) < 0) ycost = (upcost? -ycost * upcost : 7777); else if (ycost > 0) term_col = 0; if ((xcost = (to_col - term_col)) < 0) { if (!to_col && ycost+1 < cmcost) { carriage_return(); xcost = 0; } else xcost = -xcost * leftcost; } else if (xcost > 0 && cmcost < 9999) xcost = 9999; if (cmcost <= xcost + ycost) { tputs(str,1,putchr); term_line = to_line; term_col = to_col; return; } if (ycost == 7777) home_cursor(); if (to_line >= term_line) while(term_line < to_line) newline(); else while(term_line > to_line) up_line(); if (to_col >= term_col) while(term_col < to_col) term_col++, putchar(' '); else while(term_col > to_col) term_col--, backspace(); } static void line_col_calcs() { if (LINES > 0) { /* is this a crt? */ if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES]) { /* no -i or unreasonable value for initlines */ if (outspeed >= B9600) /* whole page at >= 9600 baud */ initlines = LINES; else if (outspeed >= B4800) /* 16 lines at 4800 */ initlines = 16; else /* otherwise just header */ initlines = 8; } /* Check for initlines bigger than the screen and fix it! */ if (initlines > LINES) initlines = LINES; } else { /* not a crt */ LINES = 30000; /* so don't page */ CL = "\n\n"; /* put a couple of lines between */ if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES]) initlines = 8; /* make initlines reasonable */ } if (COLS <= 0) COLS = 80; #ifdef SCAN s_resize_win(); /* let various parts know */ #endif } #ifdef SIGWINCH Signal_t winch_catcher(dummy) int dummy; { /* Reset signal in case of System V dain bramage */ sigset(SIGWINCH, winch_catcher); /* Come here if window size change signal received */ #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) { if (LINES != ws.ws_row || COLS != ws.ws_col) { LINES = ws.ws_row; COLS = ws.ws_col; line_col_calcs(); sprintf(lines_export, "%d", LINES); sprintf(cols_export, "%d", COLS); if (gmode == 's' || mode == 'a' || mode == 'p') { forceme("\f"); /* cause a refresh */ /* (defined only if TIOCSTI defined) */ } } } } #else /* Well, if SIGWINCH is defined, but TIOCGWINSZ isn't, there's */ /* almost certainly something wrong. Figure it out for yourself, */ /* because I don't know how to deal with it :-) */ ERROR! #endif } #endif void termlib_init() { #ifdef USETITE if (TI && *TI) { tputs(TI,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (KS && *KS) { tputs(KS,1,putchr); fflush(stdout); } #endif term_line = LINES-1; term_col = 0; term_scrolled = LINES; } void termlib_reset() { #ifdef USETITE if (TE && *TE) { tputs(TE,1,putchr); fflush(stdout); } #endif #ifdef USEKSKE if (KE && *KE) { tputs(KE,1,putchr); fflush(stdout); } #endif } /* Nice-Background routines used to improve interactive performance with * background processing. When called, these routines will wait for a * keystroke or a time limit to expire. Calling this before a * background loop may significantly increase responsiveness when a * user types commands quickly. */ /* NOTE: If you have problems, uncomment this define for some debugging help. * (The time limit will be 5 seconds and lots of info will be printed). */ /* #define DEBUG_NICEBG */ #ifdef PENDING #ifdef NICEBG #ifdef NBG_SELECT #undef NBG_TERMIO #undef NBG_SIGIO #endif #ifdef NBG_TERMIO #undef NBG_SELECT #undef NBG_SIGIO #endif #ifdef NBG_SIGIO #undef NBG_TERMIO #undef NBG_SELECT #endif #ifdef NBG_SIGIO /* SIGIO style */ Signal_t waitkey_sig_handler(dummy) int dummy; { #ifdef DEBUG_NICEBG /* CAA: I doubt that printf is safe in a signal handler... */ printf("wait_key_pause signal caught!\n") FLUSH; fflush(stdout); #endif } #endif /* NBG_SIGIO */ /* /dev/tty file descriptor */ /* note: for now we let the system close it on exit... */ static int wait_ttyfd INIT(-1); static bool wait_initialized INIT(FALSE); static bool wait_fd_opened INIT(FALSE); #ifdef NBG_SELECT static struct timeval wait_time; static struct fd_set wait_fdset; static int wait_tbl_size; #endif /* returns TRUE if input received */ bool wait_key_pause(ticks) int ticks; /* tenths of seconds to wait */ { #ifdef DEBUG_NICEBG ticks = 50; /* debugging: wait 5 seconds */ #endif if (input_pending()) return TRUE; #ifdef DEBUG_NICEBG printf("entry: wait_key_pause\n") FLUSH; /* */ #endif /* common initialization */ if (!wait_fd_opened) { wait_ttyfd = open("/dev/tty",0); /*$$ possible cron prob */ /* CAA: the open shouldn't really be a problem now that the * open return value is checked below. If running from cron, * I hope that the open will simply fail. If the open succeeds, * however, then there should be no cause for complaint by the * routines below. (The select method may be safest.) Still, * it might be nice to have some flag which means "we are running * in the background". */ wait_fd_opened = TRUE; } if (wait_ttyfd < 0) { /* tty open failed. Don't wait around */ return input_pending(); } #ifdef NBG_TERMIO /* First try a nice standard way */ { #ifdef I_TERMIO struct termio save_tty, wait_tty; #else /* must be TERMIOS then */ struct termios save_tty, wait_tty; #endif char lbuf[1]; /* for the read command */ int nrd; /* number read */ #ifdef DEBUG_NICEBG printf("wait_key_pause: using termio(s)\n") FLUSH; #endif if (!wait_initialized) wait_initialized = TRUE; if (ioctl(wait_ttyfd,TCGETA,&save_tty) == -1) { printf("wait_key_pause (term.c): ioctl TCGETA error\n") FLUSH; assert(FALSE); } wait_tty = save_tty; wait_tty.c_lflag &= ~ICANON; wait_tty.c_lflag &= ~ECHO; wait_tty.c_cc[VMIN] = 0; wait_tty.c_cc[VTIME] = ticks; if (ioctl(wait_ttyfd,TCSETAF,&wait_tty) == -1) { printf("wait_key_pause (term.c): ioctl TCSETAF error\n") FLUSH; assert(FALSE); } nrd = read(wait_ttyfd,&lbuf,1); ioctl(wait_ttyfd,TCSETAF,&save_tty); /* put back the old info */ if (nrd == 1) { pushchar(lbuf[0]); /* put it back */ #ifdef DEBUG_NICEBG printf("early exit: wait_key_pause\n") FLUSH; /* */ #endif } #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; /* */ #endif return input_pending(); } #endif /* NBG_TERMIO */ #ifdef NBG_SELECT #ifdef DEBUG_NICEBG printf("wait_key_pause: using select\n") FLUSH; #endif if (!wait_initialized) { wait_tbl_size = wait_ttyfd + 1; wait_initialized = TRUE; } FD_ZERO(&wait_fdset); FD_SET(wait_ttyfd,&wait_fdset); wait_time.tv_usec = (ticks%10)*1000000; wait_time.tv_sec = ticks/10; (void)select(wait_tbl_size,&wait_fdset,NULL,NULL,&wait_time); #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; /* */ #endif return input_pending(); #endif /* NBG_SELECT */ #ifdef NBG_SIGIO /* SIGIO signal sent for each keystroke style */ /* Method of last resort. Surprisingly, this code has been quite * portable to several systems. */ { #ifdef DEBUG_NICEBG printf("wait_key_pause: using SIGIO style\n") FLUSH; #endif if (!wait_initialized) { /* not initialized yet? */ /* Portable? (probably not) */ wait_initialized = TRUE; sigset(SIGALRM,waitkey_sig_handler); sigset(SIGIO,waitkey_sig_handler); /* enable SIGIO (some systems need it). ifdef it? */ fcntl(wait_ttyfd,F_SETOWN,(int)getpid()); fcntl(wait_ttyfd,F_SETFL,FASYNC); } (void) alarm((ticks+9)/10); /* sleep if no key pressed */ /* NOTE: There is a race condition here, but not a very important one IMO. * If the alarm signal is sent *before* pause() is called, then this * function will simply wait until a key is pressed. In this case * no background processing will occur. * Noted by CAA, August 1992. */ /* note2: The race condition can be minimized by using a variable * which will tell if the alarm arrived before the pause (really before * the variable was set, which should be near-instant.) Look into * this later. */ pause(); /* wait for either ALRM or SIGIO signal */ (void) alarm(0); #ifdef DEBUG_NICEBG printf("exit: wait_key_pause\n") FLUSH; #endif return input_pending(); } #endif /* NBG_SIGIO */ /* if no nice-background methods are defined, return. */ /* Thanks to Kian-Tat Lim for finding an earlier bug here */ #ifndef NBG_SELECT #ifndef NBG_TERMIO #ifndef NBG_SIGIO /* really kind of an error, but a reasonable way to handle it */ /* Act as if no input was present */ #ifdef DEBUG_NICEBG printf("wait_key_pause: no handler style, returning FALSE.\n"); #endif return FALSE; /* don't worry if not reached */ #endif #endif #endif } #endif /* NICEBG */ #endif /* PENDING */ void xmouse_init(progname) char* progname; { char* s; if (!can_home || !use_threads) return; s = getenv("XTERMMOUSE"); if (s && *s) { interp(msg, sizeof msg, s); set_option(OI_USE_MOUSE, msg); } else if (progname[strlen(progname)-1] == 'x') { /* an 'x' at the end means enable Xterm mouse tracking */ set_option(OI_USE_MOUSE, "y"); } } void xmouse_check() { mousebar_cnt = 0; if (UseMouse) { bool turn_it_on; char mmode = mode; if (gmode == 'p') { turn_it_on = TRUE; mmode = '\0'; } else if (gmode == 'i' || gmode == 'p' || (muck_up_clear && gmode != 's')) turn_it_on = FALSE; else { interp(msg, sizeof msg, MouseModes); turn_it_on = (index(msg, mode) != NULL); } if (turn_it_on) { char* s; int i, j; switch (mmode) { case 'c': mousebar_btns = NewsrcSelBtns; mousebar_cnt = NewsrcSelBtnCnt; break; case 'j': mousebar_btns = AddSelBtns; mousebar_cnt = AddSelBtnCnt; break; case 'l': mousebar_btns = OptionSelBtns; mousebar_cnt = OptionSelBtnCnt; break; case 't': mousebar_btns = NewsSelBtns; mousebar_cnt = NewsSelBtnCnt; break; case 'w': mousebar_btns = NewsgroupSelBtns; mousebar_cnt = NewsgroupSelBtnCnt; break; case 'a': case 'p': mousebar_btns = ArtPagerBtns; mousebar_cnt = ArtPagerBtnCnt; break; case 'v': mousebar_btns = UnivSelBtns; mousebar_cnt = UnivSelBtnCnt; break; default: mousebar_btns = nullstr; /*mousebar_cnt = 0;*/ break; } s = mousebar_btns; mousebar_width = 0; for (i = 0; i < mousebar_cnt; i++) { j = strlen(s); if (*s == '[') { mousebar_width += j; s += j + 1; j = strlen(s); } else mousebar_width += (j < 2? 3+1 : (j == 2? 4+1 : (j < 5? j: 5+1))); s += j + 1; } xmouse_on(); } else xmouse_off(); mouse_is_down = FALSE; } } void xmouse_on() { if (!xmouse_is_on) { /* save old highlight mouse tracking and enable mouse tracking */ fputs("\033[?1001s\033[?1000h",stdout); fflush(stdout); xmouse_is_on = TRUE; } } void xmouse_off() { if (xmouse_is_on) { /* disable mouse tracking and restore old highlight mouse tracking */ fputs("\033[?1000l\033[?1001r",stdout); fflush(stdout); xmouse_is_on = FALSE; } } void draw_mousebar(limit,restore_cursor) int limit; bool_int restore_cursor; { int i; char* s; char* t; int save_col = term_col; int save_line = term_line; mousebar_width = 0; if (mousebar_cnt == 0) return; s = mousebar_btns; t = msg; for (i = 0; i < mousebar_cnt; i++) { if (*s == '[') { while (*++s) *t++ = *s; s++; } else { switch (strlen(s)) { case 0: *t++ = ' '; /* FALL THROUGH */ case 1: case 2: *t++ = ' '; while (*s) *t++ = *s++; *t++ = ' '; break; case 3: case 4: while (*s) *t++ = *s++; break; default: strncpy(t, s, 5); t += 5; break; } } s += strlen(s) + 1; *t++ = '\0'; } mousebar_width = t - msg; mousebar_start = 0; s = msg; while (mousebar_width > limit) { int len = strlen(s) + 1; s += len; mousebar_width -= len; mousebar_start++; } goto_xy(COLS - mousebar_width - 1, LINES-1); for (i = mousebar_start; i < mousebar_cnt; i++) { putchar(' '); color_string(COLOR_MOUSE,s); s += strlen(s) + 1; } term_col = COLS-1; if (restore_cursor) goto_xy(save_col, save_line); } static void mouse_input(cp) char* cp; { static int last_btn; static int last_x, last_y; int btn; int x, y; if (cp[2] < ' ' || cp[2] > ' '+3) return; btn = (cp[2] & 3); x = cp[1] - 33; y = cp[0] - 33; if (btn != 3) { #if defined(HAS_GETTIMEOFDAY) || defined(HAS_FTIME) static double last_time = 0.; double this_time = current_time(); if (last_btn == btn && last_y == y && this_time - last_time <= 0.75 && (last_x == x || last_x == x-1 || last_x == x+1)) btn |= 4; last_time = this_time; #endif /* HAS_GETTIMEOFDAY || HAS_FTIME */ last_btn = (btn & 3); last_x = x; last_y = y; mouse_is_down = TRUE; } else { if (!mouse_is_down) return; mouse_is_down = FALSE; } if (gmode == 's') selector_mouse(btn, x,y, last_btn, last_x,last_y); else if (mode == 'a' || mode == 'p') pager_mouse(btn, x,y, last_btn, last_x,last_y); else pushchar(' '); } bool check_mousebar(btn, x,y, btn_clk, x_clk,y_clk) int btn; int x, y; int btn_clk; int x_clk, y_clk; { char* s = mousebar_btns; char* t; int i, j; int col = COLS - mousebar_width; if (mousebar_width != 0 && btn_clk == 0 && y_clk == LINES-1 && (x_clk -= col-1) > 0) { x -= col-1; for (i = 0; i < mousebar_start; i++) { if (*s == '[') s += strlen(s) + 1; s += strlen(s) + 1; } while (1) { i = strlen(s); t = s; if (*s == '[') { s += i + 1; j = 0; while (*++t == ' ') j++; } else if (i <= 2) { i = 3 + (i==2) + 1; j = 1; } else { i = (i < 5? i+1 : 5+1); j = 0; } if (x_clk < i) { int tcol = term_col; int tline = term_line; goto_xy(col+j,LINES-1); if (btn == 3) color_object(COLOR_MOUSE, 1); if (s == t) { for (j = 0; j < 5 && *t; j++, t++) term_col++, putchar(*t); } else { for (; *t && *t != ' '; t++) term_col++, putchar(*t); } if (btn == 3) color_pop(); /* of COLOR_MOUSE */ goto_xy(tcol,tline); fflush(stdout); if (btn == 3 && x > 0 && x < i) pushstring(s,0); break; } if (!(x_clk -= i)) break; x -= i; col += i; s += strlen(s) + 1; } return TRUE; } return FALSE; } static int tc_string_cnt = 0; static struct { char* capability; /* name of capability, e.g. "forground red" */ char* string; /* escape sequence, e.g. "\033[31m" */ } tc_strings[TC_STRINGS]; /* Parse a line from the [termcap] section of trnrc. */ void add_tc_string(capability, string) char* capability; char* string; { int i; for (i = 0; i < tc_string_cnt; i++) { if (strEQ(capability, tc_strings[i].capability)) { free(tc_strings[i].string); break; } } if (i == tc_string_cnt) { if (tc_string_cnt == TC_STRINGS) { fprintf(stderr,"trn: too many colors in [termcap] section (max is %d).\n", TC_STRINGS); finalize(1); } tc_string_cnt++; tc_strings[i].capability = savestr(capability); } tc_strings[i].string = savestr(string); } /* Return the named termcap color capability's string. */ char* tc_color_capability(capability) char* capability; { int c; for (c = 0; c < tc_string_cnt; c++) { if (strEQ(tc_strings[c].capability, capability)) return tc_strings[c].string; } return NULL; } #ifdef MSDOS int tputs(str,num,func) char* str; int num; int (*func) _((char_int)); { printf(str,num); return 0; } #endif #ifdef MSDOS char* tgoto(str,x,y) char* str; int x; int y; { static char gbuf[32]; sprintf(gbuf,str,y+1,x+1); return gbuf; } #endif