1 /* term.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4
5
6 #include "EXTERN.h"
7 #include "common.h"
8 #include "list.h"
9 #include "env.h"
10 #include "util.h"
11 #include "util2.h"
12 #include "final.h"
13 #include "help.h"
14 #include "hash.h"
15 #include "cache.h"
16 #include "opt.h"
17 #include "ngdata.h"
18 #include "nntpclient.h"
19 #include "datasrc.h"
20 #include "intrp.h"
21 #include "init.h"
22 #include "art.h"
23 #include "rt-select.h"
24 #ifdef SCORE
25 #include "score.h" /* for sc_lookahead */
26 #endif
27 #ifdef SCAN
28 #include "scan.h"
29 #include "sdisp.h"
30 #endif
31 #ifdef SCAN_ART
32 #include "scanart.h"
33 #endif
34 #ifdef USE_TK
35 #include "tkstuff.h"
36 #endif
37 #include "univ.h"
38 #include "color.h"
39 #include "INTERN.h"
40 #include "term.h"
41 #include "term.ih"
42
43 #ifdef u3b2
44 #undef TIOCGWINSZ
45 #endif
46
47 #undef USETITE /* use terminal init/exit seqences (not recommended) */
48 #undef USEKSKE /* use keypad start/end sequences */
49
50 char tcarea[TCSIZE]; /* area for "compiled" termcap strings */
51
52 static KEYMAP* topmap INIT(NULL);
53
54 static char* lines_export = NULL;
55 static char* cols_export = NULL;
56
57 static int leftcost, upcost;
58 static bool got_a_char = FALSE; /* TRUE if we got a char since eating */
59
60 /* guarantee capability pointer != NULL */
61 /* (I believe terminfo will ignore the &tmpaddr argument.) */
62
63 char* tgetstr();
64 #define Tgetstr(key) ((tmpstr = tgetstr(key,&tmpaddr)) ? tmpstr : nullstr)
65
66 /* terminal initialization */
67
68 void
term_init()69 term_init()
70 {
71 savetty(); /* remember current tty state */
72
73 #ifdef I_TERMIO
74 outspeed = _tty.c_cflag & CBAUD; /* for tputs() */
75 ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */
76 KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */
77 if (tc_GT = ((_tty.c_oflag & TABDLY) != TAB3))
78 /* we have tabs, so that's OK */;
79 else
80 _tty.c_oflag &= ~TAB3; /* turn off kernel tabbing -- done in rn */
81 #else /* !I_TERMIO */
82 # ifdef I_TERMIOS
83 outspeed = cfgetospeed(&_tty); /* for tputs() (output) */
84 ERASECH = _tty.c_cc[VERASE]; /* for finish_command() */
85 KILLCH = _tty.c_cc[VKILL]; /* for finish_command() */
86 #if 0
87 _tty.c_oflag &= ~OXTABS; /* turn off kernel tabbing-done in rn */
88 #endif
89 # else /* !I_TERMIOS */
90 # ifdef I_SGTTY
91 outspeed = _tty.sg_ospeed; /* for tputs() */
92 ERASECH = _tty.sg_erase; /* for finish_command() */
93 KILLCH = _tty.sg_kill; /* for finish_command() */
94 if (tc_GT = ((_tty.sg_flags & XTABS) != XTABS))
95 /* we have tabs, so that's OK */;
96 else
97 _tty.sg_flags &= ~XTABS;
98 # else /* !I_SGTTY */
99 # ifdef MSDOS
100 outspeed = B19200;
101 ERASECH = '\b';
102 KILLCH = Ctl('u');
103 tc_GT = 1;
104 # else
105 ..."Don't know how to initialize the terminal!"
106 # endif /* !MSDOS */
107 # endif /* !I_SGTTY */
108 # endif /* !I_TERMIOS */
109 #endif /* !I_TERMIO */
110
111 /* The following could be a table but I can't be sure that there isn't */
112 /* some degree of sparsity out there in the world. */
113
114 switch (outspeed) { /* 1 second of padding */
115 #ifdef BEXTA
116 case BEXTA: just_a_sec = 1920; break;
117 #else
118 #ifdef B19200
119 case B19200: just_a_sec = 1920; break;
120 #endif
121 #endif
122 case B9600: just_a_sec = 960; break;
123 case B4800: just_a_sec = 480; break;
124 case B2400: just_a_sec = 240; break;
125 case B1800: just_a_sec = 180; break;
126 case B1200: just_a_sec = 120; break;
127 case B600: just_a_sec = 60; break;
128 case B300: just_a_sec = 30; break;
129 /* do I really have to type the rest of this??? */
130 case B200: just_a_sec = 20; break;
131 case B150: just_a_sec = 15; break;
132 case B134: just_a_sec = 13; break;
133 case B110: just_a_sec = 11; break;
134 case B75: just_a_sec = 8; break;
135 case B50: just_a_sec = 5; break;
136 default: just_a_sec = 960; break;
137 /* if we are running detached I */
138 } /* don't want to know about it! */
139 }
140
141 #ifdef PENDING
142 # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS)
143 int devtty;
144 # endif
145 #endif
146
147 /* set terminal characteristics */
148
149 void
term_set(tcbuf)150 term_set(tcbuf)
151 char* tcbuf; /* temp area for "uncompiled" termcap entry */
152 {
153 char* tmpaddr; /* must not be register */
154 register char* tmpstr;
155 char* s;
156 int status;
157 #ifdef TIOCGWINSZ
158 struct winsize winsize;
159 #endif
160
161 #ifdef PENDING
162 #if !defined (FIONREAD) && !defined (HAS_RDCHK) && !defined(MSDOS)
163 /* do no delay reads on something that always gets closed on exit */
164
165 devtty = fileno(stdin);
166 if (isatty(devtty)) {
167 devtty = open("/dev/tty",0);
168 if (devtty < 0) {
169 printf(cantopen,"/dev/tty") FLUSH;
170 finalize(1);
171 }
172 fcntl(devtty,F_SETFL,O_NDELAY);
173 }
174 #endif
175 #endif
176
177 /* get all that good termcap stuff */
178
179 #ifdef HAS_TERMLIB
180 #ifdef MSDOS
181 tc_BC = "\b";
182 tc_UP = "\033[A";
183 tc_CR = "\r";
184 tc_VB = nullstr;
185 tc_CL = "\033[H\033[2J";
186 tc_CE = "\033[K";
187 tc_TI = nullstr;
188 tc_TE = nullstr;
189 tc_KS = nullstr;
190 tc_KE = nullstr;
191 tc_CM = "\033[%d;%dH";
192 tc_HO = "\033[H";
193 tc_IL = nullstr; /*$$*/
194 tc_CD = nullstr; /*$$*/
195 tc_SO = "\033[7m";
196 tc_SE = "\033[m";
197 tc_US = "\033[7m";
198 tc_UE = "\033[m";
199 tc_UC = nullstr;
200 set_lines_and_cols();
201 tc_AM = TRUE;
202 #else
203 s = getenv("TERM");
204 status = tgetent(tcbuf,s? s : "dumb"); /* get termcap entry */
205 if (status < 1) {
206 printf("No termcap %s found.\n", status ? "file" : "entry") FLUSH;
207 finalize(1);
208 }
209 tmpaddr = tcarea; /* set up strange tgetstr pointer */
210 s = Tgetstr("pc"); /* get pad character */
211 tc_PC = *s; /* get it where tputs wants it */
212 if (!tgetflag("bs")) { /* is backspace not used? */
213 tc_BC = Tgetstr("bc"); /* find out what is */
214 if (tc_BC == nullstr) { /* terminfo grok's 'bs' but not 'bc' */
215 tc_BC = Tgetstr("le");
216 if (tc_BC == nullstr)
217 tc_BC = "\b"; /* better than nothing... */
218 }
219 } else
220 tc_BC = "\b"; /* make a backspace handy */
221 tc_UP = Tgetstr("up"); /* move up a line */
222 tc_CL = Tgetstr("cl"); /* get clear string */
223 tc_CE = Tgetstr("ce"); /* clear to end of line string */
224 tc_TI = Tgetstr("ti"); /* initialize display */
225 tc_TE = Tgetstr("te"); /* reset display */
226 tc_KS = Tgetstr("ks"); /* enter `keypad transmit' mode */
227 tc_KE = Tgetstr("ke"); /* exit `keypad transmit' mode */
228 tc_HO = Tgetstr("ho"); /* home cursor */
229 tc_IL = Tgetstr("al"); /* insert (add) line */
230 tc_CM = Tgetstr("cm"); /* cursor motion */
231 tc_CD = Tgetstr("cd"); /* clear to end of display */
232 if (!*tc_CE)
233 tc_CE = tc_CD;
234 tc_SO = Tgetstr("so"); /* begin standout */
235 tc_SE = Tgetstr("se"); /* end standout */
236 if ((tc_SG = tgetnum("sg"))<0)
237 tc_SG = 0; /* blanks left by SG, SE */
238 tc_US = Tgetstr("us"); /* start underline */
239 tc_UE = Tgetstr("ue"); /* end underline */
240 if ((tc_UG = tgetnum("ug"))<0)
241 tc_UG = 0; /* blanks left by US, UE */
242 if (*tc_US)
243 tc_UC = nullstr; /* UC must not be NULL */
244 else
245 tc_UC = Tgetstr("uc"); /* underline a character */
246 if (!*tc_US && !*tc_UC) { /* no underline mode? */
247 tc_US = tc_SO; /* substitute standout mode */
248 tc_UE = tc_SE;
249 tc_UG = tc_SG;
250 }
251 tc_LINES = tgetnum("li"); /* lines per page */
252 tc_COLS = tgetnum("co"); /* columns on page */
253
254 #ifdef TIOCGWINSZ
255 { struct winsize ws;
256 if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) {
257 tc_LINES = ws.ws_row;
258 tc_COLS = ws.ws_col;
259 }
260 }
261 #endif
262
263 tc_AM = tgetflag("am"); /* terminal wraps automatically? */
264 tc_XN = tgetflag("xn"); /* then eats next newline? */
265 tc_VB = Tgetstr("vb");
266 if (!*tc_VB)
267 tc_VB = "\007";
268 tc_CR = Tgetstr("cr");
269 if (!*tc_CR) {
270 if (tgetflag("nc") && *tc_UP) {
271 tc_CR = safemalloc((MEM_SIZE)strlen(tc_UP)+2);
272 sprintf(tc_CR,"%s\r",tc_UP);
273 }
274 else
275 tc_CR = "\r";
276 }
277 #ifdef TIOCGWINSZ
278 if (ioctl(1, TIOCGWINSZ, &winsize) >= 0) {
279 if (winsize.ws_row > 0)
280 tc_LINES = winsize.ws_row;
281 if (winsize.ws_col > 0)
282 tc_COLS = winsize.ws_col;
283 }
284 # endif
285 #endif
286 if (!*tc_UP) /* no UP string? */
287 marking = 0; /* disable any marking */
288 if (*tc_CM || *tc_HO)
289 can_home = TRUE;
290 if (!*tc_CD || !can_home) /* can we CE, CD, and home? */
291 erase_each_line = FALSE; /* no, so disable use of clear eol */
292 if (muck_up_clear) /* this is for weird HPs */
293 tc_CL = NULL;
294 leftcost = strlen(tc_BC);
295 upcost = strlen(tc_UP);
296 #else /* !HAS_TERMLIB */
297 ..."Don't know how to set the terminal!"
298 #endif /* !HAS_TERMLIB */
299 termlib_init();
300 line_col_calcs();
301 noecho(); /* turn off echo */
302 crmode(); /* enter cbreak mode */
303 sprintf(buf, "%d", tc_LINES);
304 lines_export = export("LINES",buf);
305 sprintf(buf, "%d", tc_COLS);
306 cols_export = export("COLUMNS",buf);
307
308 mac_init(tcbuf);
309 }
310
311 #ifdef MSDOS
312 static void
set_lines_and_cols()313 set_lines_and_cols()
314 {
315 gotoxy(132,1);
316 if (wherex() == 132)
317 tc_COLS = 132;
318 else
319 tc_COLS = 80;
320 gotoxy(1,50);
321 if (wherey() == 50)
322 tc_LINES = 50;
323 else {
324 gotoxy(1,43);
325 if (wherey() == 50)
326 tc_LINES = 50;
327 else
328 tc_LINES = 25;
329 }
330 }
331 #endif
332
333 void
set_macro(seq,def)334 set_macro(seq,def)
335 char* seq; /* input sequence of keys */
336 char* def; /* definition */
337 {
338 mac_line(def,seq,0);
339 /* check for common (?) brain damage: ku/kd/etc sequence may be the
340 * cursor move sequence instead of the input sequence.
341 * (This happens on the local xterm definitions.)
342 * Try to recognize and adjust for this case.
343 */
344 if (seq[0] == '\033' && seq[1] == '[' && seq[2]) {
345 char lbuf[LBUFLEN]; /* copy of possibly non-writable string */
346 strcpy(lbuf,seq);
347 lbuf[1] = 'O';
348 mac_line(def,lbuf,0);
349 }
350 if (seq[0] == '\033' && seq[1] == 'O' && seq[2]) {
351 char lbuf[LBUFLEN]; /* copy of possibly non-writable string */
352 strcpy(lbuf,seq);
353 lbuf[1] = '[';
354 mac_line(def,lbuf,0);
355 }
356 }
357
358 char* up[] = {
359 "^@",
360 /* '(' at article or pager, '[' in thread sel, 'p' otherwise */
361 "%(%m=[ap]?\\(:%(%m=t?[:p))",
362 /* '(' at article or pager, '[' in thread sel, 'p' otherwise */
363 "%(%m=[ap]?\\(:%(%m=t?[:p))"
364 };
365 char* down[] = {
366 "^@",
367 /* ')' at article or pager, ']' in thread sel, 'n' otherwise */
368 "%(%m=[ap]?\\):%(%m=t?]:n))",
369 /* ')' at article or pager, ']' in thread sel, 'n' otherwise */
370 "%(%m=[ap]?\\):%(%m=t?]:n))"
371 };
372 char* left[] = {
373 "^@",
374 /* '[' at article or pager, 'Q' otherwise */
375 "%(%m=[ap]?\\[:Q)",
376 /* '[' at article or pager, '<' otherwise */
377 "%(%m=[ap]?\\[:<)"
378 };
379 char* right[] = {
380 "^@",
381 /* ']' at article or pager, CR otherwise */
382 "%(%m=[ap]?\\]:^j)",
383 /* CR at newsgroups, ']' at article or pager, '>' otherwise */
384 "%(%m=n?^j:%(%m=[ap]?\\]:>))"
385 };
386
387 /* Turn the arrow keys into macros that do some basic trn functions.
388 ** Code provided by Clifford Adams.
389 */
390 void
arrow_macros(tmpbuf)391 arrow_macros(tmpbuf)
392 char* tmpbuf;
393 {
394 #ifdef HAS_TERMLIB
395 char lbuf[256]; /* should be long enough */
396 #ifndef MSDOS
397 char* tmpaddr = tmpbuf;
398 #endif
399 register char* tmpstr;
400
401 /* If arrows are defined as single keys, we probably don't
402 * want to redefine them. (The tvi912c defines kl as ^H)
403 */
404 #ifdef MSDOS
405 strcpy(lbuf,"\035\110");
406 #else
407 strcpy(lbuf,Tgetstr("ku")); /* up */
408 #endif
409 if ((int)strlen(lbuf) > 1)
410 set_macro(lbuf,up[auto_arrow_macros]);
411
412 #ifdef MSDOS
413 strcpy(lbuf,"\035\120");
414 #else
415 strcpy(lbuf,Tgetstr("kd")); /* down */
416 #endif
417 if ((int)strlen(lbuf) > 1)
418 set_macro(lbuf,down[auto_arrow_macros]);
419
420 #ifdef MSDOS
421 strcpy(lbuf,"\035\113");
422 #else
423 strcpy(lbuf,Tgetstr("kl")); /* left */
424 #endif
425 if ((int)strlen(lbuf) > 1)
426 set_macro(lbuf,left[auto_arrow_macros]);
427
428 #ifdef MSDOS
429 strcpy(lbuf,"\035\115");
430 #else
431 strcpy(lbuf,Tgetstr("kr")); /* right */
432 #endif
433 if ((int)strlen(lbuf) > 1)
434 set_macro(lbuf,right[auto_arrow_macros]);
435
436 if (*lbuf == '\033')
437 set_macro("\033\033", "\033");
438 #endif
439 }
440
441 static void
mac_init(tcbuf)442 mac_init(tcbuf)
443 char* tcbuf;
444 {
445 char tmpbuf[1024];
446
447 if (auto_arrow_macros)
448 arrow_macros(tmpbuf);
449 if (!use_threads
450 || (tmpfp = fopen(filexp(getval("TRNMACRO",TRNMACRO)),"r")) == NULL)
451 tmpfp = fopen(filexp(getval("RNMACRO",RNMACRO)),"r");
452 if (tmpfp) {
453 while (fgets(tcbuf,TCBUF_SIZE,tmpfp) != NULL)
454 mac_line(tcbuf,tmpbuf,sizeof tmpbuf);
455 fclose(tmpfp);
456 }
457 }
458
459 void
mac_line(line,tmpbuf,tbsize)460 mac_line(line,tmpbuf,tbsize)
461 char* line;
462 char* tmpbuf;
463 int tbsize;
464 {
465 register char* s;
466 register char* m;
467 register KEYMAP* curmap;
468 register int ch;
469 register int garbage = 0;
470 static char override[] = "\nkeymap overrides string\n";
471
472 if (topmap == NULL)
473 topmap = newkeymap();
474 if (*line == '#' || *line == '\n')
475 return;
476 if (line[ch = strlen(line)-1] == '\n')
477 line[ch] = '\0';
478 /* A 0 length signifies we already parsed the macro into tmpbuf,
479 ** so line is just the definition. */
480 if (tbsize)
481 m = dointerp(tmpbuf,tbsize,line," \t",(char*)NULL);
482 else
483 m = line;
484 if (!*m)
485 return;
486 while (*m == ' ' || *m == '\t') m++;
487 for (s=tmpbuf,curmap=topmap; *s; s++) {
488 ch = *s & 0177;
489 if (s[1] == '+' && isdigit(s[2])) {
490 s += 2;
491 garbage = (*s & KM_GMASK) << KM_GSHIFT;
492 }
493 else
494 garbage = 0;
495 if (s[1]) {
496 if ((curmap->km_type[ch] & KM_TMASK) == KM_STRING) {
497 if (tbsize) {
498 fputs(override,stdout) FLUSH;
499 termdown(2);
500 }
501 free(curmap->km_ptr[ch].km_str);
502 curmap->km_ptr[ch].km_str = NULL;
503 }
504 curmap->km_type[ch] = KM_KEYMAP + garbage;
505 if (curmap->km_ptr[ch].km_km == NULL)
506 curmap->km_ptr[ch].km_km = newkeymap();
507 curmap = curmap->km_ptr[ch].km_km;
508 }
509 else {
510 if (tbsize && (curmap->km_type[ch] & KM_TMASK) == KM_KEYMAP) {
511 fputs(override,stdout) FLUSH;
512 termdown(2);
513 }
514 else {
515 curmap->km_type[ch] = KM_STRING + garbage;
516 curmap->km_ptr[ch].km_str = savestr(m);
517 }
518 }
519 }
520 }
521
522 static KEYMAP*
newkeymap()523 newkeymap()
524 {
525 register int i;
526 register KEYMAP* map;
527
528 #ifndef lint
529 map = (KEYMAP*)safemalloc(sizeof(KEYMAP));
530 #else
531 map = NULL;
532 #endif /* lint */
533 for (i = 127; i >= 0; i--) {
534 map->km_ptr[i].km_km = NULL;
535 map->km_type[i] = KM_NOTHIN;
536 }
537 return map;
538 }
539
540 void
show_macros()541 show_macros()
542 {
543 char prebuf[64];
544
545 if (topmap != NULL) {
546 print_lines("Macros:\n",STANDOUT);
547 *prebuf = '\0';
548 show_keymap(topmap,prebuf);
549 }
550 else {
551 print_lines("No macros defined.\n", NOMARKING);
552 }
553 }
554
555 static void
show_keymap(curmap,prefix)556 show_keymap(curmap,prefix)
557 register KEYMAP* curmap;
558 char* prefix;
559 {
560 register int i;
561 register char* next = prefix + strlen(prefix);
562 register int kt;
563
564 for (i = 0; i < 128; i++) {
565 if ((kt = curmap->km_type[i]) != 0) {
566 if (i < ' ')
567 sprintf(next,"^%c",i+64);
568 else if (i == ' ')
569 strcpy(next,"\\040");
570 else if (i == 127)
571 strcpy(next,"^?");
572 else
573 sprintf(next,"%c",i);
574 if ((kt >> KM_GSHIFT) & KM_GMASK) {
575 sprintf(cmd_buf,"+%d", (kt >> KM_GSHIFT) & KM_GMASK);
576 strcat(next,cmd_buf);
577 }
578 switch (kt & KM_TMASK) {
579 case KM_NOTHIN:
580 sprintf(cmd_buf,"%s %c\n",prefix,i);
581 print_lines(cmd_buf,NOMARKING);
582 break;
583 case KM_KEYMAP:
584 show_keymap(curmap->km_ptr[i].km_km, prefix);
585 break;
586 case KM_STRING:
587 sprintf(cmd_buf,"%s %s\n",prefix,curmap->km_ptr[i].km_str);
588 print_lines(cmd_buf,NOMARKING);
589 break;
590 case KM_BOGUS:
591 sprintf(cmd_buf,"%s BOGUS\n",prefix);
592 print_lines(cmd_buf,STANDOUT);
593 break;
594 }
595 }
596 }
597 }
598
599 void
set_mode(new_gmode,new_mode)600 set_mode(new_gmode, new_mode)
601 char_int new_gmode;
602 char_int new_mode;
603 {
604 if (gmode != new_gmode || mode != new_mode) {
605 gmode = new_gmode;
606 mode = new_mode;
607 xmouse_check();
608 }
609 }
610
611 /* routine to pass to tputs */
612
613 int
putchr(ch)614 putchr(ch)
615 register char_int ch;
616 {
617 putchar(ch);
618 #ifdef lint
619 ch = '\0';
620 ch = ch;
621 #endif
622 return 0;
623 }
624
625 int not_echoing = 0;
626
627 void
hide_pending()628 hide_pending()
629 {
630 not_echoing = 1;
631 pushchar(0200);
632 }
633
634 bool
finput_pending(check_term)635 finput_pending(check_term)
636 bool_int check_term;
637 {
638 while (nextout != nextin) {
639 if (circlebuf[nextout] != '\200')
640 return 1;
641 switch (not_echoing) {
642 case 0:
643 return 1;
644 case 1:
645 nextout++;
646 nextout %= PUSHSIZE;
647 not_echoing = 0;
648 break;
649 default:
650 circlebuf[nextout] = '\n';
651 not_echoing = 0;
652 return 1;
653 }
654 }
655 #ifdef PENDING
656 #ifdef USE_TK
657 if (check_term && ttk_running) {
658 ttk_do_waiting_events(); /* Update screen, process events. */
659 if (ttk_keys && *ttk_keys)
660 return 1;
661 }
662 #endif
663 if (check_term) {
664 # ifdef FIONREAD
665 int iocount;
666 ioctl(0, FIONREAD, &iocount);
667 return iocount;
668 # else /* !FIONREAD */
669 # ifdef HAS_RDCHK
670 return rdchk(0);
671 # else /* !HAS_RDCHK */
672 # ifdef MSDOS
673 return kbhit();
674 # else /* !MSDOS */
675 return circfill();
676 # endif /* !MSDOS */
677 # endif /* !HAS_RDCHK */
678 # endif /* !FIONREAD */
679 }
680 # endif /* !PENDING */
681 return 0;
682 }
683
684 /* input the 2nd and succeeding characters of a multi-character command */
685 /* returns TRUE if command finished, FALSE if they rubbed out first character */
686
687 int buflimit = LBUFLEN;
688
689 bool
finish_command(donewline)690 finish_command(donewline)
691 int donewline;
692 {
693 register char* s;
694 char gmode_save = gmode;
695
696 s = buf;
697 if (s[1] != FINISHCMD) /* someone faking up a command? */
698 return TRUE;
699 set_mode('i',mode);
700 if (not_echoing)
701 not_echoing = 2;
702 do {
703 s = edit_buf(s, buf);
704 if (s == buf) { /* entire string gone? */
705 fflush(stdout); /* return to single char command mode */
706 set_mode(gmode_save,mode);
707 return FALSE;
708 }
709 if (s - buf == buflimit)
710 break;
711 fflush(stdout);
712 getcmd(s);
713 if (errno || *s == '\f') {
714 *s = Ctl('r'); /* force rewrite on CONT */
715 }
716 } while (*s != '\r' && *s != '\n'); /* until CR or NL (not echoed) */
717 mouse_is_down = FALSE;
718
719 while (s[-1] == ' ') s--;
720 *s = '\0'; /* terminate the string nicely */
721
722 if (donewline)
723 newline();
724
725 set_mode(gmode_save,mode);
726 return TRUE; /* retrn success */
727 }
728
729 static int
echo_char(ch)730 echo_char(ch)
731 char_int ch;
732 {
733 if (((Uchar)ch & 0x7F) < ' ') {
734 putchar('^');
735 putchar((ch & 0x7F) | 64);
736 return 2;
737 }
738 if (ch == '\177') {
739 putchar('^');
740 putchar('?');
741 return 2;
742 }
743 putchar(ch);
744 return 1;
745 }
746
747 static bool screen_is_dirty; /*$$ remove this? */
748
749 /* Process the character *s in the buffer buf returning the new 's' */
750
751 char*
edit_buf(s,cmd)752 edit_buf(s, cmd)
753 register char* s;
754 char* cmd;
755 {
756 static bool quoteone = FALSE;
757 if (quoteone) {
758 quoteone = FALSE;
759 if (s != buf)
760 goto echo_it;
761 }
762 if (*s == '\033') { /* substitution desired? */
763 #ifdef ESCSUBS
764 char tmpbuf[4], *cpybuf;
765
766 tmpbuf[0] = '%';
767 read_tty(&tmpbuf[1],1);
768 #ifdef RAWONLY
769 tmpbuf[1] &= 0177;
770 #endif
771 tmpbuf[2] = '\0';
772 if (tmpbuf[1] == 'h') {
773 (void) help_subs();
774 *s = '\0';
775 reprint();
776 }
777 else if (tmpbuf[1] == '\033') {
778 *s = '\0';
779 cpybuf = savestr(buf);
780 interpsearch(buf, sizeof buf, cpybuf, cmd);
781 free(cpybuf);
782 s = buf + strlen(buf);
783 reprint();
784 }
785 else {
786 interpsearch(s, sizeof buf - (s-buf), tmpbuf, cmd);
787 fputs(s,stdout);
788 s += strlen(s);
789 }
790 #else
791 notincl("^[");
792 *s = '\0';
793 reprint();
794 #endif
795 return s;
796 }
797 else if (*s == ERASECH) { /* they want to rubout a char? */
798 if (s != buf) {
799 rubout();
800 s--; /* discount the char rubbed out */
801 if (!AT_NORM_CHAR(s))
802 rubout();
803 }
804 return s;
805 }
806 else if (*s == KILLCH) { /* wipe out the whole line? */
807 while (s != buf) { /* emulate that many ERASEs */
808 rubout();
809 s--;
810 if (!AT_NORM_CHAR(s))
811 rubout();
812 }
813 return s;
814 }
815 #ifdef WORDERASE
816 else if (*s == Ctl('w')) { /* wipe out one word? */
817 if (s == buf)
818 return s;
819 *s-- = ' ';
820 while (!isspace(*s) || isspace(s[1])) {
821 rubout();
822 if (!AT_NORM_CHAR(s))
823 rubout();
824 if (s == buf)
825 return buf;
826 s--;
827 }
828 return s+1;
829 }
830 #endif
831 else if (*s == Ctl('r')) {
832 *s = '\0';
833 reprint();
834 return s;
835 }
836 else if (*s == Ctl('v')) {
837 putchar('^');
838 backspace();
839 fflush(stdout);
840 getcmd(s);
841 }
842 else if (*s == '\\')
843 quoteone = TRUE;
844
845 echo_it:
846 if (!not_echoing)
847 echo_char(*s);
848 return s+1;
849 }
850
851 bool
finish_dblchar()852 finish_dblchar()
853 {
854 bool ret;
855 int buflimit_save = buflimit;
856 int not_echoing_save = not_echoing;
857 buflimit = 2;
858 ret = finish_command(FALSE);
859 buflimit = buflimit_save;
860 not_echoing = not_echoing_save;
861 return ret;
862 }
863
864 /* discard any characters typed ahead */
865
866 void
eat_typeahead()867 eat_typeahead()
868 {
869 static double last_time = 0.;
870 double this_time = current_time();
871
872 /* do not eat typeahead while creating virtual group */
873 if (univ_ng_virtflag)
874 return;
875 /* Don't eat twice before getting a character */
876 if (!got_a_char)
877 return;
878 got_a_char = FALSE;
879
880 /* cancel only keyboard stuff */
881 if (!allow_typeahead && !mouse_is_down && !macro_pending()
882 && this_time - last_time > 0.3) {
883 #ifdef PENDING
884 register KEYMAP* curmap = topmap;
885 Uchar lc;
886 int i, j;
887 for (j = 0; input_pending(); ) {
888 errno = 0;
889 if (read_tty(&buf[j],1) < 0) {
890 if (errno && errno != EINTR) {
891 perror(readerr);
892 sig_catcher(0);
893 }
894 continue;
895 }
896 lc = *(Uchar*)buf;
897 if ((lc & 0200) || curmap == NULL) {
898 curmap = topmap;
899 j = 0;
900 continue;
901 }
902 j++;
903 for (i = (curmap->km_type[lc] >> KM_GSHIFT) & KM_GMASK; i; i--) {
904 if (!input_pending())
905 goto dbl_break;
906 read_tty(&buf[j++],1);
907 }
908
909 switch (curmap->km_type[lc] & KM_TMASK) {
910 case KM_STRING: /* a string? */
911 case KM_NOTHIN: /* no entry? */
912 curmap = topmap;
913 j = 0;
914 continue;
915 case KM_KEYMAP: /* another keymap? */
916 curmap = curmap->km_ptr[lc].km_km;
917 break;
918 }
919 }
920 dbl_break:
921 if (j) {
922 /* Don't delete a partial macro sequence */
923 buf[j] = '\0';
924 pushstring(buf,0);
925 }
926 #else /* this is probably v7 */
927 # ifdef I_SGTTY
928 ioctl(_tty_ch,TIOCSETP,&_tty);
929 # else
930 # ifdef I_TERMIO
931 ioctl(_tty_ch,TCSETAW,&_tty);
932 # else
933 # ifdef I_TERMIOS
934 tcsetattr(_tty_ch,TCSAFLUSH,&_tty);
935 # else
936 ..."Don't know how to eat typeahead!"
937 # endif
938 # endif
939 # endif
940 #endif
941 }
942 last_time = this_time;
943 }
944
945 void
save_typeahead(buf,len)946 save_typeahead(buf, len)
947 char* buf;
948 int len;
949 {
950 int cnt;
951
952 while (input_pending()) {
953 cnt = read_tty(buf, len);
954 buf += cnt;
955 len -= cnt;
956 }
957 *buf = '\0';
958 }
959
960 void
settle_down()961 settle_down()
962 {
963 dingaling();
964 fflush(stdout);
965 /*sleep(1);*/
966 nextout = nextin; /* empty circlebuf */
967 not_echoing = 0;
968 eat_typeahead();
969 }
970
971 #ifdef SUPPORT_NNTP
972 bool ignore_EINTR = FALSE;
973
974 Signal_t
alarm_catcher(signo)975 alarm_catcher(signo)
976 int signo;
977 {
978 /*printf("\n*** In alarm catcher **\n"); $$*/
979 ignore_EINTR = TRUE;
980 check_datasrcs();
981 sigset(SIGALRM,alarm_catcher);
982 (void) alarm(DATASRC_ALARM_SECS);
983 }
984 #endif
985
986 /* read a character from the terminal, with multi-character pushback */
987
988 int
read_tty(addr,size)989 read_tty(addr,size)
990 char* addr;
991 int size;
992 {
993 if (macro_pending()) {
994 *addr = circlebuf[nextout++];
995 nextout %= PUSHSIZE;
996 return 1;
997 }
998 #ifdef USE_TK
999 if (ttk_running) {
1000 ttk_wait_for_input(); /* handle events until input is available */
1001 if (ttk_keys && *ttk_keys) {
1002 int len = strlen(ttk_keys);
1003 if (size > len)
1004 size = len;
1005 strncpy(addr, ttk_keys, size); /* return the first bit */
1006 if (len > size)
1007 pushstring(ttk_keys+size,0); /* and push the rest */
1008 free(ttk_keys); /* every byte counts... */
1009 /* A plain NULL pointer will not work -- it is "\0" in TCL */
1010 ttk_keys = savestr(nullstr);
1011 return size;
1012 }
1013 }
1014 #endif
1015 #ifdef MSDOS
1016 *addr = getch();
1017 if (*addr == '\0')
1018 *addr = Ctl('\035');
1019 size = 1;
1020 #else
1021 size = read(0,addr,size);
1022 #endif
1023 #ifdef RAWONLY
1024 *addr &= 0177;
1025 #endif
1026 got_a_char = TRUE;
1027 return size;
1028 }
1029
1030 #ifdef PENDING
1031 # if !defined(FIONREAD) && !defined(HAS_RDCHK) && !defined(MSDOS)
1032 int
circfill()1033 circfill()
1034 {
1035 register int Howmany;
1036
1037 errno = 0;
1038 Howmany = read(devtty,circlebuf+nextin,1);
1039
1040 if (Howmany < 0 && (errno == EAGAIN || errno == EINTR))
1041 Howmany = 0;
1042 if (Howmany) {
1043 nextin += Howmany;
1044 nextin %= PUSHSIZE;
1045 }
1046 return Howmany;
1047 }
1048 # endif /* FIONREAD */
1049 #endif /* PENDING */
1050
1051 void
pushchar(c)1052 pushchar(c)
1053 char_int c;
1054 {
1055 nextout--;
1056 if (nextout < 0)
1057 nextout = PUSHSIZE - 1;
1058 if (nextout == nextin) {
1059 fputs("\npushback buffer overflow\n",stdout) FLUSH;
1060 sig_catcher(0);
1061 }
1062 circlebuf[nextout] = c;
1063 }
1064
1065 /* print an underlined string, one way or another */
1066
1067 void
underprint(s)1068 underprint(s)
1069 register char* s;
1070 {
1071 assert(tc_UC);
1072 if (*tc_UC) { /* char by char underline? */
1073 while (*s) {
1074 if (!AT_NORM_CHAR(s)) {
1075 putchar('^');
1076 backspace();/* back up over it */
1077 underchar();/* and do the underline */
1078 putchar((*s & 0x7F) | 64);
1079 backspace();/* back up over it */
1080 underchar();/* and do the underline */
1081 }
1082 else {
1083 putchar(*s);
1084 backspace();/* back up over it */
1085 underchar();/* and do the underline */
1086 }
1087 s++;
1088 }
1089 }
1090 else { /* start and stop underline */
1091 underline(); /* start underlining */
1092 while (*s)
1093 echo_char(*s++);
1094 un_underline(); /* stop underlining */
1095 }
1096 }
1097
1098 /* keep screen from flashing strangely on magic cookie terminals */
1099
1100 #ifdef NOFIREWORKS
1101 void
no_sofire()1102 no_sofire()
1103 {
1104 /* should we disable fireworks? */
1105 if (!(fire_is_out & STANDOUT) && (term_line|term_col)==0 && *tc_UP && *tc_SE) {
1106 newline();
1107 un_standout();
1108 up_line();
1109 carriage_return();
1110 }
1111 }
1112 #endif
1113
1114 #ifdef NOFIREWORKS
1115 void
no_ulfire()1116 no_ulfire()
1117 {
1118 /* should we disable fireworks? */
1119 if (!(fire_is_out & UNDERLINE) && (term_line|term_col)==0 && *tc_UP && *tc_US) {
1120 newline();
1121 un_underline();
1122 up_line();
1123 carriage_return();
1124 }
1125 }
1126 #endif
1127
1128 /* get a character into a buffer */
1129
1130 void
getcmd(whatbuf)1131 getcmd(whatbuf)
1132 register char* whatbuf;
1133 {
1134 register KEYMAP* curmap;
1135 register int i;
1136 bool no_macros;
1137 int times = 0; /* loop detector */
1138
1139 #ifdef SUPPORT_NNTP
1140 if (!input_pending()) {
1141 sigset(SIGALRM,alarm_catcher);
1142 (void) alarm(DATASRC_ALARM_SECS);
1143 }
1144 #endif
1145
1146 tryagain:
1147 curmap = topmap;
1148 #ifdef OLD_RN_WAY
1149 no_macros = (whatbuf != buf && !macro_pending());
1150 #else
1151 no_macros = (whatbuf != buf && !xmouse_is_on);
1152 #endif
1153 for (;;) {
1154 int_count = 0;
1155 errno = 0;
1156 #ifdef SUPPORT_NNTP
1157 ignore_EINTR = FALSE;
1158 #endif
1159 if (read_tty(whatbuf,1) < 0) {
1160 if (!errno)
1161 errno = EINTR;
1162 if (errno == EINTR) {
1163 #ifdef SUPPORT_NNTP
1164 if (ignore_EINTR)
1165 continue;
1166 (void) alarm(0);
1167 #endif
1168 return;
1169 }
1170 perror(readerr);
1171 sig_catcher(0);
1172 }
1173 lastchar = *(Uchar*)whatbuf;
1174 if (lastchar & 0200 || no_macros) {
1175 *whatbuf &= 0177;
1176 goto got_canonical;
1177 }
1178 if (curmap == NULL)
1179 goto got_canonical;
1180 for (i = (curmap->km_type[lastchar] >> KM_GSHIFT) & KM_GMASK; i; i--)
1181 read_tty(&whatbuf[i],1);
1182
1183 switch (curmap->km_type[lastchar] & KM_TMASK) {
1184 case KM_NOTHIN: /* no entry? */
1185 if (curmap == topmap) /* unmapped canonical */
1186 goto got_canonical;
1187 settle_down();
1188 goto tryagain;
1189 case KM_KEYMAP: /* another keymap? */
1190 curmap = curmap->km_ptr[lastchar].km_km;
1191 assert(curmap != NULL);
1192 break;
1193 case KM_STRING: /* a string? */
1194 pushstring(curmap->km_ptr[lastchar].km_str,0200);
1195 if (++times > 20) { /* loop? */
1196 fputs("\nmacro loop?\n",stdout);
1197 termdown(2);
1198 settle_down();
1199 }
1200 no_macros = FALSE;
1201 goto tryagain;
1202 }
1203 }
1204
1205 got_canonical:
1206 /* This hack is for mouse support */
1207 if (xmouse_is_on && *whatbuf == Ctl('c')) {
1208 mouse_input(whatbuf+1);
1209 times = 0;
1210 goto tryagain;
1211 }
1212 #ifdef I_SGTTY
1213 if (*whatbuf == '\r')
1214 *whatbuf = '\n';
1215 #endif
1216 if (whatbuf == buf)
1217 whatbuf[1] = FINISHCMD; /* tell finish_command to work */
1218 #ifdef SUPPORT_NNTP
1219 (void) alarm(0);
1220 #endif
1221 }
1222
1223 void
pushstring(str,bits)1224 pushstring(str,bits)
1225 char* str;
1226 char_int bits;
1227 {
1228 register int i;
1229 char tmpbuf[PUSHSIZE];
1230 register char* s = tmpbuf;
1231
1232 assert(str != NULL);
1233 interp(tmpbuf,PUSHSIZE,str);
1234 for (i = strlen(s)-1; i >= 0; i--)
1235 pushchar(s[i] ^ bits);
1236 }
1237
1238 int
get_anything()1239 get_anything()
1240 {
1241 char tmpbuf[64];
1242 char mode_save = mode;
1243
1244 reask_anything:
1245 unflush_output(); /* disable any ^O in effect */
1246 color_object(COLOR_MORE, 1);
1247 #ifdef VERBOSE
1248 IF(verbose)
1249 fputs("[Type space to continue] ",stdout);
1250 ELSE
1251 #endif
1252 #ifdef TERSE
1253 fputs("[MORE] ",stdout);
1254 #endif
1255 color_pop(); /* of COLOR_MORE */
1256 fflush(stdout);
1257 eat_typeahead();
1258 if (int_count)
1259 return -1;
1260 cache_until_key();
1261 set_mode(gmode, 'K');
1262 getcmd(tmpbuf);
1263 set_mode(gmode,mode_save);
1264 if (errno || *tmpbuf == '\f') {
1265 newline(); /* if return from stop signal */
1266 goto reask_anything; /* give them a prompt again */
1267 }
1268 if (*tmpbuf == 'h') {
1269 #ifdef VERBOSE
1270 IF(verbose)
1271 fputs("\nType q to quit or space to continue.\n",stdout) FLUSH;
1272 ELSE
1273 #endif
1274 #ifdef TERSE
1275 fputs("\nq to quit, space to continue.\n",stdout) FLUSH;
1276 #endif
1277 termdown(2);
1278 goto reask_anything;
1279 }
1280 else if (*tmpbuf != ' ' && *tmpbuf != '\n') {
1281 erase_line(0); /* erase the prompt */
1282 return *tmpbuf == 'q' ? -1 : *tmpbuf;
1283 }
1284 if (*tmpbuf == '\n') {
1285 page_line = tc_LINES - 1;
1286 erase_line(0);
1287 }
1288 else {
1289 page_line = 1;
1290 if (erase_screen) /* -e? */
1291 clear(); /* clear screen */
1292 else
1293 erase_line(0); /* erase the prompt */
1294 }
1295 return 0;
1296 }
1297
1298 int
pause_getcmd()1299 pause_getcmd()
1300 {
1301 char mode_save = mode;
1302
1303 unflush_output(); /* disable any ^O in effect */
1304 color_object(COLOR_CMD, 1);
1305 #ifdef VERBOSE
1306 IF(verbose)
1307 fputs("[Type space or a command] ",stdout);
1308 ELSE
1309 #endif
1310 #ifdef TERSE
1311 fputs("[CMD] ",stdout);
1312 #endif
1313 color_pop(); /* of COLOR_CMD */
1314 fflush(stdout);
1315 eat_typeahead();
1316 if (int_count)
1317 return -1;
1318 cache_until_key();
1319 set_mode(gmode,'K');
1320 getcmd(buf);
1321 set_mode(gmode,mode_save);
1322 if (errno || *buf == '\f')
1323 return 0; /* if return from stop signal */
1324 if (*buf != ' ') {
1325 erase_line(0); /* erase the prompt */
1326 return *buf;
1327 }
1328 return 0;
1329 }
1330
1331 void
in_char(prompt,newmode,dflt)1332 in_char(prompt, newmode, dflt)
1333 char* prompt;
1334 char_int newmode;
1335 char* dflt;
1336 {
1337 char mode_save = mode;
1338 char gmode_save = gmode;
1339 char* s;
1340 int newlines;
1341
1342 for (newlines = 0, s = prompt; *s; s++) {
1343 if (*s == '\n')
1344 newlines++;
1345 }
1346
1347 reask_in_char:
1348 unflush_output(); /* disable any ^O in effect */
1349 printf("%s [%s] ", prompt, dflt);
1350 fflush(stdout);
1351 termdown(newlines);
1352 eat_typeahead();
1353 set_mode('p',newmode);
1354 getcmd(buf);
1355 if (errno || *buf == '\f') {
1356 newline(); /* if return from stop signal */
1357 goto reask_in_char; /* give them a prompt again */
1358 }
1359 setdef(buf,dflt);
1360 set_mode(gmode_save,mode_save);
1361 }
1362
1363 void
in_answer(prompt,newmode)1364 in_answer(prompt, newmode)
1365 char* prompt;
1366 char_int newmode;
1367 {
1368 char mode_save = mode;
1369 char gmode_save = gmode;
1370
1371 reask_in_answer:
1372 unflush_output(); /* disable any ^O in effect */
1373 fputs(prompt,stdout);
1374 fflush(stdout);
1375 eat_typeahead();
1376 set_mode('i',newmode);
1377 reinp_in_answer:
1378 getcmd(buf);
1379 if (errno || *buf == '\f') {
1380 newline(); /* if return from stop signal */
1381 goto reask_in_answer; /* give them a prompt again */
1382 }
1383 if (*buf == ERASECH)
1384 goto reinp_in_answer;
1385 if (*buf != '\n' && *buf != ' ') {
1386 if (!finish_command(FALSE))
1387 goto reinp_in_answer;
1388 }
1389 else
1390 buf[1] = '\0';
1391 newline();
1392 set_mode(gmode_save,mode_save);
1393 }
1394
1395 /* If this takes more than one line, return FALSE */
1396
1397 bool
in_choice(prompt,value,choices,newmode)1398 in_choice(prompt, value, choices, newmode)
1399 char* prompt;
1400 char* value;
1401 char* choices;
1402 char_int newmode;
1403 {
1404 char mode_save = mode;
1405 char gmode_save = gmode;
1406 char* cp;
1407 char* bp;
1408 char* s;
1409 char* prefix = NULL;
1410 int len, number_was = -1, any_val_OK = 0, value_changed;
1411 char tmpbuf[80], prefixes[80];
1412
1413 unflush_output(); /* disable any ^O in effect */
1414 eat_typeahead();
1415 set_mode('c',newmode);
1416 screen_is_dirty = FALSE;
1417
1418 cp = choices;
1419 if (*cp == '[') {
1420 for (s = prefixes, cp++; *cp != ']'; ) {
1421 if (*cp == '/') {
1422 *s++ = '\0';
1423 cp++;
1424 }
1425 else
1426 *s++ = *cp++;
1427 }
1428 *s++ = '\0';
1429 *s = '\0';
1430 if (*++cp == ' ')
1431 cp++;
1432 }
1433 else
1434 *prefixes = '\0';
1435 for (s = tmpbuf; *cp; ) {
1436 if (*cp == '/') {
1437 *s++ = '\0';
1438 cp++;
1439 }
1440 else if (*cp == '<') {
1441 do {
1442 *s++ = *cp;
1443 } while (*cp++ != '>');
1444 any_val_OK = 1; /* flag that '<' was found */
1445 }
1446 else
1447 *s++ = *cp++;
1448 }
1449 cp = s;
1450 *s++ = '\0';
1451 *s = '\0';
1452 strcpy(buf,value);
1453
1454 reask_in_choice:
1455 len = strlen(buf);
1456 bp = buf;
1457 if (*prefixes != '\0') {
1458 s = prefix;
1459 for (prefix = prefixes; *prefix; prefix += strlen(prefix)) {
1460 if (*prefix == *buf)
1461 break;
1462 }
1463 if (*prefix) {
1464 for (bp = buf; *bp && *bp != ' '; bp++) ;
1465 while (*bp == ' ') bp++;
1466 }
1467 else
1468 prefix = NULL;
1469 value_changed = prefix != s;
1470 }
1471 else {
1472 prefix = NULL;
1473 value_changed = 0;
1474 }
1475 s = cp;
1476 for (;;) {
1477 cp += strlen(cp) + 1;
1478 if (!*cp)
1479 cp = tmpbuf;
1480 if (*cp == '<'
1481 && (*buf == '<' || cp[1] != '#' || isdigit(*buf) || !*s)) {
1482 prefix = NULL;
1483 break;
1484 }
1485 if (s == cp) {
1486 if (!value_changed) {
1487 if (prefix)
1488 prefix = NULL;
1489 else
1490 dingaling();
1491 }
1492 break;
1493 }
1494 if (!*bp || strnEQ(cp,bp,any_val_OK? len : 1))
1495 break;
1496 }
1497
1498 if (*cp == '<') {
1499 if (*buf == '<' || cp[1] == '#') {
1500 if (number_was >= 0)
1501 sprintf(buf, "%d", number_was);
1502 else {
1503 for (s = buf; isdigit(*s); s++) ;
1504 *s = '\0';
1505 }
1506 }
1507 }
1508 else {
1509 if (prefix) {
1510 sprintf(buf, "%s ", prefix);
1511 strcat(buf,cp);
1512 }
1513 else
1514 strcpy(buf,cp);
1515 }
1516 s = buf + strlen(buf);
1517 carriage_return();
1518 erase_line(0);
1519 fputs(prompt,stdout);
1520 fputs(buf,stdout);
1521 len = strlen(prompt);
1522 number_was = -1;
1523
1524 reinp_in_choice:
1525 if ((s-buf) + len >= tc_COLS)
1526 screen_is_dirty = TRUE;
1527 fflush(stdout);
1528 getcmd(s);
1529 if (errno || *s == '\f') /* if return from stop signal */
1530 *s = '\n';
1531 if (*s != '\n') {
1532 char ch = *s;
1533 if (*cp == '<' && ch != '\t' && (ch != ' ' || buf != s)) {
1534 if (cp[1] == '#') {
1535 s = edit_buf(s, (char*)NULL);
1536 if (s != buf) {
1537 if (isdigit(s[-1]))
1538 goto reinp_in_choice;
1539 else
1540 number_was = atoi(buf);
1541 }
1542 }
1543 else {
1544 s = edit_buf(s, (char*)NULL);
1545 goto reinp_in_choice;
1546 }
1547 }
1548 *s = '\0';
1549 for (s = buf; *s && *s != ' '; s++) ;
1550 if (*s == ' ') s++;
1551 if (ch == ' ' || ch == '\t') {
1552 if (prefix)
1553 *s = '\0';
1554 else
1555 *buf = '\0';
1556 }
1557 else {
1558 char ch1 = buf[0];
1559 if (prefix) {
1560 if (ch == ch1)
1561 ch = *s;
1562 else {
1563 ch1 = ch;
1564 ch = buf[0];
1565 }
1566 }
1567 sprintf(buf,"%c %c",ch == ERASECH || ch == KILLCH? '<' : ch, ch1);
1568 }
1569 goto reask_in_choice;
1570 }
1571 *s = '\0';
1572
1573 set_mode(gmode_save,mode_save);
1574 return !screen_is_dirty;
1575 }
1576
1577 int
print_lines(what_to_print,hilite)1578 print_lines(what_to_print,hilite)
1579 char* what_to_print;
1580 int hilite;
1581 {
1582 register char* s;
1583 register int i;
1584
1585 for (s=what_to_print; *s; ) {
1586 i = check_page_line();
1587 if (i)
1588 return i;
1589 if (hilite == STANDOUT) {
1590 #ifdef NOFIREWORKS
1591 if (erase_screen)
1592 no_sofire();
1593 #endif
1594 standout();
1595 }
1596 else if (hilite == UNDERLINE) {
1597 #ifdef NOFIREWORKS
1598 if (erase_screen)
1599 no_ulfire();
1600 #endif
1601 underline();
1602 }
1603 for (i = 0; i < tc_COLS; i++) {
1604 if (!*s)
1605 break;
1606 if (AT_NORM_CHAR(s))
1607 putchar(*s);
1608 else if (*s == '\t') {
1609 putchar(*s);
1610 i = ((i+8) & ~7) - 1;
1611 }
1612 else if (*s == '\n') {
1613 i = 32000;
1614 }
1615 else {
1616 i++;
1617 putchar('^');
1618 putchar(*s + 64);
1619 }
1620 s++;
1621 }
1622 if (i) {
1623 if (hilite == STANDOUT)
1624 un_standout();
1625 else if (hilite == UNDERLINE)
1626 un_underline();
1627 if (tc_AM && i == tc_COLS)
1628 fflush(stdout);
1629 else
1630 newline();
1631 }
1632 }
1633 return 0;
1634 }
1635
1636 int
check_page_line()1637 check_page_line()
1638 {
1639 if (page_line < 0)
1640 return -1;
1641 if (page_line >= tc_LINES || int_count) {
1642 register int cmd = -1;
1643 if (int_count || (cmd = get_anything())) {
1644 page_line = -1; /* disable further printing */
1645 if (cmd > 0)
1646 pushchar(cmd);
1647 return cmd;
1648 }
1649 }
1650 page_line++;
1651 return 0;
1652 }
1653
1654 void
page_start()1655 page_start()
1656 {
1657 page_line = 1;
1658 if (erase_screen)
1659 clear();
1660 else
1661 newline();
1662 }
1663
1664 void
errormsg(str)1665 errormsg(str)
1666 char* str;
1667 {
1668 if (gmode == 's') {
1669 if (str != msg)
1670 strcpy(msg,str);
1671 error_occurred = TRUE;
1672 }
1673 else if (*str) {
1674 printf("\n%s\n", str) FLUSH;
1675 termdown(2);
1676 }
1677 }
1678
1679 void
warnmsg(str)1680 warnmsg(str)
1681 char* str;
1682 {
1683 if (gmode != 's') {
1684 printf("\n%s\n", str) FLUSH;
1685 termdown(2);
1686 pad(just_a_sec/3);
1687 }
1688 }
1689
1690 void
pad(num)1691 pad(num)
1692 int num;
1693 {
1694 register int i;
1695
1696 for (i = num; i; i--)
1697 putchar(tc_PC);
1698 fflush(stdout);
1699 }
1700
1701 /* echo the command just typed */
1702
1703 #ifdef VERIFY
1704 void
printcmd()1705 printcmd()
1706 {
1707 if (verify && buf[1] == FINISHCMD) {
1708 if (!AT_NORM_CHAR(buf)) {
1709 putchar('^');
1710 putchar((*buf & 0x7F) | 64);
1711 backspace();
1712 backspace();
1713 }
1714 else {
1715 putchar(*buf);
1716 backspace();
1717 }
1718 fflush(stdout);
1719 }
1720 }
1721 #endif
1722
1723 void
rubout()1724 rubout()
1725 {
1726 backspace(); /* do the old backspace, */
1727 putchar(' '); /* space, */
1728 backspace(); /* backspace trick */
1729 }
1730
1731 void
reprint()1732 reprint()
1733 {
1734 register char* s;
1735
1736 fputs("^R\n",stdout) FLUSH;
1737 termdown(1);
1738 for (s = buf; *s; s++)
1739 echo_char(*s);
1740 screen_is_dirty = TRUE;
1741 }
1742
1743 void
erase_line(to_eos)1744 erase_line(to_eos)
1745 bool_int to_eos;
1746 {
1747 carriage_return();
1748 if (to_eos)
1749 clear_rest();
1750 else
1751 erase_eol();
1752 carriage_return(); /* Resets kernel's tab column counter to 0 */
1753 fflush(stdout);
1754 }
1755
1756 void
clear()1757 clear()
1758 {
1759 term_line = term_col = fire_is_out = 0;
1760 if (tc_CL)
1761 tputs(tc_CL,tc_LINES,putchr);
1762 else if (tc_CD) {
1763 home_cursor();
1764 tputs(tc_CD,tc_LINES,putchr);
1765 }
1766 else {
1767 int i;
1768 for (i = 0; i < tc_LINES; i++)
1769 putchr('\n');
1770 home_cursor();
1771 }
1772 tputs(tc_CR,1,putchr) FLUSH;
1773 }
1774
1775 void
home_cursor()1776 home_cursor()
1777 {
1778 char* tgoto();
1779
1780 if (!*tc_HO) { /* no home sequence? */
1781 if (!*tc_CM) { /* no cursor motion either? */
1782 fputs("\n\n\n", stdout);
1783 termdown(3);
1784 return; /* forget it. */
1785 }
1786 tputs(tgoto(tc_CM, 0, 0), 1, putchr); /* go to home via CM */
1787 }
1788 else { /* we have home sequence */
1789 tputs(tc_HO, 1, putchr);/* home via HO */
1790 }
1791 carriage_return(); /* Resets kernel's tab column counter to 0 */
1792 term_line = term_col = 0;
1793 }
1794
1795 void
goto_xy(to_col,to_line)1796 goto_xy(to_col,to_line)
1797 int to_col;
1798 int to_line;
1799 {
1800 char* tgoto();
1801 char* str;
1802 int cmcost, xcost, ycost;
1803
1804 if (term_col == to_col && term_line == to_line)
1805 return;
1806 if (*tc_CM && !muck_up_clear) {
1807 str = tgoto(tc_CM,to_col,to_line);
1808 cmcost = strlen(str);
1809 } else {
1810 str = NULL;
1811 cmcost = 9999;
1812 }
1813
1814 if ((ycost = (to_line - term_line)) < 0)
1815 ycost = (upcost? -ycost * upcost : 7777);
1816 else if (ycost > 0)
1817 term_col = 0;
1818
1819 if ((xcost = (to_col - term_col)) < 0) {
1820 if (!to_col && ycost+1 < cmcost) {
1821 carriage_return();
1822 xcost = 0;
1823 }
1824 else
1825 xcost = -xcost * leftcost;
1826 }
1827 else if (xcost > 0 && cmcost < 9999)
1828 xcost = 9999;
1829
1830 if (cmcost <= xcost + ycost) {
1831 tputs(str,1,putchr);
1832 term_line = to_line;
1833 term_col = to_col;
1834 return;
1835 }
1836
1837 if (ycost == 7777)
1838 home_cursor();
1839
1840 if (to_line >= term_line)
1841 while(term_line < to_line) newline();
1842 else
1843 while(term_line > to_line) up_line();
1844
1845 if (to_col >= term_col)
1846 while(term_col < to_col) term_col++, putchar(' ');
1847 else
1848 while(term_col > to_col) term_col--, backspace();
1849 }
1850
1851 static void
line_col_calcs()1852 line_col_calcs()
1853 {
1854 if (tc_LINES > 0) { /* is this a crt? */
1855 if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES]) {
1856 /* no -i or unreasonable value for initlines */
1857 if (outspeed >= B9600) /* whole page at >= 9600 baud */
1858 initlines = tc_LINES;
1859 else if (outspeed >= B4800) /* 16 lines at 4800 */
1860 initlines = 16;
1861 else /* otherwise just header */
1862 initlines = 8;
1863 }
1864 /* Check for initlines bigger than the screen and fix it! */
1865 if (initlines > tc_LINES)
1866 initlines = tc_LINES;
1867 }
1868 else { /* not a crt */
1869 tc_LINES = 30000; /* so don't page */
1870 tc_CL = "\n\n"; /* put a couple of lines between */
1871 if (!initlines || !option_def_vals[OI_INITIAL_ARTICLE_LINES])
1872 initlines = 8; /* make initlines reasonable */
1873 }
1874 if (tc_COLS <= 0)
1875 tc_COLS = 80;
1876 #ifdef SCAN
1877 s_resize_win(); /* let various parts know */
1878 #endif
1879 }
1880
1881 #ifdef SIGWINCH
1882 Signal_t
winch_catcher(dummy)1883 winch_catcher(dummy)
1884 int dummy;
1885 {
1886 /* Reset signal in case of System V dain bramage */
1887 sigset(SIGWINCH, winch_catcher);
1888
1889 /* Come here if window size change signal received */
1890 #ifdef TIOCGWINSZ
1891 { struct winsize ws;
1892 if (ioctl(0, TIOCGWINSZ, &ws) >= 0 && ws.ws_row > 0 && ws.ws_col > 0) {
1893 if (tc_LINES != ws.ws_row || tc_COLS != ws.ws_col) {
1894 tc_LINES = ws.ws_row;
1895 tc_COLS = ws.ws_col;
1896 line_col_calcs();
1897 sprintf(lines_export, "%d", tc_LINES);
1898 sprintf(cols_export, "%d", tc_COLS);
1899 if (gmode == 's' || mode == 'a' || mode == 'p') {
1900 forceme("\f"); /* cause a refresh */
1901 /* (defined only if TIOCSTI defined) */
1902 }
1903 }
1904 }
1905 }
1906 #else
1907 /* Well, if SIGWINCH is defined, but TIOCGWINSZ isn't, there's */
1908 /* almost certainly something wrong. Figure it out for yourself, */
1909 /* because I don't know how to deal with it :-) */
1910 ERROR!
1911 #endif
1912 }
1913 #endif
1914
1915 void
termlib_init()1916 termlib_init()
1917 {
1918 #ifdef USETITE
1919 if (tc_TI && *tc_TI) {
1920 tputs(tc_TI,1,putchr);
1921 fflush(stdout);
1922 }
1923 #endif
1924 #ifdef USEKSKE
1925 if (tc_KS && *tc_KS) {
1926 tputs(tc_KS,1,putchr);
1927 fflush(stdout);
1928 }
1929 #endif
1930 term_line = tc_LINES-1;
1931 term_col = 0;
1932 term_scrolled = tc_LINES;
1933 }
1934
1935 void
termlib_reset()1936 termlib_reset()
1937 {
1938 #ifdef USETITE
1939 if (tc_TE && *tc_TE) {
1940 tputs(tc_TE,1,putchr);
1941 fflush(stdout);
1942 }
1943 #endif
1944 #ifdef USEKSKE
1945 if (tc_KE && *tc_KE) {
1946 tputs(tc_KE,1,putchr);
1947 fflush(stdout);
1948 }
1949 #endif
1950 }
1951
1952 /* Nice-Background routines used to improve interactive performance with
1953 * background processing. When called, these routines will wait for a
1954 * keystroke or a time limit to expire. Calling this before a
1955 * background loop may significantly increase responsiveness when a
1956 * user types commands quickly.
1957 */
1958
1959 /* NOTE: If you have problems, uncomment this define for some debugging help.
1960 * (The time limit will be 5 seconds and lots of info will be printed). */
1961 /* #define DEBUG_NICEBG */
1962
1963 #ifdef PENDING
1964 #ifdef NICEBG
1965
1966 #ifdef NBG_SELECT
1967 #undef NBG_TERMIO
1968 #undef NBG_SIGIO
1969 #endif
1970
1971 #ifdef NBG_TERMIO
1972 #undef NBG_SELECT
1973 #undef NBG_SIGIO
1974 #endif
1975
1976 #ifdef NBG_SIGIO
1977 #undef NBG_TERMIO
1978 #undef NBG_SELECT
1979 #endif
1980
1981 #ifdef NBG_SIGIO /* SIGIO style */
1982 Signal_t
waitkey_sig_handler(dummy)1983 waitkey_sig_handler(dummy)
1984 int dummy;
1985 {
1986 #ifdef DEBUG_NICEBG
1987 /* CAA: I doubt that printf is safe in a signal handler... */
1988 printf("wait_key_pause signal caught!\n") FLUSH;
1989 fflush(stdout);
1990 #endif
1991 }
1992 #endif /* NBG_SIGIO */
1993
1994 /* /dev/tty file descriptor */
1995 /* note: for now we let the system close it on exit... */
1996 static int wait_ttyfd INIT(-1);
1997 static bool wait_initialized INIT(FALSE);
1998 static bool wait_fd_opened INIT(FALSE);
1999
2000 #ifdef NBG_SELECT
2001 static struct timeval wait_time;
2002 static struct fd_set wait_fdset;
2003 static int wait_tbl_size;
2004 #endif
2005
2006 /* returns TRUE if input received */
2007 bool
wait_key_pause(ticks)2008 wait_key_pause(ticks)
2009 int ticks; /* tenths of seconds to wait */
2010 {
2011 #ifdef DEBUG_NICEBG
2012 ticks = 50; /* debugging: wait 5 seconds */
2013 #endif
2014 if (input_pending())
2015 return TRUE;
2016 #ifdef DEBUG_NICEBG
2017 printf("entry: wait_key_pause\n") FLUSH; /* */
2018 #endif
2019 /* common initialization */
2020 if (!wait_fd_opened) {
2021 wait_ttyfd = open("/dev/tty",0); /*$$ possible cron prob */
2022 /* CAA: the open shouldn't really be a problem now that the
2023 * open return value is checked below. If running from cron,
2024 * I hope that the open will simply fail. If the open succeeds,
2025 * however, then there should be no cause for complaint by the
2026 * routines below. (The select method may be safest.) Still,
2027 * it might be nice to have some flag which means "we are running
2028 * in the background".
2029 */
2030 wait_fd_opened = TRUE;
2031 }
2032 if (wait_ttyfd < 0) {
2033 /* tty open failed. Don't wait around */
2034 return input_pending();
2035 }
2036 #ifdef NBG_TERMIO
2037 /* First try a nice standard way */
2038 {
2039 #ifdef I_TERMIO
2040 struct termio save_tty, wait_tty;
2041 #else /* must be TERMIOS then */
2042 struct termios save_tty, wait_tty;
2043 #endif
2044 char lbuf[1]; /* for the read command */
2045 int nrd; /* number read */
2046
2047 #ifdef DEBUG_NICEBG
2048 printf("wait_key_pause: using termio(s)\n") FLUSH;
2049 #endif
2050 if (!wait_initialized)
2051 wait_initialized = TRUE;
2052 if (ioctl(wait_ttyfd,TCGETA,&save_tty) == -1) {
2053 printf("wait_key_pause (term.c): ioctl TCGETA error\n") FLUSH;
2054 assert(FALSE);
2055 }
2056 wait_tty = save_tty;
2057 wait_tty.c_lflag &= ~ICANON;
2058 wait_tty.c_lflag &= ~ECHO;
2059 wait_tty.c_cc[VMIN] = 0;
2060 wait_tty.c_cc[VTIME] = ticks;
2061 if (ioctl(wait_ttyfd,TCSETAF,&wait_tty) == -1) {
2062 printf("wait_key_pause (term.c): ioctl TCSETAF error\n") FLUSH;
2063 assert(FALSE);
2064 }
2065 nrd = read(wait_ttyfd,&lbuf,1);
2066 ioctl(wait_ttyfd,TCSETAF,&save_tty); /* put back the old info */
2067 if (nrd == 1) {
2068 pushchar(lbuf[0]); /* put it back */
2069 #ifdef DEBUG_NICEBG
2070 printf("early exit: wait_key_pause\n") FLUSH; /* */
2071 #endif
2072 }
2073 #ifdef DEBUG_NICEBG
2074 printf("exit: wait_key_pause\n") FLUSH; /* */
2075 #endif
2076 return input_pending();
2077 }
2078 #endif /* NBG_TERMIO */
2079 #ifdef NBG_SELECT
2080 #ifdef DEBUG_NICEBG
2081 printf("wait_key_pause: using select\n") FLUSH;
2082 #endif
2083 if (!wait_initialized) {
2084 wait_tbl_size = wait_ttyfd + 1;
2085 wait_initialized = TRUE;
2086 }
2087 FD_ZERO(&wait_fdset);
2088 FD_SET(wait_ttyfd,&wait_fdset);
2089
2090 wait_time.tv_usec = (ticks%10)*1000000;
2091 wait_time.tv_sec = ticks/10;
2092
2093 (void)select(wait_tbl_size,&wait_fdset,NULL,NULL,&wait_time);
2094
2095 #ifdef DEBUG_NICEBG
2096 printf("exit: wait_key_pause\n") FLUSH; /* */
2097 #endif
2098 return input_pending();
2099 #endif /* NBG_SELECT */
2100 #ifdef NBG_SIGIO
2101 /* SIGIO signal sent for each keystroke style */
2102 /* Method of last resort. Surprisingly, this code has been quite
2103 * portable to several systems.
2104 */
2105 {
2106 #ifdef DEBUG_NICEBG
2107 printf("wait_key_pause: using SIGIO style\n") FLUSH;
2108 #endif
2109 if (!wait_initialized) { /* not initialized yet? */
2110 /* Portable? (probably not) */
2111 wait_initialized = TRUE;
2112 sigset(SIGALRM,waitkey_sig_handler);
2113 sigset(SIGIO,waitkey_sig_handler);
2114 /* enable SIGIO (some systems need it). ifdef it? */
2115 fcntl(wait_ttyfd,F_SETOWN,(int)getpid());
2116 fcntl(wait_ttyfd,F_SETFL,FASYNC);
2117 }
2118 (void) alarm((ticks+9)/10); /* sleep if no key pressed */
2119 /* NOTE: There is a race condition here, but not a very important one IMO.
2120 * If the alarm signal is sent *before* pause() is called, then this
2121 * function will simply wait until a key is pressed. In this case
2122 * no background processing will occur.
2123 * Noted by CAA, August 1992.
2124 */
2125 /* note2: The race condition can be minimized by using a variable
2126 * which will tell if the alarm arrived before the pause (really before
2127 * the variable was set, which should be near-instant.) Look into
2128 * this later.
2129 */
2130 pause(); /* wait for either ALRM or SIGIO signal */
2131 (void) alarm(0);
2132 #ifdef DEBUG_NICEBG
2133 printf("exit: wait_key_pause\n") FLUSH;
2134 #endif
2135 return input_pending();
2136 }
2137 #endif /* NBG_SIGIO */
2138 /* if no nice-background methods are defined, return. */
2139 /* Thanks to Kian-Tat Lim for finding an earlier bug here */
2140 #ifndef NBG_SELECT
2141 #ifndef NBG_TERMIO
2142 #ifndef NBG_SIGIO
2143 /* really kind of an error, but a reasonable way to handle it */
2144 /* Act as if no input was present */
2145 #ifdef DEBUG_NICEBG
2146 printf("wait_key_pause: no handler style, returning FALSE.\n");
2147 #endif
2148 return FALSE; /* don't worry if not reached */
2149 #endif
2150 #endif
2151 #endif
2152 }
2153 #endif /* NICEBG */
2154 #endif /* PENDING */
2155
2156 void
xmouse_init(progname)2157 xmouse_init(progname)
2158 char* progname;
2159 {
2160 char* s;
2161 if (!can_home || !use_threads)
2162 return;
2163 s = getenv("XTERMMOUSE");
2164 if (s && *s) {
2165 interp(msg, sizeof msg, s);
2166 set_option(OI_USE_MOUSE, msg);
2167 }
2168 else if (progname[strlen(progname)-1] == 'x') {
2169 /* an 'x' at the end means enable Xterm mouse tracking */
2170 set_option(OI_USE_MOUSE, "y");
2171 }
2172 }
2173
2174 void
xmouse_check()2175 xmouse_check()
2176 {
2177 mousebar_cnt = 0;
2178 if (UseMouse) {
2179 bool turn_it_on;
2180 char mmode = mode;
2181 if (gmode == 'p') {
2182 turn_it_on = TRUE;
2183 mmode = '\0';
2184 }
2185 else if (gmode == 'i' || gmode == 'p'
2186 || (muck_up_clear && gmode != 's'))
2187 turn_it_on = FALSE;
2188 else {
2189 interp(msg, sizeof msg, MouseModes);
2190 turn_it_on = (index(msg, mode) != NULL);
2191 }
2192 if (turn_it_on) {
2193 char* s;
2194 int i, j;
2195 switch (mmode) {
2196 case 'c':
2197 mousebar_btns = NewsrcSelBtns;
2198 mousebar_cnt = NewsrcSelBtnCnt;
2199 break;
2200 case 'j':
2201 mousebar_btns = AddSelBtns;
2202 mousebar_cnt = AddSelBtnCnt;
2203 break;
2204 case 'l':
2205 mousebar_btns = OptionSelBtns;
2206 mousebar_cnt = OptionSelBtnCnt;
2207 break;
2208 case 't':
2209 mousebar_btns = NewsSelBtns;
2210 mousebar_cnt = NewsSelBtnCnt;
2211 break;
2212 case 'w':
2213 mousebar_btns = NewsgroupSelBtns;
2214 mousebar_cnt = NewsgroupSelBtnCnt;
2215 break;
2216 case 'a': case 'p':
2217 mousebar_btns = ArtPagerBtns;
2218 mousebar_cnt = ArtPagerBtnCnt;
2219 break;
2220 case 'v':
2221 mousebar_btns = UnivSelBtns;
2222 mousebar_cnt = UnivSelBtnCnt;
2223 break;
2224 default:
2225 mousebar_btns = nullstr;
2226 /*mousebar_cnt = 0;*/
2227 break;
2228 }
2229 s = mousebar_btns;
2230 mousebar_width = 0;
2231 for (i = 0; i < mousebar_cnt; i++) {
2232 j = strlen(s);
2233 if (*s == '[') {
2234 mousebar_width += j;
2235 s += j + 1;
2236 j = strlen(s);
2237 }
2238 else
2239 mousebar_width += (j < 2? 3+1 : (j == 2? 4+1
2240 : (j < 5? j: 5+1)));
2241 s += j + 1;
2242 }
2243 xmouse_on();
2244 }
2245 else
2246 xmouse_off();
2247 mouse_is_down = FALSE;
2248 }
2249 }
2250
2251 void
xmouse_on()2252 xmouse_on()
2253 {
2254 if (!xmouse_is_on) {
2255 /* save old highlight mouse tracking and enable mouse tracking */
2256 fputs("\033[?1001s\033[?1000h",stdout);
2257 fflush(stdout);
2258 xmouse_is_on = TRUE;
2259 }
2260 }
2261
2262 void
xmouse_off()2263 xmouse_off()
2264 {
2265 if (xmouse_is_on) {
2266 /* disable mouse tracking and restore old highlight mouse tracking */
2267 fputs("\033[?1000l\033[?1001r",stdout);
2268 fflush(stdout);
2269 xmouse_is_on = FALSE;
2270 }
2271 }
2272
2273 void
draw_mousebar(limit,restore_cursor)2274 draw_mousebar(limit,restore_cursor)
2275 int limit;
2276 bool_int restore_cursor;
2277 {
2278 int i;
2279 char* s;
2280 char* t;
2281 int save_col = term_col;
2282 int save_line = term_line;
2283
2284 mousebar_width = 0;
2285 if (mousebar_cnt == 0)
2286 return;
2287
2288 s = mousebar_btns;
2289 t = msg;
2290 for (i = 0; i < mousebar_cnt; i++) {
2291 if (*s == '[') {
2292 while (*++s) *t++ = *s;
2293 s++;
2294 }
2295 else {
2296 switch (strlen(s)) {
2297 case 0:
2298 *t++ = ' ';
2299 /* FALL THROUGH */
2300 case 1: case 2:
2301 *t++ = ' ';
2302 while (*s) *t++ = *s++;
2303 *t++ = ' ';
2304 break;
2305 case 3: case 4:
2306 while (*s) *t++ = *s++;
2307 break;
2308 default:
2309 strncpy(t, s, 5);
2310 t += 5;
2311 break;
2312 }
2313 }
2314 s += strlen(s) + 1;
2315 *t++ = '\0';
2316 }
2317 mousebar_width = t - msg;
2318 mousebar_start = 0;
2319
2320 s = msg;
2321 while (mousebar_width > limit) {
2322 int len = strlen(s) + 1;
2323 s += len;
2324 mousebar_width -= len;
2325 mousebar_start++;
2326 }
2327
2328 goto_xy(tc_COLS - mousebar_width - 1, tc_LINES-1);
2329 for (i = mousebar_start; i < mousebar_cnt; i++) {
2330 putchar(' ');
2331 color_string(COLOR_MOUSE,s);
2332 s += strlen(s) + 1;
2333 }
2334 term_col = tc_COLS-1;
2335 if (restore_cursor)
2336 goto_xy(save_col, save_line);
2337 }
2338
2339 static void
mouse_input(cp)2340 mouse_input(cp)
2341 char* cp;
2342 {
2343 static int last_btn;
2344 static int last_x, last_y;
2345 int btn;
2346 int x, y;
2347
2348 if (cp[2] < ' ' || cp[2] > ' '+3)
2349 return;
2350 btn = (cp[2] & 3);
2351 x = cp[1] - 33;
2352 y = cp[0] - 33;
2353
2354 if (btn != 3) {
2355 #if defined(HAS_GETTIMEOFDAY) || defined(HAS_FTIME)
2356 static double last_time = 0.;
2357 double this_time = current_time();
2358 if (last_btn == btn && last_y == y && this_time - last_time <= 0.75
2359 && (last_x == x || last_x == x-1 || last_x == x+1))
2360 btn |= 4;
2361 last_time = this_time;
2362 #endif /* HAS_GETTIMEOFDAY || HAS_FTIME */
2363 last_btn = (btn & 3);
2364 last_x = x;
2365 last_y = y;
2366 mouse_is_down = TRUE;
2367 }
2368 else {
2369 if (!mouse_is_down)
2370 return;
2371 mouse_is_down = FALSE;
2372 }
2373
2374 if (gmode == 's')
2375 selector_mouse(btn, x,y, last_btn, last_x,last_y);
2376 else if (mode == 'a' || mode == 'p')
2377 pager_mouse(btn, x,y, last_btn, last_x,last_y);
2378 else
2379 pushchar(' ');
2380 }
2381
2382 bool
check_mousebar(btn,x,y,btn_clk,x_clk,y_clk)2383 check_mousebar(btn, x,y, btn_clk, x_clk,y_clk)
2384 int btn;
2385 int x, y;
2386 int btn_clk;
2387 int x_clk, y_clk;
2388 {
2389 char* s = mousebar_btns;
2390 char* t;
2391 int i, j;
2392 int col = tc_COLS - mousebar_width;
2393
2394 if (mousebar_width != 0 && btn_clk == 0 && y_clk == tc_LINES-1
2395 && (x_clk -= col-1) > 0) {
2396 x -= col-1;
2397 for (i = 0; i < mousebar_start; i++) {
2398 if (*s == '[')
2399 s += strlen(s) + 1;
2400 s += strlen(s) + 1;
2401 }
2402 while (1) {
2403 i = strlen(s);
2404 t = s;
2405 if (*s == '[') {
2406 s += i + 1;
2407 j = 0;
2408 while (*++t == ' ') j++;
2409 }
2410 else if (i <= 2) {
2411 i = 3 + (i==2) + 1;
2412 j = 1;
2413 }
2414 else {
2415 i = (i < 5? i+1 : 5+1);
2416 j = 0;
2417 }
2418 if (x_clk < i) {
2419 int tcol = term_col;
2420 int tline = term_line;
2421 goto_xy(col+j,tc_LINES-1);
2422 if (btn == 3)
2423 color_object(COLOR_MOUSE, 1);
2424 if (s == t) {
2425 for (j = 0; j < 5 && *t; j++, t++)
2426 term_col++, putchar(*t);
2427 }
2428 else {
2429 for (; *t && *t != ' '; t++)
2430 term_col++, putchar(*t);
2431 }
2432 if (btn == 3)
2433 color_pop(); /* of COLOR_MOUSE */
2434 goto_xy(tcol,tline);
2435 fflush(stdout);
2436 if (btn == 3 && x > 0 && x < i)
2437 pushstring(s,0);
2438 break;
2439 }
2440 if (!(x_clk -= i))
2441 break;
2442 x -= i;
2443 col += i;
2444 s += strlen(s) + 1;
2445 }
2446 return TRUE;
2447 }
2448 return FALSE;
2449 }
2450
2451 static int tc_string_cnt = 0;
2452
2453 static struct {
2454 char* capability; /* name of capability, e.g. "forground red" */
2455 char* string; /* escape sequence, e.g. "\033[31m" */
2456 } tc_strings[TC_STRINGS];
2457
2458 /* Parse a line from the [termcap] section of trnrc. */
2459 void
add_tc_string(capability,string)2460 add_tc_string(capability, string)
2461 char* capability;
2462 char* string;
2463 {
2464 int i;
2465
2466 for (i = 0; i < tc_string_cnt; i++) {
2467 if (strEQ(capability, tc_strings[i].capability)) {
2468 free(tc_strings[i].string);
2469 break;
2470 }
2471 }
2472 if (i == tc_string_cnt) {
2473 if (tc_string_cnt == TC_STRINGS) {
2474 fprintf(stderr,"trn: too many colors in [termcap] section (max is %d).\n",
2475 TC_STRINGS);
2476 finalize(1);
2477 }
2478 tc_string_cnt++;
2479 tc_strings[i].capability = savestr(capability);
2480 }
2481
2482 tc_strings[i].string = savestr(string);
2483 }
2484
2485 /* Return the named termcap color capability's string. */
2486 char*
tc_color_capability(capability)2487 tc_color_capability(capability)
2488 char* capability;
2489 {
2490 int c;
2491
2492 for (c = 0; c < tc_string_cnt; c++) {
2493 if (strEQ(tc_strings[c].capability, capability))
2494 return tc_strings[c].string;
2495 }
2496 return NULL;
2497 }
2498
2499 #ifdef MSDOS
2500 int
tputs(str,num,func)2501 tputs(str,num,func)
2502 char* str;
2503 int num;
2504 int (*func) _((char_int));
2505 {
2506 printf(str,num);
2507 return 0;
2508 }
2509 #endif
2510
2511 #ifdef MSDOS
2512 char*
tgoto(str,x,y)2513 tgoto(str,x,y)
2514 char* str;
2515 int x;
2516 int y;
2517 {
2518 static char gbuf[32];
2519 sprintf(gbuf,str,y+1,x+1);
2520 return gbuf;
2521 }
2522 #endif
2523