/* 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
syntax highlighted by Code2HTML, v. 0.9.1