This is term.c in view mode; [Download] [Up]
/*
* (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
*
* Terminal interface.
*/
#include <signal.h>
#include <errno.h>
#include "config.h"
#include "term.h"
#include "keymap.h"
#include "regexp.h"
#ifdef RESIZING
#include <sys/ioctl.h> /* for TIOCGWINSZ */
#ifdef SYSV_RESIZING
#include <sys/stream.h>
#include <sys/ptem.h>
#endif
extern int s_resized;
#endif
import int data_bits;
import int batch_mode;
struct msg_list {
char *buf;
struct msg_list *prev;
};
static struct msg_list *msg_stack = NULL, *msg_ptr = NULL;
export int message_history = 15;
export char *term_name = NULL;
export int show_current_time = 1;
export int conf_dont_sleep = 0;
export int prompt_length;
export int terminal_speed = 0;
export int slow_speed = 1200;
export int any_message = 0;
export int flow_control = 1;
export int use_visible_bell = 1; /* if supported by terminal */
export int ignore_xon_xoff = 1;
export int multi_key_guard_time = 2; /* tenths of a second */
export int guard_double_slash = 0; /* need /// or //+ to clear line */
export key_type help_key = '?';
export key_type comp1_key = SP;
export key_type comp2_key = TAB;
export key_type erase_key, kill_key;
export key_type delword_key = CONTROL_('W');
static char bell_str[256] = "\007";
static appl_keypad_mode = 0;
#ifdef USE_TERMINFO
#include <curses.h>
#ifndef auto_left_margin
#include <term.h>
#endif
#define HAS_CAP(str) (str && *str)
extern char *tgoto(); /* some systems don't have this in term.h */
#else
#define USE_TERMCAP
char *tgoto();
char PC;
char *BC, *UP;
short ospeed;
static char XBC[64], XUP[64];
static char enter_ca_mode[64], exit_ca_mode[64];
static char cursor_home[64];
static char cursor_address[128];
static char clear_screen[64];
static char clr_eol[64];
static char clr_eos[64];
static char enter_standout_mode[64], exit_standout_mode[64];
static char enter_underline_mode[64], exit_underline_mode[64];
static char key_down[64], key_up[64], key_right[64], key_left[64];
static char keypad_local[64], keypad_xmit[64];
int magic_cookie_glitch; /* magic cookie size */
int ceol_standout_glitch; /* hp brain damage! */
#define putp(str) tputs(str, 0, outc)
#define HAS_CAP(str) (*str)
static outc(c)
{
putchar(c);
}
#endif /* USE_TERMCAP */
int Lines, Columns; /* screen size */
int cookie_size; /* size of magic cookie */
int two_cookies; /* space needed to enter&exit standout mode */
int STANDOUT; /* terminal got standout mode */
#ifdef FAKE_INTERRUPT
#include <setjmp.h>
extern jmp_buf fake_keyb_sig;
extern int arm_fake_keyb_sig;
extern char fake_keyb_siglist[];
#endif
#ifdef HAVE_TERMIO
#define KEY_BURST 50 /* read bursts of 50 chars (or timeout after 100 ms) */
#ifdef USE_TERMCAP
#include <termio.h>
#endif
#undef CBREAK
static struct termio norm_tty, raw_tty;
#define IntrC norm_tty.c_cc[VINTR]
#define EraseC norm_tty.c_cc[VERASE]
#define KillC norm_tty.c_cc[VKILL]
#define SuspC CONTROL_('Z') /* norm_tty.c_cc[SWTCH] */
#else /* V7/BSD TTY DRIVER */
#include <sgtty.h>
static struct sgttyb norm_tty, raw_tty;
static struct tchars norm_chars;
#define IntrC norm_chars.t_intrc
#define EraseC norm_tty.sg_erase
#define KillC norm_tty.sg_kill
#ifdef TIOCGLTC
static struct ltchars spec_chars;
#define SuspC spec_chars.t_suspc
#else
#define SuspC CONTROL_('Z')
#endif
#endif
#ifdef USE_TERMCAP
opt_cap(cap, buf)
char *cap, *buf;
{
char *tgetstr();
*buf = NUL;
return tgetstr(cap, &buf) != NULL;
}
get_cap(cap, buf)
char *cap, *buf;
{
if (!opt_cap(cap, buf))
user_error("TERMCAP entry for %s has no '%s' capability",
term_name, cap);
}
#endif /* USE_TERMCAP */
/*
* timeout in n/10 seconds via SIGALRM
*/
micro_alarm(n)
int n;
{
#ifdef HAVE_UALARM
ualarm(n<=1 ? 100000 : n*100000, 0); /* 4.3 BSD ualarm() */
#else
#ifdef MICRO_ALARM
if (n <= 0) n = 1;
MICRO_ALARM(n); /* System specific timeout */
#else
alarm(n <= 10 ? 1 : (n+9)/10); /* Standard alarm() call */
#endif
#endif
}
static int multi_keys = 0;
static struct multi_key {
key_type *cur_key;
key_type *keys;
key_type code;
} multi_key_list[MULTI_KEYS];
enter_multi_key(code, keys)
int code;
key_type *keys;
{
register i;
if (strlen((char *)keys) == 1)
/* will ignore arrow keys overlaying these keys */
if (*keys == NL || *keys == CR ||
*keys == erase_key || *keys == kill_key ||
*keys == IntrC) return;
/* lookup code to see if it is already defined... */
for (i = 0; i < multi_keys; i++)
if (multi_key_list[i].code == (key_type)code)
goto replace_key;
i = multi_keys++;
/* now i points to matching or empty slot */
if (i >= MULTI_KEYS) {
/* should never happen */
log_entry('E', "too many multi keys");
return;
}
replace_key:
multi_key_list[i].keys = keys;
multi_key_list[i].code = code;
}
dump_multi_keys()
{
register i;
register key_type *cp;
clrdisp();
pg_init(0, 1);
for (i = 0; i < multi_keys; i++) {
if (pg_next() < 0) break;
printf("%d\t%s\t", i, key_name(multi_key_list[i].code));
for (cp = multi_key_list[i].keys; *cp; cp++) {
putchar(SP);
fputs(key_name(*cp), stdout);
}
}
pg_end();
}
#ifdef RESIZING
sig_type catch_winch(n)
{
struct winsize winsize;
(void) signal(SIGWINCH, catch_winch);
if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
&& (winsize.ws_row != Lines || winsize.ws_col != Columns)) {
Lines = winsize.ws_row;
Columns = winsize.ws_col;
s_redraw = 1;
s_resized = 1;
}
#ifdef FAKE_INTERRUPT
if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
longjmp(fake_keyb_sig, 1);
#endif
}
#endif /* RESIZING */
#ifdef SV_INTERRUPT
#ifdef NO_SIGINTERRUPT
static siginterrupt(signo, on)
{
struct sigvec sv;
sv.sv_handler = signal (signo, SIG_DFL);
sv.sv_mask = 0;
sv.sv_flags = on ? SV_INTERRUPT : 0;
sigvec (signo, &sv, 0);
}
#endif
#endif
#ifdef FAKE_INTERRUPT
#define SV_INTERRUPT
static siginterrupt (signo, on)
{
fake_keyb_siglist[signo] = on;
}
#endif
static unsigned sp_table[] = {
B9600, 960,
#ifdef B19200
B19200, 1920,
#else
#ifdef EXTA
EXTA, 1920,
#endif
#endif
#ifdef B38400
B38400, 3840,
#else
#ifdef EXTB
EXTB, 3840,
#endif
#endif
B1200, 120,
B2400, 240,
B4800, 480,
B300, 30,
0, 0
};
static set_term_speed(sp)
register unsigned long sp;
{
register unsigned *tp;
for (tp = sp_table; *tp; tp += 2)
if (*tp == sp) {
terminal_speed = tp[1];
return;
}
terminal_speed = 30;
}
static raw_not_ok_error()
{
if (batch_mode) return 0;
user_error("Not prepared for terminal i/o");
}
#define RAW_CHECK if (terminal_speed == 0) return raw_not_ok_error()
#define BATCH_CHECK if (terminal_speed == 0) return 0
init_term(full)
int full;
{
#ifdef USE_TERMCAP
char tbuf[1024];
#endif
if (batch_mode) {
term_name = "batch";
close(0);
open("/dev/null", 0);
STANDOUT = 0;
cookie_size = 1;
return;
}
if ((term_name = getenv("TERM")) == NULL) {
if (full)
user_error("No TERM variable in environment");
else
term_name = "unknown";
}
if (!full) return;
#ifdef HAVE_TERMIO
ioctl(0, TCGETA, &norm_tty);
#else
ioctl(0, TIOCGETP, &norm_tty);
#endif
#ifdef USE_TERMINFO
setupterm((char *)NULL, 1, (int *)NULL);
Columns = columns;
Lines = lines;
cookie_size = magic_cookie_glitch;
if (use_visible_bell && HAS_CAP(flash_screen))
strcpy(bell_str, flash_screen);
else if (HAS_CAP(bell))
strcpy(bell_str, bell);
if (! HAS_CAP(cursor_home))
cursor_home = copy_str(tgoto(cursor_address, 0, 0));
#else
if (tgetent(tbuf, term_name) <= 0)
user_error("Unknown terminal type: %s", term_name);
if (opt_cap("bc", XBC)) BC = XBC;
if (opt_cap("up", XUP)) UP = XUP;
opt_cap("pc", cursor_address); /* temp. usage */
PC = cursor_address[0];
get_cap("cm", cursor_address);
if (!opt_cap("ho", cursor_home))
strcpy(cursor_home, tgoto(cursor_address, 0, 0));
get_cap("cl", clear_screen);
get_cap("ce", clr_eol);
opt_cap("cd", clr_eos);
Lines = tgetnum("li");
Columns = tgetnum("co");
opt_cap("so", enter_standout_mode);
opt_cap("se", exit_standout_mode);
opt_cap("us", enter_underline_mode);
opt_cap("ue", exit_underline_mode);
opt_cap("kd", key_down);
opt_cap("ku", key_up);
opt_cap("kr", key_right);
opt_cap("kl", key_left);
cookie_size = tgetnum("sg");
ceol_standout_glitch = tgetflag("xs");
opt_cap("ti", enter_ca_mode);
opt_cap("te", exit_ca_mode);
opt_cap("ks", keypad_xmit); /* used to turn "application cursor */
opt_cap("ke", keypad_local); /* key" mode on and off (sometimes) */
if (!use_visible_bell || !opt_cap("vb", bell_str))
if (!opt_cap("bl", bell_str))
strcpy(bell_str, "\007");
#endif /* !USE_TERMINFO */
#ifdef RESIZING
{
struct winsize winsize;
if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
&& winsize.ws_row != 0 && winsize.ws_col != 0) {
Lines = winsize.ws_row;
Columns = winsize.ws_col;
(void) signal(SIGWINCH, catch_winch);
#ifdef SV_INTERRUPT
siginterrupt(SIGWINCH, 1); /* make read from tty interruptable */
#endif /* SV_INTERRUPT */
}
}
#endif /* RESIZING */
STANDOUT = HAS_CAP(enter_standout_mode);
if (STANDOUT) {
if (cookie_size < 0) cookie_size = 0;
two_cookies = 2 * cookie_size;
} else
cookie_size = two_cookies = 0;
raw_tty = norm_tty;
#ifdef HAVE_TERMIO
raw_tty.c_iflag &= ~(BRKINT|INLCR|ICRNL|IGNCR|ISTRIP);
raw_tty.c_iflag |= IGNBRK|IGNPAR;
raw_tty.c_oflag &= ~OPOST;
raw_tty.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|NOFLSH);
/* read a maximum of 10 characters in one burst; timeout in 1-200 ms */
raw_tty.c_cc[VEOF] = KEY_BURST;
raw_tty.c_cc[VEOL] = ((raw_tty.c_cflag & CBAUD) > B1200) ? 1 : 2;
set_term_speed((unsigned long)(raw_tty.c_cflag & CBAUD));
#else
ioctl(0, TIOCGETC, &norm_chars);
#ifdef TIOCGLTC
ioctl(0, TIOCGLTC, &spec_chars);
#endif
ospeed = norm_tty.sg_ospeed;
set_term_speed((unsigned long)ospeed);
raw_tty.sg_flags &= ~(ECHO | CRMOD);
#ifdef CBREAK
#ifdef SV_INTERRUPT /* make read from tty interruptable */
siginterrupt(SIGTSTP, 1); /* this is necessary to redraw screen */
#endif
raw_tty.sg_flags |= CBREAK;
#else
raw_tty.sg_flags |= RAW;
#endif
#ifdef SV_INTERRUPT
siginterrupt(SIGALRM, 1); /* make read from tty interruptable */
#endif
#endif
erase_key = (key_type)EraseC;
kill_key = (key_type)KillC;
if (HAS_CAP(key_down))
enter_multi_key(K_down_arrow, (key_type *)key_down);
if (HAS_CAP(key_up))
enter_multi_key(K_up_arrow, (key_type *)key_up);
if (HAS_CAP(key_right))
enter_multi_key(K_right_arrow, (key_type *)key_right);
if (HAS_CAP(key_left))
enter_multi_key(K_left_arrow, (key_type *)key_left);
appl_keypad_mode = (HAS_CAP(keypad_xmit) && HAS_CAP(keypad_local));
if (!HAS_CAP(key_up)) appl_keypad_mode = 0; /* no cursor keys */
if (appl_keypad_mode) {
/* Use of ks/ke isn't consistent, so we must guess what to do. */
/* If termcap expects keys to send ESC [, don't switch */
appl_keypad_mode = (key_up[0] != '\033' || key_up[1] != '[');
}
visual_on();
}
home()
{
BATCH_CHECK;
putp(cursor_home);
}
static int curxy_c = -1, curxy_l, savxy_c = -1, savxy_l;
save_xy()
{
savxy_c = curxy_c; savxy_l = curxy_l;
}
restore_xy()
{
if (savxy_c < 0) return;
gotoxy(savxy_c, savxy_l); fl;
}
gotoxy(c, l)
int c, l;
{
BATCH_CHECK;
curxy_c = c; curxy_l = l;
putp(tgoto(cursor_address, c, l));
}
clrdisp()
{
BATCH_CHECK;
#ifdef USE_TERMINFO
putp(clear_screen); /* tputs is broken on UNISYS I've been told */
#else
tputs(clear_screen, Lines, outc);
#endif
curxy_c = savxy_c = -1;
msg_ptr = msg_stack;
}
clrline()
{
BATCH_CHECK;
putp(clr_eol);
fl;
}
clrline_noflush()
{
BATCH_CHECK;
putp(clr_eol);
}
clrpage(lineno)
register int lineno;
{
register int olineno= lineno;
BATCH_CHECK;
if (HAS_CAP(clr_eos)) {
#ifdef USE_TERMINFO
putp(clr_eos);
#else
tputs(clr_eos, Lines - lineno, outc);
#endif
} else {
clrline();
lineno++;
for (; lineno < Lines; lineno++) {
gotoxy(0, lineno);
putp(clr_eol);
}
gotoxy(0, olineno);
fl;
}
msg_ptr = msg_stack;
}
static char so_buf[512], *so_p;
static int so_c, so_l, so_b, so_active = 0;
so_gotoxy(c, l, blank)
{
if (!STANDOUT && c >= 0) {
if (l >= 0) gotoxy(c, l);
return 0;
}
so_active++;
so_c = c;
so_l = l;
so_b = blank;
so_p = so_buf;
*so_p = NUL;
return 1; /* not really true if not standout & c < 0 */
}
/*VARARGS*/
so_printf(va_alist)
va_dcl
{
use_vararg;
start_vararg;
so_vprintf(va_args1toN);
end_vararg;
}
so_vprintf(va_tail)
va_tdcl
{
char *fmt;
fmt = va_arg1(char *);
if (!so_active) {
if (ceol_standout_glitch) highlight(0);
vprintf(fmt, va_args2toN);
return;
}
vsprintf(so_p, fmt, va_args2toN);
while (*so_p) so_p++;
}
so_end()
{
int len;
if (!so_active) return;
if (so_l >= 0) {
len = so_p - so_buf + two_cookies;
if (so_c < 0)
so_c = Columns - len - 2;
if (so_c < 0) so_c = 0;
if (len + so_c >= Columns) {
len = Columns - so_c - two_cookies;
so_buf[len] = NUL;
}
if (cookie_size) {
gotoxy(so_c + len - cookie_size, so_l);
putp(exit_standout_mode);
}
gotoxy(so_c, so_l);
}
if ((so_b & 1) && (!STANDOUT || !cookie_size)) putchar(SP);
if (STANDOUT) {
if (ceol_standout_glitch) clrline();
putp(enter_standout_mode);
}
fputs(so_buf, stdout);
if (STANDOUT) putp(exit_standout_mode);
if ((so_b & 2) && (!STANDOUT || !cookie_size)) putchar(SP);
so_active = 0;
}
/*VARARGS*/
so_printxy(va_alist)
va_dcl
{
int k, l;
use_vararg;
start_vararg;
k = va_arg1(int);
l = va_arg2(int);
so_gotoxy(k, l, 0);
so_vprintf(va_args3toN);
so_end();
end_vararg;
}
underline(on)
{
if (cookie_size) return 0;
if (! HAS_CAP(enter_underline_mode)) return 0;
putp(on ? enter_underline_mode : exit_underline_mode);
return 1;
}
highlight(on)
{
if (cookie_size) return 0;
if (! HAS_CAP(enter_standout_mode)) return 0;
putp(on ? enter_standout_mode : exit_standout_mode);
return 1;
}
static int is_visual = 0;
static int is_raw = 0;
#ifdef HAVE_TERMIO
#define RAW_MODE_ON ioctl(0, TCSETAW, &raw_tty)
#define RAW_MODE_OFF ioctl(0, TCSETAW, &norm_tty)
#else
#define RAW_MODE_ON ioctl(0, TIOCSETP, &raw_tty)
#define RAW_MODE_OFF ioctl(0, TIOCSETP, &norm_tty)
#endif
visual_on()
{
BATCH_CHECK;
if (HAS_CAP(enter_ca_mode)) {
putp(enter_ca_mode);
is_visual = 1;
}
if (appl_keypad_mode) {
putp(keypad_xmit);
is_visual = 1;
}
}
visual_off()
{
int was_raw = is_raw;
if (terminal_speed == 0) return 0;
if (is_visual) {
if (appl_keypad_mode) putp(keypad_local);
if (HAS_CAP(exit_ca_mode)) putp(exit_ca_mode);
fl;
is_visual = 0;
}
is_raw = 1;
unset_raw();
return was_raw;
}
#ifdef CBREAK
raw()
{
RAW_CHECK;
if (is_raw == 1)
return;
is_raw = 1;
RAW_MODE_ON;
}
no_raw()
{
return 0;
}
unset_raw()
{
if (is_raw == 0)
return 0;
RAW_CHECK;
RAW_MODE_OFF;
is_raw = 0;
return 1;
}
#else /* not CBREAK */
static int must_set_raw = 1;
raw()
{
RAW_CHECK;
if (!flow_control) {
if (!must_set_raw) return;
must_set_raw = 0;
}
if (is_raw) return;
RAW_MODE_ON;
is_raw++;
}
no_raw()
{
if (!flow_control) return 0;
if (!is_raw) return 0;
RAW_CHECK;
RAW_MODE_OFF;
is_raw = 0;
return 1;
}
unset_raw()
{
int was_raw = is_raw;
if (is_raw) {
RAW_CHECK;
RAW_MODE_OFF;
is_raw = 0;
}
if (!flow_control)
must_set_raw = 1;
return was_raw;
}
#endif /* CBREAK */
#ifndef KEY_BURST
#define KEY_BURST 32
#endif
#define RD_PUSHBACK 10
static char rd_buffer[KEY_BURST+RD_PUSHBACK]; /* Holds stuff from read */
static char *rd_ptr;
static int rd_count = 0, rd_alarm = 0;
#ifdef FAKE_INTERRUPT
static jmp_buf fake_alarm_sig;
#endif
static sig_type rd_timeout()
{
rd_alarm = 1;
#ifdef FAKE_INTERRUPT
longjmp(fake_alarm_sig, 1);
#endif
}
#define RD_TIMEOUT 0x1000
#define RD_INTERRUPT 0x1001
static int read_char_kbd(tmo)
int tmo; /* timeout if no input arrives */
{
if (rd_count <= 0) {
if (tmo) {
#ifdef FAKE_INTERRUPT
if (setjmp(fake_alarm_sig)) goto tmout;
#endif
rd_alarm = 0;
signal(SIGALRM, rd_timeout);
micro_alarm(multi_key_guard_time);
}
rd_ptr = rd_buffer + RD_PUSHBACK;
rd_count = read(0, rd_ptr, KEY_BURST);
if (tmo) {
if (rd_alarm) goto tmout;
alarm(0);
}
if (rd_count < 0) {
if (errno != EINTR) s_hangup++;
return RD_INTERRUPT;
}
}
--rd_count;
return *rd_ptr++;
tmout:
rd_count = 0;
return RD_TIMEOUT;
}
static unread_char(c)
char c;
{
if (rd_ptr == rd_buffer) return;
rd_count++;
*--rd_ptr = c;
}
flush_input()
{
int arg;
BATCH_CHECK;
#ifdef HAVE_TERMIO
ioctl(0, TCFLSH, 0);
#else
#ifdef FREAD
arg = FREAD;
ioctl(0, TIOCFLUSH, &arg);
#else
ioctl(0, TIOCFLUSH, 0);
#endif
#endif
rd_count = 0;
}
int enable_stop = 1;
static int do_macro_processing = 1;
get_c()
{
key_type c, first_key;
int key_cnt, mc, n;
register struct multi_key *mk, *multi_match;
register int i;
next_key:
if (s_hangup) return K_interrupt;
if (do_macro_processing)
switch (m_getc(&mc)) {
case 0: break;
case 1: return mc;
case 2: return K_interrupt;
}
#ifdef RESIZING
if (s_resized) goto redraw;
#endif
if (batch_mode)
user_error("Attempt to read keyboard input in batch mode");
for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++)
mk->cur_key = mk->keys;
key_cnt = 0;
#ifdef FAKE_INTERRUPT
if (setjmp(fake_keyb_sig)) goto intr;
arm_fake_keyb_sig = 1;
#endif
multi:
switch (n = read_char_kbd(key_cnt)) {
case RD_INTERRUPT:
#ifdef CBREAK
if (s_redraw) goto redraw;
#endif
#ifdef RESIZING
if (s_resized) goto redraw;
#endif
goto intr;
case RD_TIMEOUT:
while (--key_cnt > 0) unread_char(multi_match->keys[key_cnt]);
c = first_key;
goto got_char;
default:
c = (key_type)n;
if (data_bits < 8) c &= 0x7f;
if (ignore_xon_xoff)
if (c == CONTROL_('Q') || c == CONTROL_('S')) goto multi;
break;
}
multi_match = NULL;
for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++) {
if (mk->cur_key == NUL) continue;
if (*(mk->cur_key)++ != c) {
mk->cur_key = NUL;
continue;
}
if (*(mk->cur_key) == NUL) {
c = mk->code;
goto got_char;
}
multi_match = mk;
}
if (multi_match) {
if (key_cnt == 0) first_key = c;
key_cnt++;
goto multi;
}
if (key_cnt) {
if (key_cnt == 1 && first_key == 033) {
unread_char(c);
c = 033;
goto got_char;
}
ding();
flush_input();
goto next_key;
}
got_char:
#ifdef FAKE_INTERRUPT
arm_fake_keyb_sig = 0;
#endif
c = global_key_map[c];
#ifndef CBREAK
if (c == IntrC) return K_interrupt; /* don't flush */
if (c == SuspC) {
if (enable_stop && suspend_nn()) goto redraw;
goto next_key;
}
#endif
return (int)c;
intr:
#ifdef FAKE_INTERRUPT
arm_fake_keyb_sig = 0;
#endif
rd_count = 0;
return K_interrupt;
redraw:
#ifdef RESIZING
s_resized = 0;
#endif
s_redraw = 0;
return GETC_COMMAND | K_REDRAW;
}
/*
* read string with completion, pre-filling, and break on first char
*
* dflt is a string that will be use as default value if the
* space bar is hit as the first character.
*
* prefill pre-fill the buffer with .... and print it
*
* break_chars return immediately if one of these characters
* is entered as the first character.
*
* completion is a function that will fill the buffer with a value
* see the group_completion and file_completion routines
* for examples.
*/
char *get_s(dflt, prefill, break_chars, completion)
char *dflt, *prefill, *break_chars;
fct_type completion;
{
static key_type buf[GET_S_BUFFER];
register char *cp;
register int i, c, lastc;
char *ret_val = (char *)buf;
int comp_used, comp_len;
int ostop, max, did_help;
int hit_count;
switch (m_gets(buf)) {
case 0:
break;
case 1:
return (char *)buf;
case 2:
return NULL;
}
ostop = enable_stop;
enable_stop = 0;
do_macro_processing = 0;
hit_count = 0;
max = Columns - prompt_length;
if (max >= FILENAME) max = FILENAME-1;
i = comp_len = comp_used = did_help = 0;
if (prefill && prefill[0]) {
while (c = *prefill++) {
if (i == max) break;
putchar(c);
buf[i] = c;
i++;
}
fl;
}
if (dflt && *dflt == NUL)
dflt = NULL;
if (break_chars && *break_chars == NUL)
break_chars = NULL;
c = NUL;
for(;;) {
lastc = c;
c = get_c();
if (c & GETC_COMMAND) continue;
kill_prefill_hack:
hit_count++;
if (i == 0) {
if (c == comp1_key && dflt) {
while ((c = *dflt++) != NUL && i < max) {
putchar(c);
buf[i] = c;
i++;
}
fl;
dflt = NULL;
continue;
}
if (cp = break_chars) {
while (*cp)
if (*cp++ == c) {
buf[0] = c;
buf[1] = NUL;
goto out;
}
}
}
if (completion != NULL_FCT) {
if (comp_used && c == erase_key) {
if (comp_len) {
i -= comp_len;
while (--comp_len >= 0) putchar(BS);
clrline();
}
if (!CALL(completion)(buf, -(i+1)) && did_help)
clrmsg(i);
did_help = 0;
comp_len = comp_used = 0;
if (lastc == help_key) goto no_completion;
continue;
}
if (c == comp1_key || c == comp2_key || c == help_key) {
if (!comp_used || c == comp2_key ||
(c == help_key && lastc != c)) {
buf[i] = NUL;
if ((comp_used = CALL(completion)(buf, i)) == 0) {
ding();
continue;
}
if (comp_used < 0) {
comp_used = 0;
goto no_completion;
}
comp_len = 0;
}
if (c == help_key) {
if (CALL(completion)((char *)NULL, 1)) {
gotoxy(prompt_length+i, prompt_line); fl;
did_help = 1;
}
continue;
}
if (comp_len) {
i -= comp_len;
while (--comp_len >= 0) putchar(BS);
clrline();
comp_len = 0;
}
switch ( CALL(completion)((char *)NULL, 0) ) {
case 0: /* no possible completion */
comp_used = 0;
ding();
continue;
case 2: /* whole new alternative */
while (--i >= 0) putchar(BS);
clrline();
/* fall thru */
case 1: /* completion */
comp_len = i;
while (c = buf[i]) {
if (i == max) break;
putchar(c);
i++;
}
fl;
comp_len = i - comp_len;
continue;
}
}
if (comp_used) {
if (!CALL(completion)(buf, -(i+1)) && did_help)
clrmsg(i);
did_help = 0;
comp_len = comp_used = 0;
}
}
no_completion:
if (c == CR || c == NL) {
buf[i] = NUL;
break;
}
if (c == erase_key) {
if (i <= 0) continue;
i--;
putchar(BS);
putchar(' ');
putchar(BS);
fl;
continue;
}
if (c == delword_key) {
if (i <= 0) continue;
buf[i-1] = 'X';
while (i > 0 && isalnum(buf[i-1])) { putchar(BS); i--; }
clrline();
continue;
}
if (c == kill_key) {
while (i > 0) { putchar(BS); i--; }
clrline();
if (hit_count == 1 && dflt) {
c = comp1_key;
goto kill_prefill_hack;
}
continue;
}
if (c == K_interrupt) {
ret_val = NULL;
break;
}
if (data_bits == 8) {
if (!iso8859(c)) continue;
} else
if (!isascii(c) || !isprint(c)) continue;
if (i == max) continue;
if (i > 0 && buf[i-1] == '/' && (c == '/' || c == '+')) {
if (c != '/' || !guard_double_slash || (i > 1 && buf[i-2] == '/')) {
extern int file_completion();
if (completion == file_completion) {
while (i > 0) { putchar(BS); i--; }
clrline();
}
}
}
putchar(c);
fl;
buf[i] = c;
i++;
}
out:
enable_stop = ostop;
do_macro_processing = 1;
return ret_val;
}
export int list_offset = 0;
list_completion(str)
char *str;
{
static int cols, line;
if (str == NULL) {
cols = Columns;
line = prompt_line + 1;
if (line == Lines - 1) cols--;
gotoxy(0, line);
clrpage(line);
return 1;
}
str += list_offset;
for (;;) {
cols -= strlen(str);
if (cols >= 0) {
printf("%s%s", str, cols > 0 ? " " : "");
cols--;
return 1;
}
if (line >= Lines - 1) return 0;
line++;
cols = Columns;
gotoxy(0, line);
if (line == Lines - 1) cols--;
}
}
yes(must_answer)
int must_answer;
{
int c, help = 1, in_macro = 0;
switch (m_yes()) {
case 0:
break;
case 1:
return 0;
case 2:
return 1;
case 3:
do_macro_processing = 0;
in_macro++;
break;
}
fl;
for (;;) {
if (!is_raw) {
raw();
c = get_c();
unset_raw();
} else
c = get_c();
if (c == 'y' || c == 'Y') {
c = 1;
break;
}
if (must_answer == 0 && (c == SP || c == CR || c == NL)) {
c = 1;
break;
}
if (c == 'n' || c == 'N') {
c = 0;
break;
}
if (c == K_interrupt) {
c = -1;
break;
}
if (help) {
fputs(" y=YES n=NO", stdout); fl;
prompt_length += 11;
help = 0;
}
}
if (in_macro) {
if (c < 0) m_break();
do_macro_processing = 1;
}
return c;
}
ding()
{
BATCH_CHECK;
putp(bell_str);
fl;
}
display_file(name, modes)
char *name;
int modes;
{
FILE *f;
register c, stand_on;
int linecnt, headln_cnt, hdline, no_conf;
char headline[128];
import char *help_directory;
headline[0] = 0;
hdline = 0;
no_conf = 0;
headln_cnt = -1;
if (modes & CLEAR_DISPLAY) {
gotoxy(0,0);
clrdisp();
}
linecnt = Lines - 1;
chain:
if (*name != '/') name = relative(help_directory, name);
f = open_file(name, OPEN_READ);
if (f == NULL)
printf("\r\n\nFile %s is not available\n\n", name);
else {
stand_on = 0;
while ((c = getc(f)) != EOF) {
#ifdef HAVE_JOBCONTROL
if (s_redraw) {
no_conf = 1;
break;
}
#endif
no_conf = 0;
if (c == '\1') {
if (STANDOUT) {
putp(stand_on ? exit_standout_mode : enter_standout_mode);
stand_on = !stand_on;
}
continue;
}
if (c == '\2') {
headln_cnt = 0;
continue;
}
if (c == '\3') {
headln_cnt = 0;
while ((c = getc(f)) != EOF && c != NL)
headline[headln_cnt++] = c;
headline[headln_cnt++] = NUL;
name = headline;
fclose(f);
goto chain;
}
if (c == '\4') {
printf("%s", version_id);
continue;
}
if (headln_cnt >= 0)
headline[headln_cnt++] = c;
if (hdline) {
puts(headline);
putchar(CR);
hdline = 0;
linecnt--;
}
putchar(c);
if (c == NL) {
putchar(CR);
if (headln_cnt >= 0) {
headline[--headln_cnt] = 0;
headln_cnt = -1;
}
if (--linecnt == 0) {
no_conf = 1;
if (any_key(0) == K_interrupt)
break;
linecnt = Lines - 1;
if (modes & CLEAR_DISPLAY) {
gotoxy(0,0);
clrdisp();
}
hdline = headline[0];
}
}
}
if (stand_on) putp(exit_standout_mode);
fclose(f);
}
prompt_line = Lines-1; /* move prompt to last line */
if (!no_conf && (modes & CONFIRMATION))
any_key(prompt_line);
}
/*VARARGS*/
user_error(va_alist)
va_dcl
{
char *fmt;
use_vararg;
if (terminal_speed != 0) {
clrdisp();
visual_off();
fl;
}
start_vararg;
fmt = va_arg1(char *);
vprintf(fmt, va_args2toN);
putchar(NL);
end_vararg;
nn_exit(1);
}
/*VARARGS*/
msg(va_alist)
va_dcl
{
use_vararg;
start_vararg;
vmsg(va_args1toN);
end_vararg;
}
push_msg(str)
char *str;
{
register struct msg_list *mp, *newmsg;
static int slots = 0;
if (str != NULL) {
if (slots > message_history) {
for (mp = newmsg = msg_stack; mp->prev != NULL; mp = mp->prev)
newmsg = mp;
if (newmsg == mp)
msg_stack = NULL;
else {
newmsg->prev = NULL;
newmsg = mp;
}
freeobj(newmsg->buf);
} else {
slots++;
newmsg = newobj(struct msg_list, 1);
}
newmsg->buf = copy_str(str);
newmsg->prev = msg_stack;
msg_stack = newmsg;
}
msg_ptr = msg_stack;
}
vmsg(va_tail)
va_tdcl
{
char *errmsg, *fmt;
fmt = va_arg1(char *);
if (fmt) {
char buf[512];
vsprintf(buf, fmt, va_args2toN);
push_msg(buf);
}
if (msg_ptr) {
errmsg = msg_ptr->buf;
msg_ptr = msg_ptr->prev;
} else {
errmsg = "(no more messages)";
msg_ptr = msg_stack;
}
if (terminal_speed == 0) {
printf("%s\n", errmsg);
fl;
return;
}
gotoxy(0, Lines-1);
fputs(errmsg, stdout);
clrline();
any_message = 1;
gotoxy(prompt_length, prompt_line);
fl;
}
clrmsg(col)
int col;
{
BATCH_CHECK;
gotoxy(0, prompt_line + 1);
clrpage(prompt_line + 1);
if (col >= 0)
gotoxy(prompt_length + col, prompt_line);
fl;
any_message = 0;
}
/*VARARGS*/
prompt(va_alist)
va_dcl
{
register char *cp;
int stand_on;
char *fmt;
static char cur_p[FILENAME];
static char saved_p[FILENAME];
use_vararg;
BATCH_CHECK;
start_vararg;
fmt = va_arg1(char *);
if (fmt == P_VERSION) {
gotoxy(0, prompt_line + 1);
printf("Release %s ", version_id);
clrline();
any_message++;
if (prompt_line >= 0)
gotoxy(prompt_length, prompt_line);
goto out;
}
if (fmt == P_SAVE) {
strcpy(saved_p, cur_p);
goto out;
}
if (fmt == P_RESTORE)
strcpy(cur_p, saved_p);
if (prompt_line >= 0)
gotoxy(0, prompt_line);
if (fmt == P_MOVE) {
clrline();
goto out;
}
if (fmt != P_REDRAW && fmt != P_RESTORE)
vsprintf(cur_p, fmt, va_args2toN);
putchar(CR);
for (cp = cur_p, stand_on = 0, prompt_length = 0; *cp; cp++) {
if (*cp == '\1') {
if (cp[1] != '\1') {
if (STANDOUT) {
putp(stand_on ? exit_standout_mode : enter_standout_mode);
stand_on = !stand_on;
prompt_length += cookie_size;
}
continue;
}
cp++;
} else
if (*cp == '\2') {
time_t t, tick_usage();
char *timestr;
t = tick_usage();
if (show_current_time) {
timestr = ctime(&t) + 11;
timestr[5] = NUL;
printf("-- %s ", timestr);
prompt_length += 9;
}
if (unread_mail(t)) {
printf("Mail ");
prompt_length += 5;
}
continue;
}
putchar(*cp);
prompt_length ++;
}
if (stand_on) {
putp(exit_standout_mode);
prompt_length += cookie_size;
}
clrline();
if (fmt == P_RESTORE)
restore_xy();
else
curxy_c = -1;
out:
end_vararg;
}
any_key(line)
int line;
{
int was_raw, c, dmp;
BATCH_CHECK;
was_raw = is_raw;
if (!is_raw) raw();
if (line == 0)
line = -1;
else
if (line < 0)
line = Lines + line;
if (line != 10000)
so_printxy(0, line, "Hit any key to continue");
clrline();
dmp = do_macro_processing;
do_macro_processing = 0;
c = get_c();
if (c == 'q' || c == 'Q') c = K_interrupt;
do_macro_processing = dmp;
if (!was_raw) unset_raw();
return c;
}
static pg_fline, pg_width, pg_maxw, pg_line, pg_col, pg_quit;
export regexp *pg_regexp = NULL;
export int pg_new_regexp = 0;
pg_init(first_line, cols)
int first_line, cols;
{
if (pg_regexp) {
freeobj(pg_regexp);
pg_regexp = NULL;
}
pg_new_regexp = 0;
pg_fline = first_line;
pg_line = pg_fline - 1;
pg_quit = pg_col = 0;
pg_width = Columns / cols;
pg_maxw = pg_width * (cols - 1);
}
pg_scroll(n)
int n;
{
pg_line += n;
if (pg_line >= (Lines - 1)) {
pg_line = 0;
if (any_key(0) == K_interrupt)
return 1;
putchar(CR);
clrline();
}
return 0;
}
pg_next()
{
int c;
if (batch_mode) {
putchar(NL);
return 0;
}
pg_line++;
if (pg_line < Lines) {
gotoxy(pg_col, pg_line);
if (pg_line == Lines - 1 && pg_col == pg_maxw) {
c = any_key(0);
if (c == '/') {
char *expr;
putchar(CR);
putchar('/');
clrline();
expr = get_s((char *)NULL, (char *)NULL, (char *)NULL, NULL_FCT);
if (expr != NULL && *expr != NUL) {
freeobj(pg_regexp);
pg_regexp = regcomp(expr);
pg_new_regexp = 1;
}
}
gotoxy(0, pg_fline);
clrpage(pg_fline);
pg_col = 0;
pg_line = pg_fline;
if (c == K_interrupt) {
pg_quit = 1;
return -1;
}
return 1;
}
} else {
pg_line = pg_fline;
pg_col += pg_width;
gotoxy(pg_col, pg_line);
}
return 0;
}
pg_indent(pos)
int pos;
{
BATCH_CHECK;
gotoxy(pg_col + pos, pg_line);
}
pg_end()
{
int c;
if (pg_quit == 0 && pg_next() == 0)
c = any_key(0);
else
c = K_interrupt;
if (pg_regexp) {
freeobj(pg_regexp);
pg_regexp = NULL;
}
return c == K_interrupt ? -1 : 0;
}
user_delay(ticks)
int ticks;
{
BATCH_CHECK;
if (ticks <= 0 || conf_dont_sleep) {
printf(" <>");
any_key(10000);
} else {
fl;
sleep((unsigned)ticks);
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.