1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  *	Terminal interface.
6  */
7 #define raw __curses__raw__
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include "config.h"
19 #include "global.h"
20 #include "execute.h"
21 #include "folder.h"
22 #include "keymap.h"
23 #include "macro.h"
24 #include "nn.h"
25 #include "regexp.h"
26 #include "nn_term.h"
27 
28 #if !defined(__FreeBSD__) && !(__NetBSD__) && !defined(NeXT)
29 #include <stropts.h>
30 #else
31 #include <sys/ioctl.h>
32 #endif
33 
34 #ifdef RESIZING
35 #include <sys/ioctl.h>		/* for TIOCGWINSZ */
36 
37 extern int      s_resized;
38 #endif				/* RESIZING */
39 
40 #ifdef FAKE_INTERRUPT
41 #include <setjmp.h>
42 #endif
43 
44 #ifdef HAVE_TERMIOS_H
45 #include <termios.h>
46 #endif
47 
48 #ifdef USE_TERMINFO
49 #include <curses.h>
50 
51 #ifndef VINTR
52 #include <termio.h>		/* some systems don't include this in
53 				 * curses.h */
54 #endif				/* VINTR */
55 
56 #ifndef auto_left_margin
57 #include <term.h>
58 #endif				/* !auto_left_margin */
59 
60 #else
61 #define USE_TERMCAP
62 
63 #if !defined(SUNOS4) && !defined(NeXT)
64 #include <termcap.h>
65 #endif				/* SUNOS4 */
66 
67 #endif
68 
69 #ifdef HAVE_TERMIO_H
70 
71 #ifdef USE_TERMCAP
72 #include <termio.h>
73 #endif				/* USE_TERMCAP */
74 
75 #else
76 
77 #ifndef __FreeBSD__
78 #include <sgtty.h>
79 #endif				/* __FreeBSD__ */
80 
81 #endif
82 
83 #ifdef SYSV_RESIZING
84 #include <sys/stream.h>
85 #include <sys/ptem.h>
86 #endif				/* SYSV_RESIZING */
87 
88 /* SYSV curses.h clash */
89 #undef raw
90 
91 /* AIX term.h clash */
92 
93 #ifdef _AIX
94 #undef Lines
95 #undef Columns
96 #endif				/* _AIX */
97 
98 #ifdef __osf__
99 void            cfmakeraw(struct termios * t);
100 #endif
101 
102 /* term.c */
103 
104 #ifdef HAVE_TERMIOS_H
105 static int      outc(int c);
106 #else
107 static int      outc(char c);
108 #endif
109 
110 static void     set_term_speed(register unsigned long sp);
111 static void     raw_not_ok_error(void);
112 static sig_type rd_timeout(int n);
113 static int      read_char_kbd(int tmo);
114 static void     unread_char(int c);
115 static int      nnstandout(int);
116 
117 static sig_type catch_winch(int n);
118 
119 void            visual_on(void);
120 void            xterm_mouse_on(void);
121 void            xterm_mouse_off(void);
122 
123 extern int      data_bits;
124 extern int      batch_mode;
125 extern char    *help_directory;
126 
127 extern void     ding();
128 extern void     clrmsg();
129 extern void     gotoxy();
130 
131 struct msg_list {
132     char           *buf;
133     struct msg_list *prev;
134 };
135 
136 static struct msg_list *msg_stack = NULL, *msg_ptr = NULL;
137 
138 int             message_history = 15;
139 char           *term_name = NULL;
140 int             show_current_time = 1;
141 int             conf_dont_sleep = 0;
142 int             prompt_length;
143 int             terminal_speed = 0;
144 int             slow_speed = 1200;
145 int             any_message = 0;
146 int             flow_control = 1;
147 int             use_visible_bell = 1;	/* if supported by terminal */
148 int             ignore_xon_xoff = 1;
149 int             multi_key_guard_time = 2;	/* tenths of a second */
150 int             guard_double_slash = 0;	/* need /// or //+ to clear line */
151 char           *shade_on_attr = NULL;
152 char           *shade_off_attr = NULL;
153 int             mouse_state;	/* if xterm is in mouse state */
154 int             mouse_usage;	/* 0 don't set mouse, 1 only if xterm, 2 set
155 				 * mouse */
156 
157 key_type        help_key = '?';
158 key_type        comp1_key = SP;
159 key_type        comp2_key = TAB;
160 key_type        erase_key, kill_key;
161 key_type        delword_key = CONTROL_('W');
162 
163 static char     bell_str[256] = "\007";
164 static int      appl_keypad_mode = 0;
165 
166 #ifdef USE_TERMINFO
167 #define HAS_CAP(str) (str && *str)
168 
169 extern char    *tgoto();	/* some systems don't have this in term.h */
170 
171 #else
172 
173 char           *tgoto();
174 char            PC;
175 char           *BC, *UP;
176 char            nnspeed;
177 
178 static char     XBC[64], XUP[64];
179 static char     enter_ca_mode[64], exit_ca_mode[64];
180 static char     cursor_home[64];
181 static char     cursor_address[128];
182 static char     clear_screen[64];
183 static char     clr_eol[64];
184 static char     clr_eos[64];
185 static char     enter_standout_mode[64], exit_standout_mode[64];
186 static char     enter_underline_mode[64], exit_underline_mode[64];
187 static char     key_down[64], key_up[64], key_right[64], key_left[64];
188 static char     keypad_local[64], keypad_xmit[64];
189 
190 int             magic_cookie_glitch;	/* magic cookie size */
191 int             ceol_standout_glitch;	/* hp brain damage! */
192 int             auto_right_margin;	/* automatic right margin */
193 int             eat_newline_glitch;	/* newline ignored at right margin */
194 
195 #define putp(str)	tputs(str, 1, outc)
196 
197 #define HAS_CAP(str)	(*str)
198 #endif				/* USE_TERMCAP */
199 
200 static char     key_mouse_d1[64] = "\33[M ";
201 static char     key_mouse_d2[64] = "\33[M!";
202 static char     key_mouse_d3[64] = "\33[M\"";
203 static char     key_mouse_u1[64] = "\33[M#";
204 
205 /*
206  * Compute the greatest multiple of p not greater than x.
207  * p must be a power of 2.
208  * x must be nonnegative, for portability to non-2's-complement hosts.
209  */
210 #define DISCARD_REMAINDER(x,p) ((x) & ~((p)-1))
211 
212 #ifdef HAVE_TERMIOS_H
213 static int
outc(int c)214 outc(int c)
215 #else
216 static int
217 outc(char c)
218 #endif
219 {
220     putchar(c);
221     return 0;			/* XXX What is it supposed to return? */
222 }
223 
224 #define putpc(str, cnt) tputs(str, cnt, outc)
225 
226 int             Lines, Columns;	/* screen size */
227 int             Name_Length;	/* length of displayed name */
228 int             cookie_size;	/* size of magic cookie */
229 static int      two_cookies;	/* space needed to enter&exit standout mode */
230 int             STANDOUT;	/* terminal got standout mode */
231 
232 static int      curxy_c = 0, curxy_l = -1, savxy_c = 0, savxy_l = -1;
233 static int      just_sent_cr = 0;	/* just sent \r to avoid 'xn' term
234 					 * attribute */
235 
236 static int     *nonsp;		/* number of non-space characters on line */
237 
238 #ifdef TERM_DEBUG
239 static char    *term_debug = NULL;
240 extern char    *getenv();
241 #define curxy_nonsp	(curxy_l < 0 ? -1 : nonsp[curxy_l])
242 #endif
243 
244 #ifdef FAKE_INTERRUPT
245 extern jmp_buf  fake_keyb_sig;
246 extern int      arm_fake_keyb_sig;
247 extern char     fake_keyb_siglist[];
248 #endif				/* FAKE_INTERRUPT */
249 
250 #if defined(HAVE_TERMIO_H) || defined(HAVE_TERMIOS_H)
251 /* This used to be 50, but there are some rather complex bugs in the SYSV */
252 /* TERMIO driver... */
253 #define KEY_BURST 2		/* read bursts of 1 char (or timeout after
254 				 * 100 ms) */
255 
256 #undef CBREAK
257 
258 #ifdef HAVE_TERMIOS_H
259 static struct termios norm_tty, raw_tty;
260 #else
261 static struct termio norm_tty, raw_tty;
262 #endif
263 
264 #define	IntrC	((key_type) norm_tty.c_cc[VINTR])
265 #define	EraseC	((key_type) norm_tty.c_cc[VERASE])
266 #define KillC	((key_type) norm_tty.c_cc[VKILL])
267 
268 #ifdef HAVE_TERMIOS_H
269 #define SuspC	((key_type) norm_tty.c_cc[VSUSP])
270 #else
271 #define SuspC	((key_type) CONTROL_('Z'))	/* norm_tty.c_cc[SWTCH] */
272 #endif
273 
274 #else				/* V7/BSD TTY DRIVER */
275 
276 static struct sgttyb norm_tty, raw_tty;
277 static struct tchars norm_chars;
278 
279 #define	IntrC	((key_type) norm_chars.t_intrc)
280 #define	EraseC	((key_type) norm_tty.sg_erase)
281 #define KillC	((key_type) norm_tty.sg_kill)
282 
283 #ifdef TIOCGLTC
284 static struct ltchars spec_chars;
285 #define SuspC	((key_type) spec_chars.t_suspc)
286 #else
287 #define	SuspC	((key_type) CONTROL_('Z'))
288 #endif
289 
290 #endif
291 
292 #ifdef USE_TERMCAP
293 static int
opt_cap(char * cap,char * buf)294 opt_cap(char *cap, char *buf)
295 {
296     char           *tgetstr();
297 
298     *buf = NUL;
299     return tgetstr(cap, &buf) != NULL;
300 }
301 
302 static void
get_cap(char * cap,char * buf)303 get_cap(char *cap, char *buf)
304 {
305     if (!opt_cap(cap, buf))
306 	nn_exitmsg(1, "TERMCAP entry for %s has no '%s' capability",
307 		   term_name, cap);
308 }
309 
310 #endif				/* USE_TERMCAP */
311 
312 /*
313  * timeout in n/10 seconds via SIGALRM
314  */
315 
316 static void
micro_alarm(int n)317 micro_alarm(int n)
318 {
319 
320 #ifdef HAVE_UALARM
321     ualarm(n <= 1 ? 100000 : n * 100000, 0);	/* 4.3 BSD ualarm() */
322 #else
323 
324 #ifdef MICRO_ALARM
325     if (n <= 0)
326 	n = 1;
327     MICRO_ALARM(n);		/* System specific timeout */
328 #else
329     alarm(n <= 10 ? 1 : (n + 9) / 10);	/* Standard alarm() call */
330 #endif				/* MICRO_ALARM */
331 
332 #endif				/* HAVE_UALARM */
333 }
334 
335 static int      multi_keys = 0;
336 
337 static struct multi_key {
338     key_type       *cur_key;
339     key_type       *keys;
340     key_type        code;
341 }               multi_key_list[MULTI_KEYS];
342 
343 void
enter_multi_key(int code,key_type * keys)344 enter_multi_key(int code, key_type * keys)
345 {
346     register int    i;
347 
348     if (strlen((char *) keys) == 1)
349 	/* will ignore arrow keys overlaying these keys */
350 	if (*keys == NL || *keys == CR ||
351 	    *keys == erase_key || *keys == kill_key ||
352 	    *keys == IntrC)
353 	    return;
354 
355     /* lookup code to see if it is already defined... */
356     for (i = 0; i < multi_keys; i++)
357 	if (multi_key_list[i].code == (key_type) code)
358 	    goto replace_key;
359 
360     i = multi_keys++;
361 
362     /* now i points to matching or empty slot */
363     if (i >= MULTI_KEYS) {
364 	/* should never happen */
365 	log_entry('E', "too many multi keys");
366 	return;
367     }
368 replace_key:
369 
370     multi_key_list[i].keys = keys;
371     multi_key_list[i].code = code;
372 }
373 
374 void
dump_multi_keys(void)375 dump_multi_keys(void)
376 {
377     register int    i;
378     register key_type *cp;
379 
380     clrdisp();
381     pg_init(0, 1);
382 
383     for (i = 0; i < multi_keys; i++) {
384 	if (pg_next() < 0)
385 	    break;
386 	tprintf("%d\t%s\t", i, key_name(multi_key_list[i].code));
387 	for (cp = multi_key_list[i].keys; *cp; cp++)
388 	    tprintf(" %s", key_name(*cp));
389     }
390 
391     pg_end();
392 }
393 
394 
395 #ifdef RESIZING
396 static          sig_type
catch_winch(int n)397 catch_winch(int n)
398 {
399     struct winsize  winsize;
400     int             i;
401 
402     signal(SIGWINCH, catch_winch);
403     if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
404 	&& (winsize.ws_row != Lines || winsize.ws_col != Columns)) {
405 	nonsp = resizeobj(nonsp, int, winsize.ws_row);
406 	if ((int) winsize.ws_row > Lines)
407 	    for (i = Lines; i < (int) winsize.ws_row; i++)
408 		nonsp[i] = winsize.ws_col;
409 	if ((int) winsize.ws_col < Columns)
410 	    for (i = 0; i < (int) winsize.ws_row; i++)
411 		if (nonsp[i] > (int) winsize.ws_col)
412 		    nonsp[i] = winsize.ws_col;
413 	Lines = winsize.ws_row;
414 	Columns = winsize.ws_col;
415 	Name_Length = Columns / 5;
416 	if (Name_Length < NAME_LENGTH)
417 	    Name_Length = NAME_LENGTH;
418 	curxy_l = -1;
419 	s_redraw = 1;
420 	s_resized = 1;
421     }
422 
423 #ifdef FAKE_INTERRUPT
424     if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
425 	longjmp(fake_keyb_sig, 1);
426 #endif				/* FAKE_INTERRUPT */
427 }
428 
429 #endif				/* RESIZING */
430 
431 #ifdef SV_INTERRUPT
432 
433 #ifdef NO_SIGINTERRUPT
434 static int
siginterrupt(signo,on)435 siginterrupt(signo, on)
436 {
437     struct sigvec   sv;
438     sv.sv_handler = signal(signo, SIG_DFL);
439     sv.sv_mask = 0;
440     sv.sv_flags = on ? SV_INTERRUPT : 0;
441     sigvec(signo, &sv, 0);
442 }
443 
444 #endif				/* NO_SIGINTERRUPT */
445 
446 #endif				/* SV_INTERRUPT */
447 
448 #ifdef FAKE_INTERRUPT
449 #define SV_INTERRUPT
450 static int
siginterrupt(signo,on)451 siginterrupt(signo, on)
452 {
453     fake_keyb_siglist[signo] = on;
454 }
455 
456 #endif				/* FAKE_INTERRUPT */
457 
458 static unsigned sp_table[] = {
459     B9600, 960,
460 
461 #ifdef B19200
462     B19200, 1920,
463 #else
464 
465 #ifdef EXTA
466     EXTA, 1920,
467 #endif				/* EXTA */
468 
469 #endif				/* B19200 */
470 
471 #ifdef B38400
472     B38400, 3840,
473 #else
474 
475 #ifdef EXTB
476     EXTB, 3840,
477 #endif				/* EXTB */
478 
479 #endif				/* B38400 */
480 
481     B1200, 120,
482     B2400, 240,
483     B4800, 480,
484     B300, 30,
485     0, 0
486 };
487 
488 static void
set_term_speed(register unsigned long sp)489 set_term_speed(register unsigned long sp)
490 {
491     register unsigned *tp;
492 
493     for (tp = sp_table; *tp; tp += 2)
494 	if (*tp == sp) {
495 	    terminal_speed = tp[1];
496 	    return;
497 	}
498     terminal_speed = 30;
499 }
500 
501 static void
raw_not_ok_error(void)502 raw_not_ok_error(void)
503 {
504     if (batch_mode)
505 	return;
506     nn_exitmsg(1, "Not prepared for terminal i/o");
507     /* NOTREACHED */
508 }
509 
510 
511 #define	RAW_CHECK	if (terminal_speed == 0) {raw_not_ok_error(); return 0;}
512 #define	RAW_CHECK_V	if (terminal_speed == 0) {raw_not_ok_error(); return;}
513 #define BATCH_CHECK	if (terminal_speed == 0) return 0
514 #define BATCH_CHECK_V	if (terminal_speed == 0) return
515 
516 void
init_term(int full)517 init_term(int full)
518 {
519 
520 #ifdef USE_TERMCAP
521     char            tbuf[1024];
522 #endif
523 
524     int             i;
525 
526 #ifdef TERM_DEBUG
527     term_debug = getenv("TERM_DEBUG");
528     if (term_debug)
529 	fprintf(stderr, "init_term(%d)\n", full);
530 #endif
531 
532     if (batch_mode) {
533 	term_name = "batch";
534 	close(0);
535 	open("/dev/null", 0);
536 	STANDOUT = 0;
537 	cookie_size = 1;
538 	return;
539     }
540     if ((term_name = getenv("TERM")) == NULL) {
541 	if (full)
542 	    nn_exitmsg(1, "No TERM variable in environment");
543 	else
544 	    term_name = "unknown";
545     }
546     if (!full)
547 	return;
548 
549 #ifdef HAVE_TERMIO_H
550     ioctl(0, TCGETA, &norm_tty);
551 #else
552 
553 #ifdef HAVE_TERMIOS_H
554     tcgetattr(0, &norm_tty);
555 #else
556     ioctl(0, TIOCGETP, &norm_tty);
557 #endif				/* HAVE_TERMIOS_H */
558 
559 #endif				/* HAVE_TERMIO_H */
560 
561 #ifdef USE_TERMINFO
562     setupterm((char *) NULL, 1, (int *) NULL);
563     Columns = columns;
564     Lines = lines;
565     if (use_visible_bell && HAS_CAP(flash_screen))
566 	strcpy(bell_str, flash_screen);
567     else if (HAS_CAP(bell))
568 	strcpy(bell_str, bell);
569     if (!HAS_CAP(cursor_home))
570 	cursor_home = copy_str(tgoto(cursor_address, 0, 0));
571 #else
572 
573     if (tgetent(tbuf, term_name) <= 0)
574 	nn_exitmsg(1, "Unknown terminal type: %s", term_name);
575 
576     if (opt_cap("bc", XBC))
577 	BC = XBC;
578     if (opt_cap("up", XUP))
579 	UP = XUP;
580     opt_cap("pc", cursor_address);	/* temp. usage */
581     PC = cursor_address[0];
582 
583     get_cap("cm", cursor_address);
584     if (!opt_cap("ho", cursor_home))
585 	strcpy(cursor_home, tgoto(cursor_address, 0, 0));
586 
587     get_cap("cl", clear_screen);
588     opt_cap("ce", clr_eol);
589     opt_cap("cd", clr_eos);
590 
591     Lines = tgetnum("li");
592     Columns = tgetnum("co");
593 
594     opt_cap("so", enter_standout_mode);
595     opt_cap("se", exit_standout_mode);
596 
597     opt_cap("us", enter_underline_mode);
598     opt_cap("ue", exit_underline_mode);
599 
600     opt_cap("kd", key_down);
601     opt_cap("ku", key_up);
602     opt_cap("kr", key_right);
603     opt_cap("kl", key_left);
604 
605     magic_cookie_glitch = tgetnum("sg");
606 
607     ceol_standout_glitch = tgetflag("xs");
608     auto_right_margin = tgetflag("am");
609     eat_newline_glitch = tgetflag("xn");
610 
611     opt_cap("ti", enter_ca_mode);
612     opt_cap("te", exit_ca_mode);
613     opt_cap("ks", keypad_xmit);	/* used to turn "application cursor */
614     opt_cap("ke", keypad_local);/* key" mode on and off (sometimes) */
615 
616     if (!use_visible_bell || !opt_cap("vb", bell_str))
617 	if (!opt_cap("bl", bell_str))
618 	    strcpy(bell_str, "\007");
619 #endif				/* USE_TERMINFO */
620 
621 #ifdef RESIZING
622     {
623 	struct winsize  winsize;
624 
625 	if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
626 	    && winsize.ws_row != 0 && winsize.ws_col != 0) {
627 	    Lines = winsize.ws_row;
628 	    Columns = winsize.ws_col;
629 	    signal(SIGWINCH, catch_winch);
630 
631 #ifdef SV_INTERRUPT
632 	    siginterrupt(SIGWINCH, 1);	/* make read from tty interruptable */
633 #endif				/* SV_INTERRUPT */
634 	}
635     }
636 #endif				/* RESIZING */
637 
638     /* Stop NN from blowing up if on a *really* dumb terminal, like "dumb" */
639     if (Lines < 1)
640 	Lines = 24;
641     if (Columns < 1)
642 	Columns = 80;
643 
644     nonsp = newobj(int, Lines);
645     for (i = 0; i < Lines; i++)
646 	nonsp[i] = Columns;
647 
648     STANDOUT = HAS_CAP(enter_standout_mode);
649     cookie_size = magic_cookie_glitch;
650     if (STANDOUT) {
651 	if (cookie_size < 0)
652 	    cookie_size = 0;
653 	two_cookies = 2 * cookie_size;
654     } else
655 	cookie_size = two_cookies = 0;
656 
657     raw_tty = norm_tty;
658 
659 #ifdef HAVE_TERMIO_H
660     raw_tty.c_iflag &= ~(BRKINT | INLCR | ICRNL | IGNCR | ISTRIP);
661     raw_tty.c_iflag |= IGNBRK | IGNPAR;
662     raw_tty.c_oflag &= ~OPOST;
663     raw_tty.c_lflag &= ~(ISIG | ICANON | XCASE | ECHO | NOFLSH);
664 
665     /* read a maximum of 10 characters in one burst; timeout in 1-200 ms */
666     raw_tty.c_cc[VMIN] = KEY_BURST;
667     raw_tty.c_cc[VTIME] = ((int) (raw_tty.c_cflag & CBAUD) > B1200) ? 1 : 2;
668     set_term_speed((unsigned long) (raw_tty.c_cflag & CBAUD));
669 #else
670 
671 #ifdef HAVE_TERMIOS_H
672     cfmakeraw(&raw_tty);
673     /* read a maximum of 10 characters in one burst; timeout in 1-200 ms */
674     raw_tty.c_cc[VMIN] = KEY_BURST;
675 
676 #if 0
677     raw_tty.c_cc[VTIME] = (cfgetispeed(&raw_tty) > B1200) ? 1 : 2;
678 #else
679     raw_tty.c_cc[VTIME] = 1;
680 #endif				/* 0 */
681 
682     set_term_speed((unsigned long) cfgetospeed(&raw_tty));
683 
684 #ifdef SV_INTERRUPT
685     siginterrupt(SIGTSTP, 1);
686     siginterrupt(SIGALRM, 1);
687 #endif				/* SV_INTERRUPT */
688 
689 #else
690     ioctl(0, TIOCGETC, &norm_chars);
691 
692 #ifdef TIOCGLTC
693     ioctl(0, TIOCGLTC, &spec_chars);
694 #endif				/* TIOCGLTC */
695 
696     nnspeed = norm_tty.sg_ospeed;
697     set_term_speed((unsigned long) nnspeed);
698 
699     raw_tty.sg_flags &= ~(ECHO | CRMOD);
700 
701 #ifdef CBREAK
702 
703 #ifdef SV_INTERRUPT		/* make read from tty interruptable */
704     siginterrupt(SIGTSTP, 1);	/* this is necessary to redraw screen */
705 #endif				/* SV_INTERRUPT */
706 
707     raw_tty.sg_flags |= CBREAK;
708 #else
709     raw_tty.sg_flags |= RAW;
710 #endif				/* CBREAK */
711 
712 #ifdef SV_INTERRUPT
713     siginterrupt(SIGALRM, 1);	/* make read from tty interruptable */
714 #endif				/* SV_INTERRUPT */
715 
716 #endif				/* HAVE_TERMIOS_H */
717 
718 #endif				/* HAVE_TERMIO_H */
719 
720     Name_Length = Columns / 5;
721     if (Name_Length < NAME_LENGTH)
722 	Name_Length = NAME_LENGTH;
723 
724     erase_key = EraseC;
725     kill_key = KillC;
726 
727     if (HAS_CAP(key_down))
728 	enter_multi_key(K_down_arrow, (key_type *) key_down);
729     if (HAS_CAP(key_up))
730 	enter_multi_key(K_up_arrow, (key_type *) key_up);
731     if (HAS_CAP(key_right))
732 	enter_multi_key(K_right_arrow, (key_type *) key_right);
733     if (HAS_CAP(key_left))
734 	enter_multi_key(K_left_arrow, (key_type *) key_left);
735 
736     enter_multi_key(K_m_d1, (key_type *) key_mouse_d1);
737     enter_multi_key(K_m_d2, (key_type *) key_mouse_d2);
738     enter_multi_key(K_m_d3, (key_type *) key_mouse_d3);
739     enter_multi_key(K_m_u1, (key_type *) key_mouse_u1);
740 
741     appl_keypad_mode = (HAS_CAP(keypad_xmit) && HAS_CAP(keypad_local));
742     if (!HAS_CAP(key_up))
743 	appl_keypad_mode = 0;	/* no cursor keys */
744     if (appl_keypad_mode) {
745 	/* Use of ks/ke isn't consistent, so we must guess what to do. */
746 	/* If termcap expects keys to send ESC [, don't switch */
747 	appl_keypad_mode = (key_up[0] != '\033' || key_up[1] != '[');
748     }
749     if ((mouse_usage == 2) || ((mouse_usage == 1)
750 			       && !strncmp("xterm", term_name, 5))) {
751 	mouse_state = 1;
752 	flow_control = 0;
753     } else {
754 	mouse_state = 0;
755     }
756     visual_on();
757 }
758 
759 void
home(void)760 home(void)
761 {
762     BATCH_CHECK_V;
763 
764 #ifdef TERM_DEBUG
765     if (term_debug)
766 	fprintf(stderr, "home\n");
767 #endif
768 
769     putp(cursor_home);
770     curxy_c = curxy_l = 0;
771 }
772 
773 void
save_xy(void)774 save_xy(void)
775 {
776     savxy_c = curxy_c;
777     savxy_l = curxy_l;
778 }
779 
780 void
restore_xy(void)781 restore_xy(void)
782 {
783     if (savxy_l < 0)
784 	return;
785     gotoxy(savxy_c, savxy_l);
786     fl;
787 }
788 
789 void
gotoxy(int c,int l)790 gotoxy(int c, int l)
791 {
792     BATCH_CHECK_V;
793 
794 #ifdef TERM_DEBUG
795     if (term_debug)
796 	fprintf(stderr, "gotoxy %d %d -> %d %d\n", curxy_c, curxy_l, c, l);
797 #endif
798 
799     if (Columns <= (unsigned) c || Lines <= (unsigned) l)
800 	return;
801 
802     if (!(c | l))
803 	putp(cursor_home);
804     else
805 	putp(tgoto(cursor_address, c, l));
806     curxy_c = c;
807     curxy_l = l;
808 }
809 
810 void
clrdisp(void)811 clrdisp(void)
812 {
813     int             i;
814 
815     BATCH_CHECK_V;
816 
817 #ifdef TERM_DEBUG
818     if (term_debug)
819 	fprintf(stderr, "clrdisp\n");
820 #endif
821 
822     putpc(clear_screen, Lines);
823     curxy_c = curxy_l = 0;
824     savxy_l = -1;
825     for (i = 0; i < Lines; i++)
826 	nonsp[i] = 0;
827     msg_ptr = msg_stack;
828 }
829 
830 void            clrline_noflush();
831 
832 void
clrline(void)833 clrline(void)
834 {
835     BATCH_CHECK_V;
836 
837     /* If we moved the cursor left to avoid weird effects, don't clear. */
838     if (just_sent_cr)
839 	return;
840 
841     clrline_noflush();
842     fl;
843 }
844 
845 void
clrline_noflush(void)846 clrline_noflush(void)
847 {
848     int             oldxy_c, oldxy_l, spcnt;
849 
850     BATCH_CHECK_V;
851 
852 #ifdef TERM_DEBUG
853     if (term_debug)
854 	fprintf(stderr, "clrline %d %d [%d]", curxy_c, curxy_l, curxy_nonsp);
855 #endif
856 
857     if (curxy_l < 0)
858 	return;
859 
860     /* remainder of line already blank? */
861 
862     if (curxy_c >= nonsp[curxy_l]) {
863 
864 #ifdef TERM_DEBUG
865 	if (term_debug)
866 	    fprintf(stderr, " ignored\n");
867 #endif
868 
869 	return;
870     }
871     if (HAS_CAP(clr_eol)) {
872 
873 #ifdef TERM_DEBUG
874 	if (term_debug)
875 	    fprintf(stderr, "\n");
876 #endif
877 
878 	putp(clr_eol);
879     } else {
880 	oldxy_c = curxy_c;
881 	oldxy_l = curxy_l;
882 
883 	spcnt = nonsp[curxy_l] - curxy_c;
884 
885 	/* guard against scroll */
886 
887 	if (auto_right_margin && curxy_l == Lines - 1 && curxy_c + spcnt == Columns)
888 	    spcnt--;
889 
890 #ifdef TERM_DEBUG
891 	if (term_debug)
892 	    fprintf(stderr, " %d\n", spcnt);
893 #endif				/* TERM_DEBUG */
894 
895 #ifdef TERM_DEBUG
896 	if (term_debug && *term_debug) {
897 	    while (spcnt--)
898 		tputc(*term_debug);
899 	    spcnt = 0;
900 	}
901 #endif
902 
903 	/* clear out line */
904 
905 	while (spcnt--)
906 	    tputc(SP);
907 
908 	if (curxy_c != oldxy_c || curxy_l != oldxy_l)
909 	    gotoxy(oldxy_c, oldxy_l);
910     }
911     nonsp[curxy_l] = curxy_c;
912 }
913 
914 void
clrpage(void)915 clrpage(void)
916 {
917     int             oldxy_c, oldxy_l, i;
918 
919     BATCH_CHECK_V;
920 
921 #ifdef TERM_DEBUG
922     if (term_debug)
923 	fprintf(stderr, "clrpage %d %d [%d]\n", curxy_c, curxy_l, curxy_nonsp);
924 #endif
925 
926     if (curxy_l < 0)
927 	return;
928 
929     oldxy_c = curxy_c;
930     oldxy_l = curxy_l;
931 
932     /* code below only handles curxy_c == 0 */
933 
934     if (curxy_c != 0) {
935 	clrline();
936 	if (curxy_l < Lines - 1)
937 	    gotoxy(0, curxy_l + 1);
938     }
939     /* clear to end of screen */
940 
941     if (curxy_c == 0) {
942 	if (HAS_CAP(clr_eos)) {
943 	    putpc(clr_eos, Lines - curxy_l);
944 	    for (i = curxy_l; i < Lines; i++)
945 		nonsp[i] = 0;
946 	} else if (curxy_l == 0) {
947 	    putpc(clear_screen, Lines);
948 	    for (i = 0; i < Lines; i++)
949 		nonsp[i] = 0;
950 	} else {
951 	    for (i = curxy_l; i < Lines; i++) {
952 		if (nonsp[i] != 0) {
953 		    gotoxy(0, i);
954 		    clrline_noflush();
955 		}
956 	    }
957 	}
958     }
959     if (curxy_c != oldxy_c || curxy_l != oldxy_l)
960 	gotoxy(oldxy_c, oldxy_l);
961 
962     fl;
963     msg_ptr = msg_stack;
964 }
965 
966 static char     buf[512];
967 
968 static void
tvprintf(char * fmt,va_list ap)969 tvprintf(char *fmt, va_list ap)
970 {
971     char           *str;
972 
973     vsprintf(buf, fmt, ap);
974     for (str = buf; *str; str++)
975 	tputc(*str);
976 }
977 
978 void
tprintf(char * fmt,...)979 tprintf(char *fmt,...)
980 {
981     va_list         ap;
982 
983     va_start(ap, fmt);
984     tvprintf(fmt, ap);
985     va_end(ap);
986 }
987 
988 void
tputc(int c)989 tputc(int c)
990 {
991     int             i;
992 
993 #ifdef TERM_DEBUG
994     if (term_debug) {
995 	fprintf(stderr, "tputc %d %d [%d] ", curxy_c, curxy_l, curxy_nonsp);
996 	if (c < ' ' || c > '~')
997 	    switch (c) {
998 		case 0:
999 		    fprintf(stderr, "'NUL'");
1000 		    break;
1001 		case 1:
1002 		    fprintf(stderr, "'SOH'");
1003 		    break;
1004 		case 2:
1005 		    fprintf(stderr, "'STX'");
1006 		    break;
1007 		case 3:
1008 		    fprintf(stderr, "'ETX'");
1009 		    break;
1010 		case 4:
1011 		    fprintf(stderr, "'EOT'");
1012 		    break;
1013 		case 5:
1014 		    fprintf(stderr, "'ENQ'");
1015 		    break;
1016 		case 6:
1017 		    fprintf(stderr, "'ACK'");
1018 		    break;
1019 		case 7:
1020 		    fprintf(stderr, "'BEL'");
1021 		    break;
1022 		case 8:
1023 		    fprintf(stderr, "'BS'");
1024 		    break;
1025 		case 9:
1026 		    fprintf(stderr, "'HT'");
1027 		    break;
1028 		case 10:
1029 		    fprintf(stderr, "'NL'");
1030 		    break;
1031 		case 11:
1032 		    fprintf(stderr, "'VT'");
1033 		    break;
1034 		case 12:
1035 		    fprintf(stderr, "'NP'");
1036 		    break;
1037 		case 13:
1038 		    fprintf(stderr, "'CR'");
1039 		    break;
1040 		case 14:
1041 		    fprintf(stderr, "'SP'");
1042 		    break;
1043 		case 15:
1044 		    fprintf(stderr, "'SI'");
1045 		    break;
1046 		case 16:
1047 		    fprintf(stderr, "'DLE'");
1048 		    break;
1049 		case 17:
1050 		    fprintf(stderr, "'DC1'");
1051 		    break;
1052 		case 18:
1053 		    fprintf(stderr, "'DC2'");
1054 		    break;
1055 		case 19:
1056 		    fprintf(stderr, "'DC3'");
1057 		    break;
1058 		case 20:
1059 		    fprintf(stderr, "'DC4'");
1060 		    break;
1061 		case 21:
1062 		    fprintf(stderr, "'NAK'");
1063 		    break;
1064 		case 22:
1065 		    fprintf(stderr, "'SYN'");
1066 		    break;
1067 		case 23:
1068 		    fprintf(stderr, "'ETB'");
1069 		    break;
1070 		case 24:
1071 		    fprintf(stderr, "'CAN'");
1072 		    break;
1073 		case 25:
1074 		    fprintf(stderr, "'EM'");
1075 		    break;
1076 		case 26:
1077 		    fprintf(stderr, "'SUB'");
1078 		    break;
1079 		case 27:
1080 		    fprintf(stderr, "'ESC'");
1081 		    break;
1082 		case 28:
1083 		    fprintf(stderr, "'FS'");
1084 		    break;
1085 		case 29:
1086 		    fprintf(stderr, "'GS'");
1087 		    break;
1088 		case 30:
1089 		    fprintf(stderr, "'RS'");
1090 		    break;
1091 		case 31:
1092 		    fprintf(stderr, "'US'");
1093 		    break;
1094 		case 32:
1095 		    fprintf(stderr, "'SP'");
1096 		    break;
1097 		default:
1098 		    fprintf(stderr, "out of range: ???");
1099 		    break;
1100 	    }
1101 	else
1102 	    fprintf(stderr, "'%c'", c);
1103     }
1104 #endif
1105 
1106     just_sent_cr = 0;
1107 
1108     putchar(c);
1109 
1110     switch (c) {
1111 
1112 	case '\n':
1113 	    curxy_c = 0;
1114 	    if (curxy_l >= 0)
1115 		curxy_l++;
1116 	    break;
1117 
1118 	case '\r':
1119 	    curxy_c = 0;
1120 	    break;
1121 
1122 	case '\t':
1123 	    curxy_c = DISCARD_REMAINDER(curxy_c + 8, 8);
1124 	    break;
1125 
1126 	case '\b':
1127 	    curxy_c--;
1128 	    break;
1129 
1130 	case ' ':
1131 	    curxy_c++;
1132 	    if (curxy_l >= 0 && nonsp[curxy_l] == curxy_c)
1133 		nonsp[curxy_l]--;
1134 	    break;
1135 
1136 	default:
1137 	    curxy_c++;
1138 	    if (curxy_l >= 0 && nonsp[curxy_l] < curxy_c)
1139 		nonsp[curxy_l] = curxy_c;
1140 	    break;
1141 
1142     }
1143 
1144 #ifdef TERM_DEBUG
1145     if (term_debug)
1146 	fprintf(stderr, " -> %d %d [%d]", curxy_c, curxy_l, curxy_nonsp);
1147 #endif
1148 
1149     /* account for right margin */
1150 
1151     if (curxy_c == Columns) {
1152 
1153 #ifdef TERM_DEBUG
1154 	if (term_debug)
1155 	    fprintf(stderr, " margin");
1156 #endif
1157 
1158 	if (auto_right_margin) {
1159 	    if (eat_newline_glitch) {
1160 		putchar(CR);
1161 		just_sent_cr = 1;
1162 		curxy_c = 0;
1163 	    } else {
1164 		curxy_c = 0;
1165 		if (curxy_l >= 0)
1166 		    curxy_l++;
1167 	    }
1168 	} else
1169 	    curxy_c--;
1170     }
1171     /* account for vertical scroll */
1172 
1173     if (curxy_l == Lines) {
1174 
1175 #ifdef TERM_DEBUG
1176 	if (term_debug)
1177 	    fprintf(stderr, " scroll");
1178 #endif
1179 
1180 	for (i = 1; i < Lines; i++)
1181 	    nonsp[i - 1] = nonsp[i];
1182 	nonsp[--curxy_l] = 0;
1183     }
1184 
1185 #ifdef TERM_DEBUG
1186     if (term_debug)
1187 	fprintf(stderr, "\n");
1188 #endif
1189 }
1190 
1191 static char     so_buf[512], *so_p;
1192 static int      so_c, so_l, so_b, so_active = 0;
1193 
1194 int
so_gotoxy(int c,int l,int blank)1195 so_gotoxy(int c, int l, int blank)
1196 {
1197     if (!STANDOUT && c >= 0) {
1198 	if (c >= 0 && l >= 0)
1199 	    gotoxy(c, l);
1200 	return 0;
1201     }
1202     so_active++;
1203     so_c = c;
1204     so_l = l;
1205     so_b = blank;
1206     so_p = so_buf;
1207     *so_p = NUL;
1208 
1209     return 1;			/* not really true if not standout & c < 0 */
1210 }
1211 
1212 static void
so_vprintf(char * fmt,va_list ap)1213 so_vprintf(char *fmt, va_list ap)
1214 {
1215     if (!so_active) {
1216 	if (ceol_standout_glitch)
1217 	    highlight(0);	/* xxx why? */
1218 	tvprintf(fmt, ap);
1219 	return;
1220     }
1221     vsprintf(so_p, fmt, ap);
1222     while (*so_p)
1223 	so_p++;
1224 }
1225 
1226 void
so_printf(char * fmt,...)1227 so_printf(char *fmt,...)
1228 {
1229     va_list         ap;
1230 
1231     va_start(ap, fmt);
1232     so_vprintf(fmt, ap);
1233     va_end(ap);
1234 }
1235 
1236 void
so_end(void)1237 so_end(void)
1238 {
1239     int             len;
1240 
1241     if (!so_active)
1242 	return;
1243 
1244     if (so_l >= 0) {
1245 
1246 	len = so_p - so_buf + two_cookies;
1247 
1248 	if (so_c < 0)
1249 	    so_c = Columns - len - 2;
1250 	if (so_c < 0)
1251 	    so_c = 0;
1252 
1253 	if (len + so_c >= Columns) {
1254 	    len = Columns - so_c - two_cookies;
1255 	    so_buf[len] = NUL;
1256 	}
1257 	if (cookie_size) {
1258 	    gotoxy(so_c + len - cookie_size, so_l);
1259 	    nnstandout(0);
1260 	}
1261 	gotoxy(so_c, so_l);
1262 
1263     }
1264     if ((so_b & 1) && (!STANDOUT || !cookie_size))
1265 	tputc(SP);
1266 
1267     if (STANDOUT) {
1268 	if (ceol_standout_glitch)
1269 	    clrline();
1270 	nnstandout(1);
1271     }
1272     tprintf("%s", so_buf);
1273 
1274     if (STANDOUT)
1275 	nnstandout(0);
1276 
1277     if ((so_b & 2) && (!STANDOUT || !cookie_size))
1278 	tputc(SP);
1279 
1280     so_active = 0;
1281 }
1282 
1283 
1284 void
so_printxy(int c,int l,char * fmt,...)1285 so_printxy(int c, int l, char *fmt,...)
1286 {
1287     va_list         ap;
1288 
1289     va_start(ap, fmt);
1290     so_gotoxy(c, l, 0);
1291     so_vprintf(fmt, ap);
1292     so_end();
1293     va_end(ap);
1294 }
1295 
1296 
1297 int
underline(int on)1298 underline(int on)
1299 {
1300     if (cookie_size)
1301 	return 0;
1302 
1303 #ifdef TERM_DEBUG
1304     if (term_debug)
1305 	fprintf(stderr, "underline %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on);
1306 #endif
1307 
1308     if (!HAS_CAP(enter_underline_mode))
1309 	return 0;
1310     putp(on ? enter_underline_mode : exit_underline_mode);
1311     return 1;
1312 }
1313 
1314 int
highlight(int on)1315 highlight(int on)
1316 {
1317     if (cookie_size)
1318 	return 0;
1319 
1320 #ifdef TERM_DEBUG
1321     if (term_debug)
1322 	fprintf(stderr, "highlight %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on);
1323 #endif
1324 
1325     if (!HAS_CAP(enter_standout_mode))
1326 	return 0;
1327     putp(on ? enter_standout_mode : exit_standout_mode);
1328     return 1;
1329 }
1330 
1331 int
shadeline(int on)1332 shadeline(int on)
1333 {
1334     if (cookie_size)
1335 	return 0;
1336 
1337 #ifdef TERM_DEBUG
1338     if (term_debug)
1339 	fprintf(stderr, "shadeline %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on);
1340 #endif
1341 
1342     if (shade_on_attr && shade_off_attr) {
1343 	putp(on ? shade_on_attr : shade_off_attr);
1344 	return 1;
1345     } else
1346 	return underline(1);
1347 }
1348 
1349 static int
nnstandout(int on)1350 nnstandout(int on)
1351 {
1352 
1353 #ifdef TERM_DEBUG
1354     if (term_debug)
1355 	fprintf(stderr, "standout %d %d [%d] %d", curxy_c, curxy_l, curxy_nonsp, on);
1356 #endif
1357 
1358     if (!HAS_CAP(enter_standout_mode))
1359 	return 0;
1360     putp(on ? enter_standout_mode : exit_standout_mode);
1361     curxy_c += cookie_size;
1362     if (curxy_l >= 0 && curxy_c > nonsp[curxy_l])
1363 	nonsp[curxy_l] = curxy_c;
1364 
1365 #ifdef TERM_DEBUG
1366     if (term_debug)
1367 	fprintf(stderr, " -> %d %d [%d]\n", curxy_c, curxy_l, curxy_nonsp);
1368 #endif
1369 
1370     return 1;
1371 }
1372 
1373 static int      is_visual = 0;
1374 static int      is_raw = 0;
1375 
1376 #ifdef HAVE_TERMIO_H
1377 #define RAW_MODE_ON    ioctl(0, TCSETAW, &raw_tty)
1378 #define RAW_MODE_OFF   ioctl(0, TCSETAW, &norm_tty)
1379 #else
1380 
1381 #ifdef HAVE_TERMIOS_H
1382 #define RAW_MODE_ON    tcsetattr(0, TCSADRAIN, &raw_tty)
1383 #define RAW_MODE_OFF   tcsetattr(0, TCSADRAIN, &norm_tty)
1384 #else
1385 #define RAW_MODE_ON    ioctl(0, TIOCSETP, &raw_tty)
1386 #define RAW_MODE_OFF   ioctl(0, TIOCSETP, &norm_tty)
1387 #endif				/* HAVE_TERMIOS_H */
1388 
1389 #endif				/* HAVE_TERMIO_H */
1390 
1391 void
xterm_mouse_on(void)1392 xterm_mouse_on(void)
1393 {
1394     putp("\33[?1000h");
1395 }
1396 
1397 void
xterm_mouse_off(void)1398 xterm_mouse_off(void)
1399 {
1400     putp("\33[?1000l");
1401 }
1402 
1403 void
visual_on(void)1404 visual_on(void)
1405 {
1406     BATCH_CHECK_V;
1407 
1408     if (terminal_speed == 0)
1409 	return;
1410 
1411 #ifdef TERM_DEBUG
1412     if (term_debug)
1413 	fprintf(stderr, "visual_on\n");
1414 #endif
1415 
1416     if (!is_visual) {
1417 	if (HAS_CAP(enter_ca_mode))
1418 	    putp(enter_ca_mode);
1419 	if (appl_keypad_mode)
1420 	    putp(keypad_xmit);
1421 	if (mouse_state)
1422 	    xterm_mouse_on();
1423 	fl;
1424     }
1425     is_visual = 1;
1426 }
1427 
1428 int
visual_off(void)1429 visual_off(void)
1430 {
1431     int             was_raw = is_raw;
1432 
1433     if (terminal_speed == 0)
1434 	return 0;
1435 
1436 #ifdef TERM_DEBUG
1437     if (term_debug)
1438 	fprintf(stderr, "visual_off\n");
1439 #endif
1440 
1441     if (is_visual) {
1442 	if (appl_keypad_mode)
1443 	    putp(keypad_local);
1444 	if (HAS_CAP(exit_ca_mode))
1445 	    putp(exit_ca_mode);
1446 	if (mouse_state)
1447 	    xterm_mouse_off();
1448 	fl;
1449     }
1450     is_visual = 0;
1451 
1452     is_raw = 1;
1453     unset_raw();
1454 
1455     return was_raw;
1456 }
1457 
1458 
1459 #ifdef CBREAK
1460 void
nn_raw(void)1461 nn_raw(void)
1462 {
1463     RAW_CHECK_V;
1464 
1465     if (is_raw == 1)
1466 	return;
1467     is_raw = 1;
1468     RAW_MODE_ON;
1469 }
1470 
1471 int
no_raw(void)1472 no_raw(void)
1473 {
1474     return 0;
1475 }
1476 
1477 int
unset_raw(void)1478 unset_raw(void)
1479 {
1480     if (is_raw == 0)
1481 	return 0;
1482     RAW_CHECK;
1483     RAW_MODE_OFF;
1484     is_raw = 0;
1485     return 1;
1486 }
1487 
1488 #else				/* not CBREAK */
1489 static int      must_set_raw = 1;
1490 
1491 #undef raw
1492 void
nn_raw(void)1493 nn_raw(void)
1494 {
1495     RAW_CHECK_V;
1496 
1497     if (!flow_control) {
1498 	if (!must_set_raw)
1499 	    return;
1500 	must_set_raw = 0;
1501     }
1502     if (is_raw)
1503 	return;
1504 
1505     RAW_MODE_ON;
1506     is_raw++;
1507 }
1508 
1509 int
no_raw(void)1510 no_raw(void)
1511 {
1512     if (!flow_control)
1513 	return 0;
1514 
1515     if (!is_raw)
1516 	return 0;
1517 
1518     RAW_CHECK;
1519 
1520     RAW_MODE_OFF;
1521 
1522     is_raw = 0;
1523 
1524     return 1;
1525 }
1526 
1527 int
unset_raw(void)1528 unset_raw(void)
1529 {
1530     int             was_raw = is_raw;
1531 
1532     if (is_raw) {
1533 	RAW_CHECK;
1534 	RAW_MODE_OFF;
1535 	is_raw = 0;
1536     }
1537     if (!flow_control)
1538 	must_set_raw = 1;
1539     return was_raw;
1540 }
1541 
1542 #endif				/* CBREAK */
1543 
1544 #ifndef KEY_BURST
1545 #define KEY_BURST 32
1546 #endif				/* KEY_BURST */
1547 
1548 #define RD_PUSHBACK 10
1549 static char     rd_buffer[KEY_BURST + RD_PUSHBACK];	/* Holds stuff from read */
1550 static char    *rd_ptr;
1551 static int      rd_count = 0, rd_alarm = 0;
1552 
1553 #ifdef FAKE_INTERRUPT
1554 static jmp_buf  fake_alarm_sig;
1555 #endif				/* FAKE_INTERRUPT */
1556 
1557 static          sig_type
rd_timeout(int n)1558 rd_timeout(int n)
1559 {
1560     rd_alarm = 1;
1561 
1562 #ifdef FAKE_INTERRUPT
1563     longjmp(fake_alarm_sig, 1);
1564 #endif				/* FAKE_INTERRUPT */
1565 }
1566 
1567 #define RD_TIMEOUT	0x1000
1568 #define RD_INTERRUPT	0x1001
1569 
1570 static int
read_char_kbd(int tmo)1571 read_char_kbd(int tmo)
1572  /* tmo	 timeout if no input arrives */
1573 {
1574     if (rd_count <= 0) {
1575 	if (tmo) {
1576 
1577 #ifdef FAKE_INTERRUPT
1578 	    if (setjmp(fake_alarm_sig))
1579 		goto tmout;
1580 #endif				/* FAKE_INTERRUPT */
1581 
1582 	    rd_alarm = 0;
1583 	    signal(SIGALRM, rd_timeout);
1584 	    micro_alarm(multi_key_guard_time);
1585 	}
1586 	rd_ptr = rd_buffer + RD_PUSHBACK;
1587 	rd_count = read(0, rd_ptr, KEY_BURST);
1588 	if (tmo) {
1589 	    if (rd_alarm)
1590 		goto tmout;
1591 	    alarm(0);
1592 	}
1593 	if (rd_count < 0) {
1594 	    if (errno != EINTR)
1595 		s_hangup++;
1596 	    return RD_INTERRUPT;
1597 	}
1598     }
1599     --rd_count;
1600     return *rd_ptr++;
1601 
1602 tmout:
1603     rd_count = 0;
1604     return RD_TIMEOUT;
1605 }
1606 
1607 static void
unread_char(int c)1608 unread_char(int c)
1609 {
1610     if (rd_ptr == rd_buffer)
1611 	return;
1612     rd_count++;
1613     *--rd_ptr = c;
1614 }
1615 
1616 void
flush_input(void)1617 flush_input(void)
1618 {
1619 
1620 #ifndef HAVE_TERMIO_H
1621 
1622 #if !defined(HAVE_TERMIOS_H) && defined(FREAD)
1623     int             arg;
1624 #endif
1625 
1626 #endif
1627 
1628     BATCH_CHECK_V;
1629 
1630 #ifdef HAVE_TERMIO_H
1631     ioctl(0, TCFLSH, 0);
1632 #else
1633 
1634 #ifdef HAVE_TERMIOS_H
1635     ioctl(0, TCIFLUSH);
1636 #else
1637 
1638 #ifdef FREAD
1639     arg = FREAD;
1640     ioctl(0, TIOCFLUSH, &arg);
1641 #else
1642     ioctl(0, TIOCFLUSH, 0);
1643 #endif				/* FREAD */
1644 
1645 #endif				/* HAVE_TERMIOS_H */
1646 
1647 #endif				/* HAVE_TERMIO_H */
1648 
1649     rd_count = 0;
1650 }
1651 
1652 int             enable_stop = 1;
1653 
1654 static int      do_macro_processing = 1;
1655 int             mouse_y = -1;
1656 static int      mouse_last = 0;
1657 
1658 int
get_c(void)1659 get_c(void)
1660 {
1661     key_type        c, first_key;
1662     int             key_cnt, mc, n;
1663     register struct multi_key *mk, *multi_match;
1664     register int    i;
1665 
1666     multi_match = NULL;
1667     first_key = 0;
1668     mouse_y = -1;
1669 
1670 next_key:
1671     if (s_hangup)
1672 	return K_interrupt;
1673 
1674     if (do_macro_processing)
1675 	switch (m_getc(&mc)) {
1676 	    case 0:
1677 		break;
1678 	    case 1:
1679 		return mc;
1680 	    case 2:
1681 		return K_interrupt;
1682 	}
1683 
1684 #ifdef RESIZING
1685     if (s_resized)
1686 	goto redraw;
1687 #endif				/* RESIZING */
1688 
1689     if (batch_mode)
1690 	nn_exitmsg(1, "Attempt to read keyboard input in batch mode");
1691 
1692     for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++)
1693 	mk->cur_key = mk->keys;
1694     key_cnt = 0;
1695 
1696 #ifdef FAKE_INTERRUPT
1697     if (setjmp(fake_keyb_sig))
1698 	goto intr;
1699     arm_fake_keyb_sig = 1;
1700 #endif				/* FAKE_INTERRUPT */
1701 
1702 multi:
1703     switch (n = read_char_kbd(key_cnt)) {
1704 
1705 	case RD_INTERRUPT:
1706 
1707 #ifdef CBREAK
1708 	    if (s_redraw)
1709 		goto redraw;
1710 #endif				/* CBREAK */
1711 
1712 #ifdef RESIZING
1713 	    if (s_resized)
1714 		goto redraw;
1715 #endif				/* RESIZING */
1716 
1717 	    goto intr;
1718 
1719 	case RD_TIMEOUT:
1720 	    while (--key_cnt > 0)
1721 		unread_char(multi_match->keys[key_cnt]);
1722 	    c = first_key;
1723 	    goto got_char;
1724 
1725 	default:
1726 	    c = (key_type) n;
1727 	    if (data_bits < 8)
1728 		c &= 0x7f;
1729 	    if (ignore_xon_xoff)
1730 		if (c == CONTROL_('Q') || c == CONTROL_('S'))
1731 		    goto multi;
1732 	    break;
1733     }
1734 
1735     multi_match = NULL;
1736     for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++) {
1737 	if (mk->cur_key == NUL)
1738 	    continue;
1739 	if (*(mk->cur_key)++ != c) {
1740 	    mk->cur_key = NUL;
1741 	    continue;
1742 	}
1743 	if (*(mk->cur_key) == NUL) {
1744 	    c = mk->code;
1745 
1746 	    /*
1747 	     * xterm mouse specific code, translate cursor position into
1748 	     * static variables, synthesize key up events
1749 	     */
1750 	    if ((c >= K_m_d1) && (c <= K_m_u1)) {
1751 		mouse_y = read_char_kbd(key_cnt) - '!';
1752 		if (c == K_m_u1) {
1753 		    if (mouse_last == K_m_d2) {
1754 			c = K_m_u2;
1755 		    } else if (mouse_last == K_m_d3) {
1756 			c = K_m_u3;
1757 		    }
1758 		}
1759 		mouse_last = c;
1760 	    }
1761 	    goto got_char;
1762 	}
1763 	multi_match = mk;
1764     }
1765 
1766     if (multi_match) {
1767 	if (key_cnt == 0)
1768 	    first_key = c;
1769 	key_cnt++;
1770 	goto multi;
1771     }
1772     if (key_cnt) {
1773 	if (key_cnt == 1 && first_key == 033) {
1774 	    unread_char(c);
1775 	    c = 033;
1776 	    goto got_char;
1777 	}
1778 	ding();
1779 	flush_input();
1780 	goto next_key;
1781     }
1782 got_char:
1783 
1784 #ifdef FAKE_INTERRUPT
1785     arm_fake_keyb_sig = 0;
1786 #endif				/* FAKE_INTERRUPT */
1787 
1788     c = global_key_map[c];
1789 
1790 #ifndef CBREAK
1791     if (c == IntrC)
1792 	return K_interrupt;	/* don't flush */
1793     if (c == SuspC) {
1794 	if (enable_stop && suspend_nn())
1795 	    goto redraw;
1796 	goto next_key;
1797     }
1798 #endif				/* CBREAK */
1799 
1800     return (int) c;
1801 
1802 intr:
1803 
1804 #ifdef FAKE_INTERRUPT
1805     arm_fake_keyb_sig = 0;
1806 #endif				/* FAKE_INTERRUPT */
1807 
1808     rd_count = 0;
1809     return K_interrupt;
1810 
1811 redraw:
1812 
1813 #ifdef RESIZING
1814     s_resized = 0;
1815 #endif				/* RESIZING */
1816 
1817     s_redraw = 0;
1818     return GETC_COMMAND | K_REDRAW;
1819 }
1820 
1821 
1822 /*
1823  * read string with completion, pre-filling, and break on first char
1824  *
1825  *	dflt		is a string that will be use as default value if the
1826  *			space bar is hit as the first character.
1827  *
1828  *	prefill		pre-fill the buffer with .... and print it
1829  *
1830  *	break_chars	return immediately if one of these characters
1831  *			is entered as the first character.
1832  *
1833  *	completion	is a function that will fill the buffer with a value
1834  *			see the group_completion and file_completion routines
1835  *			for examples.
1836  */
1837 
1838 char           *
get_s(char * dflt,char * prefill,char * break_chars,fct_type completion)1839 get_s(char *dflt, char *prefill, char *break_chars, fct_type completion)
1840 {
1841     static key_type lbuf[GET_S_BUFFER];
1842     register char  *cp;
1843     register int    i, c, lastc;
1844     char           *ret_val = (char *) lbuf;
1845     int             comp_used, comp_len;
1846     int             ostop, max, did_help;
1847     int             hit_count;
1848 
1849     switch (m_gets((char *) lbuf)) {
1850 	case 0:
1851 	    break;
1852 	case 1:
1853 	    return (char *) lbuf;
1854 	case 2:
1855 	    return NULL;
1856     }
1857 
1858     ostop = enable_stop;
1859     enable_stop = 0;
1860     do_macro_processing = 0;
1861     hit_count = 0;
1862 
1863     max = Columns - prompt_length;
1864 
1865     if (max >= FILENAME)
1866 	max = FILENAME - 1;
1867 
1868     i = comp_len = comp_used = did_help = 0;
1869 
1870     if (prefill && prefill[0]) {
1871 	while ((c = *prefill++)) {
1872 	    if (i == max)
1873 		break;
1874 
1875 	    tputc(c);
1876 	    lbuf[i] = c;
1877 	    i++;
1878 	}
1879 	fl;
1880     }
1881     if (dflt && *dflt == NUL)
1882 	dflt = NULL;
1883 
1884     if (break_chars && *break_chars == NUL)
1885 	break_chars = NULL;
1886 
1887     c = NUL;
1888     for (;;) {
1889 	lastc = c;
1890 	c = get_c();
1891 	if (c & GETC_COMMAND)
1892 	    continue;
1893 
1894 kill_prefill_hack:
1895 
1896 	hit_count++;
1897 
1898 	if (i == 0) {
1899 	    if (c == comp1_key && dflt) {
1900 		while ((c = *dflt++) != NUL && i < max) {
1901 		    tputc(c);
1902 		    lbuf[i] = c;
1903 		    i++;
1904 		}
1905 		fl;
1906 		dflt = NULL;
1907 		continue;
1908 	    }
1909 	    if ((cp = break_chars)) {	/* Stupid ^@$# GCC!!! */
1910 		while (*cp)
1911 		    if (*cp++ == c) {
1912 			lbuf[0] = c;
1913 			lbuf[1] = NUL;
1914 			goto out;
1915 		    }
1916 	    }
1917 	}
1918 	if (completion != NULL_FCT) {
1919 	    if (comp_used && c == erase_key) {
1920 		if (comp_len) {
1921 		    i -= comp_len;
1922 		    while (--comp_len >= 0)
1923 			tputc(BS);
1924 		    clrline();
1925 		}
1926 		if (!CALL(completion) (lbuf, -(i + 1)) && did_help)
1927 		    clrmsg(i);
1928 		did_help = 0;
1929 		comp_len = comp_used = 0;
1930 		if (lastc == help_key)
1931 		    goto no_completion;
1932 		continue;
1933 	    }
1934 	    if (c == comp1_key || c == comp2_key || c == help_key) {
1935 		if (!comp_used || c == comp2_key ||
1936 		    (c == help_key && lastc != c)) {
1937 		    lbuf[i] = NUL;
1938 		    if ((comp_used = CALL(completion) (lbuf, i)) == 0) {
1939 			ding();
1940 			continue;
1941 		    }
1942 		    if (comp_used < 0) {
1943 			comp_used = 0;
1944 			goto no_completion;
1945 		    }
1946 		    comp_len = 0;
1947 		}
1948 		if (c == help_key) {
1949 		    if (CALL(completion) ((char *) NULL, 1)) {
1950 			gotoxy(prompt_length + i, prompt_line);
1951 			fl;
1952 			did_help = 1;
1953 		    }
1954 		    continue;
1955 		}
1956 		if (comp_len) {
1957 		    i -= comp_len;
1958 		    while (--comp_len >= 0)
1959 			tputc(BS);
1960 		    clrline();
1961 		    comp_len = 0;
1962 		}
1963 		switch (CALL(completion) ((char *) NULL, 0)) {
1964 
1965 		    case 0:	/* no possible completion */
1966 			comp_used = 0;
1967 			ding();
1968 			continue;
1969 
1970 		    case 2:	/* whole new alternative */
1971 			while (--i >= 0)
1972 			    tputc(BS);
1973 			clrline();
1974 
1975 			/* FALLTHRU */
1976 		    case 1:	/* completion */
1977 			comp_len = i;
1978 			while ((c = lbuf[i])) {
1979 			    if (i == max)
1980 				break;
1981 			    tputc(c);
1982 			    i++;
1983 			}
1984 			fl;
1985 			comp_len = i - comp_len;
1986 			continue;
1987 		}
1988 	    }
1989 	    if (comp_used) {
1990 		if (!CALL(completion) (lbuf, -(i + 1)) && did_help)
1991 		    clrmsg(i);
1992 		did_help = 0;
1993 		comp_len = comp_used = 0;
1994 	    }
1995 	}
1996 no_completion:
1997 
1998 	if (c == CR || c == NL) {
1999 	    lbuf[i] = NUL;
2000 	    break;
2001 	}
2002 	if (c == erase_key) {
2003 	    if (i <= 0)
2004 		continue;
2005 	    i--;
2006 	    tputc(BS);
2007 	    tputc(' ');
2008 	    tputc(BS);
2009 	    fl;
2010 	    continue;
2011 	}
2012 	if (c == delword_key) {
2013 	    if (i <= 0)
2014 		continue;
2015 	    lbuf[i - 1] = 'X';
2016 	    while (i > 0 && isalnum(lbuf[i - 1])) {
2017 		tputc(BS);
2018 		i--;
2019 	    }
2020 	    clrline();
2021 	    continue;
2022 	}
2023 	if (c == kill_key) {
2024 	    while (i > 0) {
2025 		tputc(BS);
2026 		i--;
2027 	    }
2028 	    clrline();
2029 	    if (hit_count == 1 && dflt) {
2030 		c = comp1_key;
2031 		goto kill_prefill_hack;
2032 	    }
2033 	    continue;
2034 	}
2035 	if (c == K_interrupt) {
2036 	    ret_val = NULL;
2037 	    break;
2038 	}
2039 	if (data_bits == 8) {
2040 	    if (!iso8859(c))
2041 		continue;
2042 	} else if (!isascii(c) || !isprint(c))
2043 	    continue;
2044 
2045 	if (i == max)
2046 	    continue;
2047 
2048 	if (i > 0 && lbuf[i - 1] == '/' && (c == '/' || c == '+')) {
2049 	    if (c != '/' || !guard_double_slash || (i > 1 && lbuf[i - 2] == '/')) {
2050 		if (completion == file_completion) {
2051 		    while (i > 0) {
2052 			tputc(BS);
2053 			i--;
2054 		    }
2055 		    clrline();
2056 		}
2057 	    }
2058 	}
2059 	tputc(c);
2060 	fl;
2061 
2062 	lbuf[i] = c;
2063 	i++;
2064     }
2065 out:
2066     enable_stop = ostop;
2067     do_macro_processing = 1;
2068     return ret_val;
2069 }
2070 
2071 int             list_offset = 0;
2072 
2073 int
list_completion(char * str)2074 list_completion(char *str)
2075 {
2076     static int      cols, line;
2077 
2078     if (str == NULL) {
2079 	cols = Columns;
2080 	line = prompt_line + 1;
2081 	if (line == Lines - 1)
2082 	    cols--;
2083 
2084 	gotoxy(0, line);
2085 	clrpage();
2086 	return 1;
2087     }
2088     str += list_offset;
2089 
2090     for (;;) {
2091 	cols -= strlen(str);
2092 	if (cols >= 0) {
2093 	    tprintf("%s%s", str, cols > 0 ? " " : "");
2094 	    cols--;
2095 	    return 1;
2096 	}
2097 	if (line >= Lines - 1)
2098 	    return 0;
2099 	line++;
2100 	cols = Columns;
2101 	gotoxy(0, line);
2102 	if (line == Lines - 1)
2103 	    cols--;
2104     }
2105 }
2106 
2107 int
yes(int must_answer)2108 yes(int must_answer)
2109 {
2110     int             c, help = 1, in_macro = 0;
2111 
2112     switch (m_yes()) {
2113 	case 0:
2114 	    break;
2115 	case 1:
2116 	    return 0;
2117 	case 2:
2118 	    return 1;
2119 	case 3:
2120 	    do_macro_processing = 0;
2121 	    in_macro++;
2122 	    break;
2123     }
2124     fl;
2125 
2126     for (;;) {
2127 	if (!is_raw) {
2128 	    nn_raw();
2129 	    c = get_c();
2130 	    unset_raw();
2131 	} else
2132 	    c = get_c();
2133 
2134 	if (c == 'y' || c == 'Y') {
2135 	    c = 1;
2136 	    break;
2137 	}
2138 	if (must_answer == 0 && (c == SP || c == CR || c == NL)) {
2139 	    c = 1;
2140 	    break;
2141 	}
2142 	if (c == 'n' || c == 'N') {
2143 	    c = 0;
2144 	    break;
2145 	}
2146 	if (c == K_interrupt) {
2147 	    c = -1;
2148 	    break;
2149 	}
2150 	if (help) {
2151 	    tprintf(" y=YES n=NO");
2152 	    fl;
2153 	    prompt_length += 11;
2154 	    help = 0;
2155 	}
2156     }
2157 
2158     if (in_macro) {
2159 	if (c < 0)
2160 	    m_break();
2161 	do_macro_processing = 1;
2162     }
2163     return c;
2164 }
2165 
2166 void
ding(void)2167 ding(void)
2168 {
2169     BATCH_CHECK_V;
2170 
2171     putp(bell_str);
2172     fl;
2173 }
2174 
2175 
2176 void
display_file(char * name,int modes)2177 display_file(char *name, int modes)
2178 {
2179     FILE           *f;
2180     register int    c, stand_on;
2181     int             linecnt, headln_cnt, hdline, no_conf;
2182     char            headline[128];
2183 
2184     headline[0] = 0;
2185     hdline = 0;
2186     no_conf = 0;
2187 
2188     headln_cnt = -1;
2189 
2190     if (modes & CLEAR_DISPLAY) {
2191 	gotoxy(0, 0);
2192 	clrdisp();
2193     }
2194     linecnt = Lines - 1;
2195 
2196 chain:
2197 
2198     if (*name != '/')
2199 	name = relative(help_directory, name);
2200     f = open_file(name, OPEN_READ);
2201     if (f == NULL)
2202 	tprintf("\r\n\nFile %s is not available\n\n", name);
2203     else {
2204 	stand_on = 0;
2205 
2206 	while ((c = getc(f)) != EOF) {
2207 
2208 #ifdef HAVE_JOBCONTROL
2209 	    if (s_redraw) {
2210 		no_conf = 1;
2211 		break;
2212 	    }
2213 #endif				/* HAVE_JOBCONTROL */
2214 
2215 	    no_conf = 0;
2216 	    if (c == '\1') {
2217 		if (STANDOUT) {
2218 		    putp(stand_on ? exit_standout_mode : enter_standout_mode);
2219 		    curxy_c += cookie_size;
2220 		    stand_on = !stand_on;
2221 		}
2222 		continue;
2223 	    }
2224 	    if (c == '\2') {
2225 		headln_cnt = 0;
2226 		continue;
2227 	    }
2228 	    if (c == '\3') {
2229 		headln_cnt = 0;
2230 		while ((c = getc(f)) != EOF && c != NL)
2231 		    headline[headln_cnt++] = c;
2232 		headline[headln_cnt++] = NUL;
2233 		name = headline;
2234 		fclose(f);
2235 		goto chain;
2236 	    }
2237 	    if (c == '\4') {
2238 		tprintf("%s", version_id);
2239 		continue;
2240 	    }
2241 	    if (headln_cnt >= 0)
2242 		headline[headln_cnt++] = c;
2243 
2244 	    if (hdline) {
2245 		tprintf("%s\r", headline);
2246 		hdline = 0;
2247 		linecnt--;
2248 	    }
2249 	    tputc(c);
2250 	    if (c == NL) {
2251 		tputc(CR);
2252 		if (headln_cnt >= 0) {
2253 		    headline[--headln_cnt] = 0;
2254 		    headln_cnt = -1;
2255 		}
2256 		if (--linecnt == 0) {
2257 		    no_conf = 1;
2258 		    if (any_key(0) == K_interrupt)
2259 			break;
2260 		    linecnt = Lines - 1;
2261 		    if (modes & CLEAR_DISPLAY) {
2262 			gotoxy(0, 0);
2263 			clrdisp();
2264 		    }
2265 		    hdline = headline[0];
2266 		}
2267 	    }
2268 	}
2269 
2270 	if (stand_on) {
2271 	    putp(exit_standout_mode);
2272 	    curxy_c += cookie_size;
2273 	}
2274 	fclose(f);
2275     }
2276 
2277     prompt_line = Lines - 1;	/* move prompt to last line */
2278 
2279     if (!no_conf && (modes & CONFIRMATION))
2280 	any_key(prompt_line);
2281 }
2282 
2283 
2284 void
nn_exitmsg(int n,char * fmt,...)2285 nn_exitmsg(int n, char *fmt,...)
2286 {
2287     va_list         ap;
2288 
2289     if (terminal_speed != 0) {
2290 	clrdisp();
2291 	visual_off();
2292     }
2293     va_start(ap, fmt);
2294     vprintf(fmt, ap);
2295     putchar(NL);
2296     va_end(ap);
2297 
2298     nn_exit(n);
2299     /* NOTREACHED */
2300 }
2301 
2302 static void
push_msg(char * str)2303 push_msg(char *str)
2304 {
2305     register struct msg_list *mp, *newmsg;
2306     static int      slots = 0;
2307 
2308     if (str != NULL) {
2309 	if (slots > message_history) {
2310 	    for (mp = newmsg = msg_stack; mp->prev != NULL; mp = mp->prev)
2311 		newmsg = mp;
2312 	    if (newmsg == mp)
2313 		msg_stack = NULL;
2314 	    else {
2315 		newmsg->prev = NULL;
2316 		newmsg = mp;
2317 	    }
2318 	    freeobj(newmsg->buf);
2319 	} else {
2320 	    slots++;
2321 	    newmsg = newobj(struct msg_list, 1);
2322 	}
2323 	newmsg->buf = copy_str(str);
2324 	newmsg->prev = msg_stack;
2325 	msg_stack = newmsg;
2326     }
2327     msg_ptr = msg_stack;
2328 }
2329 
2330 void
vmsg(char * fmt,va_list ap)2331 vmsg(char *fmt, va_list ap)
2332 {
2333     char           *errmsg;
2334 
2335     if (fmt) {
2336 	char            lbuf[512];
2337 
2338 	vsprintf(lbuf, fmt, ap);
2339 	push_msg(lbuf);
2340     }
2341     if (msg_ptr) {
2342 	errmsg = msg_ptr->buf;
2343 	msg_ptr = msg_ptr->prev;
2344     } else {
2345 	errmsg = "(no more messages)";
2346 	msg_ptr = msg_stack;
2347     }
2348 
2349     if (terminal_speed == 0) {
2350 	tprintf("%s\n", errmsg);
2351 	fl;
2352 	return;
2353     }
2354     gotoxy(0, Lines - 1);
2355     tprintf("%s", errmsg);
2356     clrline();
2357     any_message = 1;
2358 
2359     if (prompt_line != Lines - 1)
2360 	gotoxy(prompt_length, prompt_line);
2361     fl;
2362 }
2363 
2364 void
msg(char * fmt,...)2365 msg(char *fmt,...)
2366 {
2367     va_list         ap;
2368 
2369     va_start(ap, fmt);
2370     vmsg(fmt, ap);
2371     va_end(ap);
2372 }
2373 
2374 void
clrmsg(int col)2375 clrmsg(int col)
2376 {
2377     BATCH_CHECK_V;
2378 
2379     gotoxy(0, prompt_line + 1);
2380     clrpage();
2381     if (col >= 0)
2382 	gotoxy(prompt_length + col, prompt_line);
2383     fl;
2384     any_message = 0;
2385 }
2386 
2387 
2388 void
prompt(char * fmt,...)2389 prompt(char *fmt,...)
2390 {
2391     va_list         ap;
2392     register char  *cp;
2393     int             stand_on;
2394     static char     cur_p[FILENAME];
2395     static char     saved_p[FILENAME];
2396 
2397     BATCH_CHECK_V;
2398 
2399     va_start(ap, fmt);
2400 
2401     if (fmt == P_VERSION) {
2402 	gotoxy(0, prompt_line + 1);
2403 	tprintf("Release %s ", version_id);
2404 	clrline();
2405 	any_message++;
2406 
2407 	if (prompt_line >= 0)
2408 	    gotoxy(prompt_length, prompt_line);
2409 	goto out;
2410     }
2411     if (fmt == P_SAVE) {
2412 	strcpy(saved_p, cur_p);
2413 	goto out;
2414     }
2415     if (fmt == P_RESTORE)
2416 	strcpy(cur_p, saved_p);
2417 
2418     if (prompt_line >= 0)
2419 	gotoxy(0, prompt_line);
2420 
2421     if (fmt == P_MOVE) {
2422 	clrline();
2423 	goto out;
2424     }
2425     if (fmt != P_REDRAW && fmt != P_RESTORE)
2426 	vsprintf(cur_p, fmt, ap);
2427 
2428     tputc(CR);
2429 
2430     for (cp = cur_p, stand_on = 0, prompt_length = 0; *cp; cp++) {
2431 	if (*cp == '\1') {
2432 	    if (cp[1] != '\1') {
2433 		if (STANDOUT) {
2434 		    stand_on = !stand_on;
2435 		    nnstandout(stand_on);
2436 		    prompt_length += cookie_size;
2437 		}
2438 		continue;
2439 	    }
2440 	    cp++;
2441 	} else if (*cp == '\2') {
2442 	    time_t          t;
2443 	    char           *timestr;
2444 
2445 #ifdef STATISTICS
2446 	    t = tick_usage();
2447 #else
2448 	    t = cur_time();
2449 #endif
2450 
2451 	    if (show_current_time) {
2452 		timestr = ctime(&t) + 11;
2453 		timestr[5] = NUL;
2454 
2455 		tprintf("-- %s ", timestr);
2456 		prompt_length += 9;
2457 	    }
2458 	    if (unread_mail(t)) {
2459 		tprintf("Mail ");
2460 		prompt_length += 5;
2461 	    }
2462 	    continue;
2463 	}
2464 	tputc(*cp);
2465 	prompt_length++;
2466     }
2467     if (stand_on) {
2468 	nnstandout(0);
2469 	prompt_length += cookie_size;
2470     }
2471     clrline();
2472 
2473     if (fmt == P_RESTORE)
2474 	restore_xy();
2475 
2476 #if notdef
2477     else
2478 	curxy_c = -1;
2479 #endif
2480 
2481 out:
2482     va_end(ap);
2483 }
2484 
2485 
2486 int
any_key(int line)2487 any_key(int line)
2488 {
2489     int             was_raw, c, dmp;
2490 
2491     BATCH_CHECK;
2492 
2493     was_raw = is_raw;
2494     if (!is_raw)
2495 	nn_raw();
2496     if (line == 0)
2497 	line = -1;
2498     else if (line < 0)
2499 	line = Lines + line;
2500 
2501     if (line != 10000)
2502 	so_printxy(0, line, "Hit any key to continue");
2503 
2504     clrline();
2505 
2506     dmp = do_macro_processing;
2507     do_macro_processing = 0;
2508     c = get_c();
2509     if (c == 'q' || c == 'Q')
2510 	c = K_interrupt;
2511     do_macro_processing = dmp;
2512 
2513     if (!was_raw)
2514 	unset_raw();
2515 
2516     return c;
2517 }
2518 
2519 
2520 static int      pg_fline, pg_width, pg_maxw, pg_line, pg_col, pg_quit;
2521 regexp         *pg_regexp = NULL;
2522 int             pg_new_regexp = 0;
2523 
2524 void
pg_init(int first_line,int cols)2525 pg_init(int first_line, int cols)
2526 {
2527     if (pg_regexp) {
2528 	freeobj(pg_regexp);
2529 	pg_regexp = NULL;
2530     }
2531     pg_new_regexp = 0;
2532 
2533     pg_fline = first_line;
2534     pg_line = pg_fline - 1;
2535     pg_quit = pg_col = 0;
2536     pg_width = Columns / cols;
2537     pg_maxw = pg_width * (cols - 1);
2538 }
2539 
2540 int
pg_scroll(int n)2541 pg_scroll(int n)
2542 {
2543     pg_line += n;
2544     if (pg_line >= (Lines - 1)) {
2545 	pg_line = 0;
2546 	if (any_key(0) == K_interrupt)
2547 	    return 1;
2548 	tputc(CR);
2549 	clrline();
2550     }
2551     return 0;
2552 }
2553 
2554 int
pg_next(void)2555 pg_next(void)
2556 {
2557     int             c;
2558 
2559     if (batch_mode) {
2560 	putchar(NL);
2561 	return 0;
2562     }
2563     pg_line++;
2564     if (pg_line < Lines) {
2565 	gotoxy(pg_col, pg_line);
2566 	if (pg_line == Lines - 1 && pg_col == pg_maxw) {
2567 	    c = any_key(0);
2568 	    if (c == '/') {
2569 		char           *expr;
2570 		tputc(CR);
2571 		tputc('/');
2572 		clrline();
2573 		expr = get_s((char *) NULL, (char *) NULL, (char *) NULL, NULL_FCT);
2574 		if (expr != NULL && *expr != NUL) {
2575 		    freeobj(pg_regexp);
2576 		    pg_regexp = regcomp(expr);
2577 		    pg_new_regexp = 1;
2578 		}
2579 	    }
2580 	    gotoxy(0, pg_fline);
2581 	    clrpage();
2582 	    pg_col = 0;
2583 	    pg_line = pg_fline;
2584 	    if (c == K_interrupt) {
2585 		pg_quit = 1;
2586 		return -1;
2587 	    }
2588 	    return 1;
2589 	}
2590     } else {
2591 	pg_line = pg_fline;
2592 	pg_col += pg_width;
2593 	gotoxy(pg_col, pg_line);
2594     }
2595     return 0;
2596 }
2597 
2598 void
pg_indent(int pos)2599 pg_indent(int pos)
2600 {
2601     BATCH_CHECK_V;
2602 
2603     gotoxy(pg_col + pos, pg_line);
2604 }
2605 
2606 int
pg_end(void)2607 pg_end(void)
2608 {
2609     int             c;
2610 
2611     if (pg_quit == 0 && pg_next() == 0)
2612 	c = any_key(0);
2613     else
2614 	c = K_interrupt;
2615 
2616     if (pg_regexp) {
2617 	freeobj(pg_regexp);
2618 	pg_regexp = NULL;
2619     }
2620     return c == K_interrupt ? -1 : 0;
2621 }
2622 
2623 void
user_delay(int ticks)2624 user_delay(int ticks)
2625 {
2626     BATCH_CHECK_V;
2627 
2628     if (ticks <= 0 || conf_dont_sleep) {
2629 	tprintf(" <>");
2630 	any_key(10000);
2631     } else {
2632 	fl;
2633 	sleep((unsigned) ticks);
2634     }
2635 }
2636