/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * * Terminal interface. */ #define raw __curses__raw__ #include #include #include "config.h" #include "keymap.h" #include "regexp.h" #include "nn_term.h" #ifdef RESIZING #include /* for TIOCGWINSZ */ #ifdef SYSV_RESIZING #include #include #endif /* SYSV_RESIZING */ extern int s_resized; #endif /* RESIZING */ #ifdef FAKE_INTERRUPT #include #endif #ifdef HAVE_TERMIOS_H #include #endif #ifdef USE_TERMINFO # include # ifndef VINTR # include /* some systems don't include this in curses.h */ # endif /* VINTR */ # ifndef auto_left_margin # include # endif /* !auto_left_margin */ #else #define USE_TERMCAP #endif #ifdef HAVE_TERMIO_H # ifdef USE_TERMCAP # include # endif /* USE_TERMCAP */ #else # include #endif /* SYSV curses.h clash */ #undef raw /* AIX term.h clash */ #ifdef _AIX #undef Lines #undef Columns #endif /* _AIX */ /* term.c */ #ifdef __STDC__ #ifdef APOLLO_DOMAIN_OS static int outc __APROTO((int c)); #else static int outc __APROTO((char c)); #endif /* APOLLO_DOMAIN_OS */ #else static int outc __APROTO(()); #endif /* __STDC__ */ static void set_term_speed __APROTO((register unsigned long sp)); static void raw_not_ok_error __APROTO((void)); static sig_type rd_timeout __APROTO((int n)); static int read_char_kbd __APROTO((int tmo)); static void unread_char __APROTO((int c)); static sig_type catch_winch(); void visual_on(); void xterm_mouse_on(); void xterm_mouse_off(); import int data_bits; import int batch_mode; import char *help_directory; extern void ding(); extern void clrmsg(); extern void gotoxy(); 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 char *shade_on_attr = NULL; export char *shade_off_attr = NULL; export int mouse_state; /* if xterm is in mouse state */ export int mouse_usage; /* 0 don't set mouse, 1 only if xterm, 2 set mouse */ 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 #define HAS_CAP(str) (str && *str) extern char *tgoto(); /* some systems don't have this in term.h */ #else 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! */ int auto_right_margin; /* automatic right margin */ int eat_newline_glitch; /* newline ignored at right margin */ #define putp(str) tputs(str, 1, outc) #define HAS_CAP(str) (*str) #endif /* USE_TERMCAP */ static char key_mouse_d1[64] = "\33[M "; static char key_mouse_d2[64] = "\33[M!"; static char key_mouse_d3[64] = "\33[M\""; static char key_mouse_u1[64] = "\33[M#"; /* * Compute the greatest multiple of p not greater than x. * p must be a power of 2. * x must be nonnegative, for portability to non-2's-complement hosts. */ #define DISCARD_REMAINDER(x,p) ((x) & ~((p)-1)) #ifdef __STDC__ static int #ifdef APOLLO_DOMAIN_OS outc(int c) #else outc(char c) #endif /* APOLLO_DOMAIN_OS */ #else static int outc(c) char c; #endif /* __STDC__ */ { putchar(c); return 0; /* XXX What is it supposed to return? */ } #ifdef BROKEN_TPUTS /* tputs is broken on UNISYS I've been told */ #define putpc(str, cnt) tputs(str, 0, outc) #else #define putpc(str, cnt) tputs(str, cnt, outc) #endif #undef standout 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 */ static int curxy_c = 0, curxy_l = -1, savxy_c = 0, savxy_l = -1; static int just_sent_cr = 0; /* just sent \r to avoid 'xn' term attribute */ static int *nonsp; /* number of non-space characters on line */ #ifdef TERM_DEBUG static char *term_debug = NULL; extern char *getenv(); #define curxy_nonsp (curxy_l < 0 ? -1 : nonsp[curxy_l]) #endif #ifdef FAKE_INTERRUPT extern jmp_buf fake_keyb_sig; extern int arm_fake_keyb_sig; extern char fake_keyb_siglist[]; #endif /* FAKE_INTERRUPT */ #if defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H) /* This used to be 50, but there are some rather complex bugs in the SYSV */ /* TERMIO driver... */ #define KEY_BURST 2 /* read bursts of 1 char (or timeout after 100 ms) */ #undef CBREAK #ifdef HAVE_TERMIOS_H static struct termios norm_tty, raw_tty; #else static struct termio norm_tty, raw_tty; #endif #define IntrC ((key_type) norm_tty.c_cc[VINTR]) #define EraseC ((key_type) norm_tty.c_cc[VERASE]) #define KillC ((key_type) norm_tty.c_cc[VKILL]) #ifdef HAVE_TERMIOS_H #define SuspC ((key_type) norm_tty.c_cc[VSUSP]) #else #define SuspC ((key_type) CONTROL_('Z')) /* norm_tty.c_cc[SWTCH] */ #endif #else /* V7/BSD TTY DRIVER */ static struct sgttyb norm_tty, raw_tty; static struct tchars norm_chars; #define IntrC ((key_type) norm_chars.t_intrc) #define EraseC ((key_type) norm_tty.sg_erase) #define KillC ((key_type) norm_tty.sg_kill) #ifdef TIOCGLTC static struct ltchars spec_chars; #define SuspC ((key_type) spec_chars.t_suspc) #else #define SuspC ((key_type) 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)) nn_exitmsg(1, "TERMCAP entry for %s has no '%s' capability", term_name, cap); } #endif /* USE_TERMCAP */ /* * timeout in n/10 seconds via SIGALRM */ void 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 /* MICRO_ALARM */ #endif /* HAVE_UALARM */ } static int multi_keys = 0; static struct multi_key { key_type *cur_key; key_type *keys; key_type code; } multi_key_list[MULTI_KEYS]; void 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; } void 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; tprintf("%d\t%s\t", i, key_name(multi_key_list[i].code)); for (cp = multi_key_list[i].keys; *cp; cp++) tprintf(" %s", key_name(*cp)); } pg_end(); } #ifdef RESIZING static sig_type catch_winch(n) int n; { struct winsize winsize; int i; (void) signal(SIGWINCH, catch_winch); if (ioctl(0, TIOCGWINSZ, &winsize) >= 0 && (winsize.ws_row != Lines || winsize.ws_col != Columns)) { nonsp = resizeobj(nonsp, int, winsize.ws_row); if ((int)winsize.ws_row > Lines) for (i = Lines; i < (int)winsize.ws_row; i++) nonsp[i] = winsize.ws_col; if ((int)winsize.ws_col < Columns) for (i = 0; i < (int)winsize.ws_row; i++) if (nonsp[i] > (int)winsize.ws_col) nonsp[i] = winsize.ws_col; Lines = winsize.ws_row; Columns = winsize.ws_col; curxy_l = -1; s_redraw = 1; s_resized = 1; } #ifdef FAKE_INTERRUPT if (fake_keyb_siglist[n] && arm_fake_keyb_sig) longjmp(fake_keyb_sig, 1); #endif /* FAKE_INTERRUPT */ } #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 /* NO_SIGINTERRUPT */ #endif /* SV_INTERRUPT */ #ifdef FAKE_INTERRUPT #define SV_INTERRUPT static siginterrupt (signo, on) { fake_keyb_siglist[signo] = on; } #endif /* FAKE_INTERRUPT */ static unsigned sp_table[] = { B9600, 960, #ifdef B19200 B19200, 1920, #else #ifdef EXTA EXTA, 1920, #endif /* EXTA */ #endif /* B19200 */ #ifdef B38400 B38400, 3840, #else #ifdef EXTB EXTB, 3840, #endif /* EXTB */ #endif /* B38400 */ B1200, 120, B2400, 240, B4800, 480, B300, 30, 0, 0 }; static void 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 void raw_not_ok_error() { if (batch_mode) return; nn_exitmsg(1, "Not prepared for terminal i/o"); /*NOTREACHED*/ } #define RAW_CHECK if (terminal_speed == 0) {raw_not_ok_error(); return 0;} #define RAW_CHECK_V if (terminal_speed == 0) {raw_not_ok_error(); return;} #define BATCH_CHECK if (terminal_speed == 0) return 0 #define BATCH_CHECK_V if (terminal_speed == 0) return void init_term(full) int full; { #ifdef USE_TERMCAP char tbuf[1024]; #endif int i; #ifdef TERM_DEBUG term_debug = getenv("TERM_DEBUG"); if (term_debug) fprintf (stderr, "init_term(%d)\n", full); #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) nn_exitmsg(1, "No TERM variable in environment"); else term_name = "unknown"; } if (!full) return; #ifdef HAVE_TERMIO_H ioctl(0, TCGETA, &norm_tty); #else #ifdef HAVE_TERMIOS_H tcgetattr(0, &norm_tty); #else ioctl(0, TIOCGETP, &norm_tty); #endif /* HAVE_TERMIOS_H */ #endif /* HAVE_TERMIO_H */ #ifdef USE_TERMINFO setupterm((char *)NULL, 1, (int *)NULL); Columns = columns; Lines = lines; 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)) { char * result = tgoto(cursor_address, 0, 0); /* reikel 1/2/97 */ if (result == NULL) nn_exitmsg(1, "This terminal lacks features necessary for nn."); else cursor_home = copy_str(result); } #else if (tgetent(tbuf, term_name) <= 0) nn_exitmsg(1, "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)) { char * result = tgoto(cursor_address, 0, 0); /* reikel 1/2/97 */ if (result == NULL) nn_exitmsg(1, "This terminal lacks features necessary for nn."); else strcpy(cursor_home, result); } get_cap("cl", clear_screen); opt_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); magic_cookie_glitch = tgetnum("sg"); ceol_standout_glitch = tgetflag("xs"); auto_right_margin = tgetflag("am"); eat_newline_glitch = tgetflag("xn"); 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 */ /* Stop NN from blowing up if on a *really* dumb terminal, like "dumb" */ if (Lines < 1) Lines = 24; if (Columns < 1) Columns = 80; nonsp = newobj(int, Lines); for (i = 0; i < Lines; i++) nonsp[i] = Columns; STANDOUT = HAS_CAP(enter_standout_mode); cookie_size = magic_cookie_glitch; 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_H 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[VMIN] = KEY_BURST; raw_tty.c_cc[VTIME] = ((int)(raw_tty.c_cflag & CBAUD) > B1200) ? 1 : 2; set_term_speed((unsigned long)(raw_tty.c_cflag & CBAUD)); #else #ifdef HAVE_TERMIOS_H cfmakeraw(&raw_tty); /* read a maximum of 10 characters in one burst; timeout in 1-200 ms */ raw_tty.c_cc[VMIN] = KEY_BURST; #if 0 raw_tty.c_cc[VTIME] = (cfgetispeed(&raw_tty) > B1200) ? 1 : 2; #else raw_tty.c_cc[VTIME] = 1; #endif /* 0 */ set_term_speed((unsigned long)cfgetospeed(&raw_tty)); #ifdef SV_INTERRUPT siginterrupt(SIGTSTP, 1); siginterrupt(SIGALRM, 1); #endif /* SV_INTERRUPT */ #else ioctl(0, TIOCGETC, &norm_chars); #ifdef TIOCGLTC ioctl(0, TIOCGLTC, &spec_chars); #endif /* TIOCGLTC */ 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 /* SV_INTERRUPT */ raw_tty.sg_flags |= CBREAK; #else raw_tty.sg_flags |= RAW; #endif /* CBREAK */ #ifdef SV_INTERRUPT siginterrupt(SIGALRM, 1); /* make read from tty interruptable */ #endif /* SV_INTERRUPT */ #endif /* HAVE_TERMIOS_H */ #endif /* HAVE_TERMIO_H */ erase_key = EraseC; kill_key = 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); enter_multi_key(K_m_d1, (key_type *)key_mouse_d1); enter_multi_key(K_m_d2, (key_type *)key_mouse_d2); enter_multi_key(K_m_d3, (key_type *)key_mouse_d3); enter_multi_key(K_m_u1, (key_type *)key_mouse_u1); 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] != '['); } if ((mouse_usage == 2) || ((mouse_usage == 1) && !strncmp("xterm",term_name,5))) { mouse_state = 1; flow_control = 0; } else { mouse_state = 0; } visual_on(); } void home() { BATCH_CHECK_V; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "home\n"); #endif putp(cursor_home); curxy_c = curxy_l = 0; } void save_xy() { savxy_c = curxy_c; savxy_l = curxy_l; } void restore_xy() { if (savxy_l < 0) return; gotoxy(savxy_c, savxy_l); fl; } void gotoxy(c, l) int c, l; { BATCH_CHECK_V; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "gotoxy %d %d -> %d %d\n", curxy_c, curxy_l, c, l); #endif if (Columns <= (unsigned)c || Lines <= (unsigned)l) return; if (!(c | l)) putp(cursor_home); else putp(tgoto(cursor_address, c, l)); curxy_c = c; curxy_l = l; } void clrdisp() { int i; BATCH_CHECK_V; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "clrdisp\n"); #endif putpc(clear_screen, Lines); curxy_c = curxy_l = 0; savxy_l = -1; for (i = 0; i < Lines; i++) nonsp[i] = 0; msg_ptr = msg_stack; } void clrline_noflush(); void clrline() { BATCH_CHECK_V; /* If we moved the cursor left to avoid weird effects, don't clear. */ if (just_sent_cr) return; clrline_noflush(); fl; } void clrline_noflush() { int oldxy_c, oldxy_l, spcnt; BATCH_CHECK_V; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "clrline %d %d [%d]",curxy_c, curxy_l, curxy_nonsp); #endif if (curxy_l < 0) return; /* remainder of line already blank? */ if (curxy_c >= nonsp[curxy_l]) { #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, " ignored\n"); #endif return; } if (HAS_CAP(clr_eol)) { #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "\n"); #endif putp(clr_eol); } else { oldxy_c = curxy_c; oldxy_l = curxy_l; spcnt = nonsp[curxy_l] - curxy_c; /* guard against scroll */ if (auto_right_margin && curxy_l == Lines-1 && curxy_c+spcnt == Columns) spcnt--; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr," %d\n", spcnt); #endif /* TERM_DEBUG */ #ifdef TERM_DEBUG if (term_debug && *term_debug) { while (spcnt--) tputc(*term_debug); spcnt = 0; } #endif /* clear out line */ while (spcnt--) tputc(SP); if (curxy_c != oldxy_c || curxy_l != oldxy_l) gotoxy(oldxy_c, oldxy_l); } nonsp[curxy_l] = curxy_c; } void clrpage() { int oldxy_c, oldxy_l, i; BATCH_CHECK_V; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "clrpage %d %d [%d]\n", curxy_c, curxy_l, curxy_nonsp); #endif if (curxy_l < 0) return; oldxy_c = curxy_c; oldxy_l = curxy_l; /* code below only handles curxy_c == 0 */ if (curxy_c != 0) { clrline(); if (curxy_l < Lines-1) gotoxy(0, curxy_l+1); } /* clear to end of screen */ if (curxy_c == 0) { if (HAS_CAP(clr_eos)) { putpc(clr_eos, Lines - curxy_l); for (i = curxy_l; i < Lines; i++) nonsp[i] = 0; } else if (curxy_l == 0) { putpc(clear_screen, Lines); for (i = 0; i < Lines; i++) nonsp[i] = 0; } else { for (i = curxy_l; i < Lines; i++) { if (nonsp[i] != 0) { gotoxy(0, i); clrline_noflush(); } } } } if (curxy_c != oldxy_c || curxy_l != oldxy_l) gotoxy(oldxy_c, oldxy_l); fl; msg_ptr = msg_stack; } static char buf[512]; /*VARARGS*/ void tprintf(va_alist) va_dcl { use_vararg; start_vararg; tvprintf(va_args1toN); end_vararg; } void tvprintf(va_tail) va_tdcl { char *fmt, *str; fmt = va_arg1(char *); vsprintf(buf, fmt, va_args2toN); for (str = buf; *str; str++) tputc(*str); } void tputc(c) int c; { int i; #ifdef TERM_DEBUG if (term_debug) { fprintf (stderr, "tputc %d %d [%d] ", curxy_c, curxy_l, curxy_nonsp); if (c < ' ' || c > '~') ; else fprintf(stderr, "'%c'", c); } #endif just_sent_cr = 0; putchar(c); switch (c) { case '\n': curxy_c = 0; if (curxy_l >= 0) curxy_l++; break; case '\r': curxy_c = 0; break; case '\t': curxy_c = DISCARD_REMAINDER(curxy_c + 8, 8); break; case '\b': curxy_c--; break; case ' ': curxy_c++; if (curxy_l >= 0 && nonsp[curxy_l] == curxy_c) nonsp[curxy_l]--; break; default: curxy_c++; if (curxy_l >= 0 && nonsp[curxy_l] < curxy_c) nonsp[curxy_l] = curxy_c; break; } #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, " -> %d %d [%d]",curxy_c,curxy_l,curxy_nonsp); #endif /* account for right margin */ if (curxy_c == Columns) { #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, " margin"); #endif if (auto_right_margin) { if (eat_newline_glitch) { putchar(CR); just_sent_cr = 1; curxy_c = 0; } else { curxy_c = 0; if (curxy_l >= 0) curxy_l++; } } else curxy_c--; } /* account for vertical scroll */ if (curxy_l == Lines) { #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, " scroll"); #endif for (i = 1; i < Lines; i++) nonsp[i-1] = nonsp[i]; nonsp[--curxy_l] = 0; } #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "\n"); #endif } static char so_buf[512], *so_p; static int so_c, so_l, so_b, so_active = 0; int so_gotoxy(c, l, blank) int c, l, blank; { if (!STANDOUT && c >= 0) { if (c>= 0 && 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*/ void so_printf(va_alist) va_dcl { use_vararg; start_vararg; so_vprintf(va_args1toN); end_vararg; } void so_vprintf(va_tail) va_tdcl { char *fmt; if (!so_active) { if (ceol_standout_glitch) highlight(0); /* xxx why? */ tvprintf(va_args1toN); return; } fmt = va_arg1(char *); vsprintf(so_p, fmt, va_args2toN); while (*so_p) so_p++; } void 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); standout(0); } gotoxy(so_c, so_l); } if ((so_b & 1) && (!STANDOUT || !cookie_size)) tputc(SP); if (STANDOUT) { if (ceol_standout_glitch) clrline(); standout(1); } tprintf("%s", so_buf); if (STANDOUT) standout(0); if ((so_b & 2) && (!STANDOUT || !cookie_size)) tputc(SP); so_active = 0; } /*VARARGS*/ void so_printxy(va_alist) va_dcl { int c, l; use_vararg; start_vararg; c = va_arg1(int); l = va_arg2(int); so_gotoxy(c, l, 0); so_vprintf(va_args3toN); so_end(); end_vararg; } int underline(on) int on; { if (cookie_size) return 0; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "underline %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on); #endif if (! HAS_CAP(enter_underline_mode)) return 0; putp(on ? enter_underline_mode : exit_underline_mode); return 1; } int highlight(on) int on; { if (cookie_size) return 0; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "highlight %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on); #endif if (! HAS_CAP(enter_standout_mode)) return 0; putp(on ? enter_standout_mode : exit_standout_mode); return 1; } int shadeline(on) int on; { if (cookie_size) return 0; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "shadeline %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on); #endif if (shade_on_attr && shade_off_attr) { putp(on ? shade_on_attr : shade_off_attr); return 1; } else return underline(1); } int standout(on) int on; { #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "standout %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on); #endif if (! HAS_CAP(enter_standout_mode)) return 0; putp(on ? enter_standout_mode : exit_standout_mode); curxy_c += cookie_size; if (curxy_l >= 0 && curxy_c > nonsp[curxy_l]) nonsp[curxy_l] = curxy_c; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, " -> %d %d [%d]\n", curxy_c, curxy_l, curxy_nonsp); #endif return 1; } static int is_visual = 0; static int is_raw = 0; #ifdef HAVE_TERMIO_H #define RAW_MODE_ON ioctl(0, TCSETAW, &raw_tty) #define RAW_MODE_OFF ioctl(0, TCSETAW, &norm_tty) #else #ifdef HAVE_TERMIOS_H #define RAW_MODE_ON tcsetattr(0, TCSADRAIN, &raw_tty) #define RAW_MODE_OFF tcsetattr(0, TCSADRAIN, &norm_tty) #else #define RAW_MODE_ON ioctl(0, TIOCSETP, &raw_tty) #define RAW_MODE_OFF ioctl(0, TIOCSETP, &norm_tty) #endif /* HAVE_TERMIOS_H */ #endif /* HAVE_TERMIO_H */ void xterm_mouse_on() { putp("\33[?1000h"); } void xterm_mouse_off() { putp("\33[?1000l"); } void visual_on() { BATCH_CHECK_V; if (terminal_speed == 0) return; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "visual_on\n"); #endif if (!is_visual) { if (HAS_CAP(enter_ca_mode)) putp(enter_ca_mode); if (appl_keypad_mode) putp(keypad_xmit); if (mouse_state) xterm_mouse_on(); fl; } is_visual = 1; } int visual_off() { int was_raw = is_raw; if (terminal_speed == 0) return 0; #ifdef TERM_DEBUG if (term_debug) fprintf(stderr, "visual_off\n"); #endif if (is_visual) { if (appl_keypad_mode) putp(keypad_local); if (HAS_CAP(exit_ca_mode)) putp(exit_ca_mode); if (mouse_state) xterm_mouse_off(); fl; } is_visual = 0; is_raw = 1; unset_raw(); return was_raw; } #ifdef CBREAK void nn_raw() { RAW_CHECK_V; if (is_raw == 1) return; is_raw = 1; RAW_MODE_ON; } int no_raw() { return 0; } int 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; #undef raw void nn_raw() { RAW_CHECK_V; if (!flow_control) { if (!must_set_raw) return; must_set_raw = 0; } if (is_raw) return; RAW_MODE_ON; is_raw++; } int no_raw() { if (!flow_control) return 0; if (!is_raw) return 0; RAW_CHECK; RAW_MODE_OFF; is_raw = 0; return 1; } int 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 /* KEY_BURST */ #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 /* FAKE_INTERRUPT */ static sig_type rd_timeout(n) int n; { rd_alarm = 1; #ifdef FAKE_INTERRUPT longjmp(fake_alarm_sig, 1); #endif /* FAKE_INTERRUPT */ } #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 /* FAKE_INTERRUPT */ 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 void unread_char(c) int c; { if (rd_ptr == rd_buffer) return; rd_count++; *--rd_ptr = c; } void flush_input() { #ifndef HAVE_TERMIO_H #ifdef FREAD int arg; #endif #endif BATCH_CHECK_V; #ifdef HAVE_TERMIO_H ioctl(0, TCFLSH, 0); #else #ifdef HAVE_TERMIOS_H ioctl(0, TCIFLUSH); #else #ifdef FREAD arg = FREAD; ioctl(0, TIOCFLUSH, &arg); #else ioctl(0, TIOCFLUSH, 0); #endif /* FREAD */ #endif /* HAVE_TERMIOS_H */ #endif /* HAVE_TERMIO_H */ rd_count = 0; } int enable_stop = 1; static int do_macro_processing = 1; export int mouse_x,mouse_y = -1; static int mouse_last = 0; int get_c() { key_type c, first_key; int key_cnt, mc, n; register struct multi_key *mk, *multi_match; register int i; multi_match = NULL; first_key = 0; mouse_y = -1; 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 /* RESIZING */ if (batch_mode) nn_exitmsg(1, "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 /* FAKE_INTERRUPT */ multi: switch (n = read_char_kbd(key_cnt)) { case RD_INTERRUPT: #ifdef CBREAK if (s_redraw) goto redraw; #endif /* CBREAK */ #ifdef RESIZING if (s_resized) goto redraw; #endif /* RESIZING */ 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; /* xterm mouse specific code, translate cursor position into static variables, synthesize key up events */ if ((c >= K_m_d1) && (c <= K_m_u1)) { mouse_x = read_char_kbd(key_cnt) - '!'; mouse_y = read_char_kbd(key_cnt) - '!'; if (c == K_m_u1) { if (mouse_last == K_m_d2) { c = K_m_u2; } else if (mouse_last == K_m_d3) { c = K_m_u3; } } mouse_last = c; } 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 /* FAKE_INTERRUPT */ 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 /* CBREAK */ return (int)c; intr: #ifdef FAKE_INTERRUPT arm_fake_keyb_sig = 0; #endif /* FAKE_INTERRUPT */ rd_count = 0; return K_interrupt; redraw: #ifdef RESIZING s_resized = 0; #endif /* RESIZING */ 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 lbuf[GET_S_BUFFER]; register char *cp; register int i, c, lastc; char *ret_val = (char *)lbuf; int comp_used, comp_len; int ostop, max, did_help; int hit_count; switch (m_gets((char *)lbuf)) { case 0: break; case 1: return (char *)lbuf; 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; tputc(c); lbuf[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) { tputc(c); lbuf[i] = c; i++; } fl; dflt = NULL; continue; } if ((cp = break_chars)) { /* Stupid ^@$# GCC!!! */ while (*cp) if (*cp++ == c) { lbuf[0] = c; lbuf[1] = NUL; goto out; } } } if (completion != NULL_FCT) { if (comp_used && c == erase_key) { if (comp_len) { i -= comp_len; while (--comp_len >= 0) tputc(BS); clrline(); } if (!CALL(completion)(lbuf, -(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)) { lbuf[i] = NUL; if ((comp_used = CALL(completion)(lbuf, 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) tputc(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) tputc(BS); clrline(); /*FALLTHRU*/ case 1: /* completion */ comp_len = i; while ((c = lbuf[i])) { if (i == max) break; tputc(c); i++; } fl; comp_len = i - comp_len; continue; } } if (comp_used) { if (!CALL(completion)(lbuf, -(i+1)) && did_help) clrmsg(i); did_help = 0; comp_len = comp_used = 0; } } no_completion: if (c == CR || c == NL) { lbuf[i] = NUL; break; } if (c == erase_key) { if (i <= 0) continue; i--; tputc(BS); tputc(' '); tputc(BS); fl; continue; } if (c == delword_key) { if (i <= 0) continue; lbuf[i-1] = 'X'; while (i > 0 && isalnum(lbuf[i-1])) { tputc(BS); i--; } clrline(); continue; } if (c == kill_key) { while (i > 0) { tputc(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 && lbuf[i-1] == '/' && (c == '/' || c == '+')) { if (c != '/' || !guard_double_slash || (i > 1 && lbuf[i-2] == '/')) { if (completion == file_completion) { while (i > 0) { tputc(BS); i--; } clrline(); } } } tputc(c); fl; lbuf[i] = c; i++; } out: enable_stop = ostop; do_macro_processing = 1; return ret_val; } export int list_offset = 0; int 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(); return 1; } str += list_offset; for (;;) { cols -= strlen(str); if (cols >= 0) { tprintf("%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--; } } int 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) { nn_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) { tprintf(" y=YES n=NO"); fl; prompt_length += 11; help = 0; } } if (in_macro) { if (c < 0) m_break(); do_macro_processing = 1; } return c; } void ding() { BATCH_CHECK_V; putp(bell_str); fl; } void display_file(name, modes) char *name; int modes; { FILE *f; register c, stand_on; int linecnt, headln_cnt, hdline, no_conf; char headline[128]; 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) tprintf("\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 /* HAVE_JOBCONTROL */ no_conf = 0; if (c == '\1') { if (STANDOUT) { putp(stand_on ? exit_standout_mode : enter_standout_mode); curxy_c += cookie_size; 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') { tprintf("%s", version_id); continue; } if (headln_cnt >= 0) headline[headln_cnt++] = c; if (hdline) { tprintf("%s\r", headline); hdline = 0; linecnt--; } tputc(c); if (c == NL) { tputc(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); curxy_c += cookie_size; } fclose(f); } prompt_line = Lines-1; /* move prompt to last line */ if (!no_conf && (modes & CONFIRMATION)) any_key(prompt_line); } /*VARARGS*/ void nn_exitmsg(va_alist) va_dcl { char *fmt; int n; use_vararg; if (terminal_speed != 0) { clrdisp(); visual_off(); } start_vararg; n = va_arg1(int); fmt = va_arg2(char *); vprintf(fmt, va_args3toN); putchar(NL); end_vararg; nn_exit(n); /*NOTREACHED*/ } /*VARARGS*/ void msg(va_alist) va_dcl { use_vararg; start_vararg; vmsg(va_args1toN); end_vararg; } void 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; } void vmsg(va_tail) va_tdcl { char *errmsg, *fmt; fmt = va_arg1(char *); if (fmt) { char lbuf[512]; vsprintf(lbuf, fmt, va_args2toN); push_msg(lbuf); } 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) { tprintf("%s\n", errmsg); fl; return; } gotoxy(0, Lines-1); tprintf("%s", errmsg); clrline(); any_message = 1; if (prompt_line != Lines-1) gotoxy(prompt_length, prompt_line); fl; } void clrmsg(col) int col; { BATCH_CHECK_V; gotoxy(0, prompt_line + 1); clrpage(); if (col >= 0) gotoxy(prompt_length + col, prompt_line); fl; any_message = 0; } /*VARARGS*/ void 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_V; start_vararg; fmt = va_arg1(char *); if (fmt == P_VERSION) { gotoxy(0, prompt_line + 1); tprintf("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); tputc(CR); for (cp = cur_p, stand_on = 0, prompt_length = 0; *cp; cp++) { if (*cp == '\1') { if (cp[1] != '\1') { if (STANDOUT) { stand_on = !stand_on; standout(stand_on); prompt_length += cookie_size; } continue; } cp++; } else if (*cp == '\2') { time_t t; char *timestr; t = tick_usage(); if (show_current_time) { timestr = ctime(&t) + 11; timestr[5] = NUL; tprintf("-- %s ", timestr); prompt_length += 9; } if (unread_mail(t)) { tprintf("Mail "); prompt_length += 5; } continue; } tputc(*cp); prompt_length ++; } if (stand_on) { standout(0); prompt_length += cookie_size; } clrline(); if (fmt == P_RESTORE) restore_xy(); #if notdef else curxy_c = -1; #endif out: end_vararg; } int any_key(line) int line; { int was_raw, c, dmp; BATCH_CHECK; was_raw = is_raw; if (!is_raw) nn_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; void 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); } int pg_scroll(n) int n; { pg_line += n; if (pg_line >= (Lines - 1)) { pg_line = 0; if (any_key(0) == K_interrupt) return 1; tputc(CR); clrline(); } return 0; } int 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; tputc(CR); tputc('/'); 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_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; } void pg_indent(pos) int pos; { BATCH_CHECK_V; gotoxy(pg_col + pos, pg_line); } int 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; } void user_delay(ticks) int ticks; { BATCH_CHECK_V; if (ticks <= 0 || conf_dont_sleep) { tprintf(" <>"); any_key(10000); } else { fl; sleep((unsigned)ticks); } }