Annotation of src/usr.bin/less/screen.c, Revision 1.4
1.1 etheisen 1: /*
2: * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice in the documentation and/or other materials provided with
12: * the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
15: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
18: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
20: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
21: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27:
28: /*
29: * Routines which deal with the characteristics of the terminal.
30: * Uses termcap to be as terminal-independent as possible.
31: *
32: * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
33: */
34:
35: #include "less.h"
36: #include "cmd.h"
37:
38: #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
39: #include <termios.h>
40: #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
41: #include <sys/ioctl.h>
42: #endif
43: #else
44: #if HAVE_TERMIO_H
45: #include <termio.h>
46: #else
47: #include <sgtty.h>
48: #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
49: #include <sys/ioctl.h>
50: #endif
51: #endif
52: #endif
53: #if HAVE_TERMCAP_H
54: #include <termcap.h>
55: #endif
56:
57: #ifndef TIOCGWINSZ
58: /*
59: * For the Unix PC (ATT 7300 & 3B1):
60: * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
61: * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
62: */
63: #include <sys/signal.h>
64: #ifdef SIGPHONE
65: #include <sys/window.h>
66: #endif
67: #endif
68:
69: #if HAVE_SYS_STREAM_H
70: #include <sys/stream.h>
71: #endif
72: #if HAVE_SYS_PTEM_H
73: #include <sys/ptem.h>
74: #endif
75:
76: #if OS2
77: #define DEFAULT_TERM "ansi"
78: #else
79: #define DEFAULT_TERM "unknown"
80: #endif
81:
82: /*
83: * Strings passed to tputs() to do various terminal functions.
84: */
85: static char
86: *sc_pad, /* Pad string */
87: *sc_home, /* Cursor home */
88: *sc_addline, /* Add line, scroll down following lines */
89: *sc_lower_left, /* Cursor to last line, first column */
90: *sc_move, /* General cursor positioning */
91: *sc_clear, /* Clear screen */
92: *sc_eol_clear, /* Clear to end of line */
93: *sc_eos_clear, /* Clear to end of screen */
94: *sc_s_in, /* Enter standout (highlighted) mode */
95: *sc_s_out, /* Exit standout mode */
96: *sc_u_in, /* Enter underline mode */
97: *sc_u_out, /* Exit underline mode */
98: *sc_b_in, /* Enter bold mode */
99: *sc_b_out, /* Exit bold mode */
100: *sc_bl_in, /* Enter blink mode */
101: *sc_bl_out, /* Exit blink mode */
102: *sc_visual_bell, /* Visual bell (flash screen) sequence */
103: *sc_backspace, /* Backspace cursor */
104: *sc_s_keypad, /* Start keypad mode */
105: *sc_e_keypad, /* End keypad mode */
106: *sc_init, /* Startup terminal initialization */
107: *sc_deinit; /* Exit terminal de-initialization */
108:
109: static int init_done = 0;
110:
111: public int auto_wrap; /* Terminal does \r\n when write past margin */
112: public int ignaw; /* Terminal ignores \n immediately after wrap */
113: public int erase_char, kill_char; /* The user's erase and line-kill chars */
114: public int werase_char; /* The user's word-erase char */
115: public int sc_width, sc_height; /* Height & width of screen */
116: public int bo_s_width, bo_e_width; /* Printing width of boldface seq */
117: public int ul_s_width, ul_e_width; /* Printing width of underline seq */
118: public int so_s_width, so_e_width; /* Printing width of standout seq */
119: public int bl_s_width, bl_e_width; /* Printing width of blink seq */
120: public int above_mem, below_mem; /* Memory retained above/below screen */
121: public int can_goto_line; /* Can move cursor to any line */
122:
123: static char *cheaper();
124:
125: /*
126: * These two variables are sometimes defined in,
127: * and needed by, the termcap library.
128: */
129: #if MUST_DEFINE_OSPEED
130: extern short ospeed; /* Terminal output baud rate */
131: extern char PC; /* Pad character */
132: #endif
133:
134: extern int quiet; /* If VERY_QUIET, use visual bell for bell */
135: extern int know_dumb; /* Don't complain about a dumb terminal */
136: extern int back_scroll;
137: extern int swindow;
138: extern int no_init;
1.2 etheisen 139: extern int quit_at_eof;
140: extern int more_mode;
1.1 etheisen 141: #if HILITE_SEARCH
142: extern int hilite_search;
143: #endif
144:
145: extern char *tgetstr();
146: extern char *tgoto();
147:
148:
149: /*
150: * Change terminal to "raw mode", or restore to "normal" mode.
151: * "Raw mode" means
152: * 1. An outstanding read will complete on receipt of a single keystroke.
153: * 2. Input is not echoed.
154: * 3. On output, \n is mapped to \r\n.
155: * 4. \t is NOT expanded into spaces.
156: * 5. Signal-causing characters such as ctrl-C (interrupt),
157: * etc. are NOT disabled.
158: * It doesn't matter whether an input \n is mapped to \r, or vice versa.
159: */
160: public void
161: raw_mode(on)
162: int on;
163: {
164: static int curr_on = 0;
165:
166: if (on == curr_on)
167: return;
168: #if OS2
169: signal(SIGINT, SIG_IGN);
170: erase_char = '\b';
171: kill_char = '\033';
172: #else
173: #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
174: {
175: struct termios s;
176: static struct termios save_term;
177:
178: if (on)
179: {
180: /*
181: * Get terminal modes.
182: */
183: tcgetattr(2, &s);
184:
185: /*
186: * Save modes and set certain variables dependent on modes.
187: */
188: save_term = s;
189: #if HAVE_OSPEED
190: switch (cfgetospeed(&s))
191: {
192: #ifdef B0
193: case B0: ospeed = 0; break;
194: #endif
195: #ifdef B50
196: case B50: ospeed = 1; break;
197: #endif
198: #ifdef B75
199: case B75: ospeed = 2; break;
200: #endif
201: #ifdef B110
202: case B110: ospeed = 3; break;
203: #endif
204: #ifdef B134
205: case B134: ospeed = 4; break;
206: #endif
207: #ifdef B150
208: case B150: ospeed = 5; break;
209: #endif
210: #ifdef B200
211: case B200: ospeed = 6; break;
212: #endif
213: #ifdef B300
214: case B300: ospeed = 7; break;
215: #endif
216: #ifdef B600
217: case B600: ospeed = 8; break;
218: #endif
219: #ifdef B1200
220: case B1200: ospeed = 9; break;
221: #endif
222: #ifdef B1800
223: case B1800: ospeed = 10; break;
224: #endif
225: #ifdef B2400
226: case B2400: ospeed = 11; break;
227: #endif
228: #ifdef B4800
229: case B4800: ospeed = 12; break;
230: #endif
231: #ifdef B9600
232: case B9600: ospeed = 13; break;
233: #endif
234: #ifdef EXTA
235: case EXTA: ospeed = 14; break;
236: #endif
237: #ifdef EXTB
238: case EXTB: ospeed = 15; break;
239: #endif
240: #ifdef B57600
241: case B57600: ospeed = 16; break;
242: #endif
243: #ifdef B115200
244: case B115200: ospeed = 17; break;
245: #endif
246: default: ;
247: }
248: #endif
249: erase_char = s.c_cc[VERASE];
250: kill_char = s.c_cc[VKILL];
251: #ifdef VWERASE
252: werase_char = s.c_cc[VWERASE];
253: #else
254: werase_char = 0;
255: #endif
256:
257: /*
258: * Set the modes to the way we want them.
259: */
260: s.c_lflag &= ~(0
261: #ifdef ICANON
262: | ICANON
263: #endif
264: #ifdef ECHO
265: | ECHO
266: #endif
267: #ifdef ECHOE
268: | ECHOE
269: #endif
270: #ifdef ECHOK
271: | ECHOK
272: #endif
273: #if ECHONL
274: | ECHONL
275: #endif
276: );
277:
278: s.c_oflag |= (0
279: #ifdef XTABS
280: | XTABS
281: #else
282: #ifdef TAB3
283: | TAB3
284: #else
285: #ifdef OXTABS
286: | OXTABS
287: #endif
288: #endif
289: #endif
290: #ifdef OPOST
291: | OPOST
292: #endif
293: #ifdef ONLCR
294: | ONLCR
295: #endif
296: );
297:
298: s.c_oflag &= ~(0
299: #ifdef ONOEOT
300: | ONOEOT
301: #endif
302: #ifdef OCRNL
303: | OCRNL
304: #endif
305: #ifdef ONOCR
306: | ONOCR
307: #endif
308: #ifdef ONLRET
309: | ONLRET
310: #endif
311: );
312: s.c_cc[VMIN] = 1;
313: s.c_cc[VTIME] = 0;
314: } else
315: {
316: /*
317: * Restore saved modes.
318: */
319: s = save_term;
320: }
1.4 ! millert 321: tcsetattr(2, TCSANOW, &s);
1.1 etheisen 322: }
323: #else
324: #ifdef TCGETA
325: {
326: struct termio s;
327: static struct termio save_term;
328:
329: if (on)
330: {
331: /*
332: * Get terminal modes.
333: */
334: ioctl(2, TCGETA, &s);
335:
336: /*
337: * Save modes and set certain variables dependent on modes.
338: */
339: save_term = s;
340: #if HAVE_OSPEED
341: ospeed = s.c_cflag & CBAUD;
342: #endif
343: erase_char = s.c_cc[VERASE];
344: kill_char = s.c_cc[VKILL];
345: #ifdef VWERASE
346: werase_char = s.c_cc[VWERASE];
347: #else
348: werase_char = 0;
349: #endif
350:
351: /*
352: * Set the modes to the way we want them.
353: */
354: s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
355: s.c_oflag |= (OPOST|ONLCR|TAB3);
356: s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
357: s.c_cc[VMIN] = 1;
358: s.c_cc[VTIME] = 0;
359: } else
360: {
361: /*
362: * Restore saved modes.
363: */
364: s = save_term;
365: }
366: ioctl(2, TCSETAW, &s);
367: }
368: #else
369: {
370: struct sgttyb s;
371: static struct sgttyb save_term;
372:
373: if (on)
374: {
375: /*
376: * Get terminal modes.
377: */
378: ioctl(2, TIOCGETP, &s);
379:
380: /*
381: * Save modes and set certain variables dependent on modes.
382: */
383: save_term = s;
384: #if HAVE_OSPEED
385: ospeed = s.sg_ospeed;
386: #endif
387: erase_char = s.sg_erase;
388: kill_char = s.sg_kill;
389: werase_char = 0;
390:
391: /*
392: * Set the modes to the way we want them.
393: */
394: s.sg_flags |= CBREAK;
395: s.sg_flags &= ~(ECHO|XTABS);
396: } else
397: {
398: /*
399: * Restore saved modes.
400: */
401: s = save_term;
402: }
403: ioctl(2, TIOCSETN, &s);
404: }
405: #endif
406: #endif
407: #endif
408: curr_on = on;
409: }
410:
411: static void
412: cannot(s)
413: char *s;
414: {
415: PARG parg;
416:
1.2 etheisen 417: if (know_dumb || more_mode)
1.1 etheisen 418: /*
419: * User knows this is a dumb terminal, so don't tell him.
1.2 etheisen 420: * more doesn't complain about these, either.
1.1 etheisen 421: */
422: return;
423:
424: parg.p_string = s;
425: error("WARNING: terminal cannot %s", &parg);
426: }
427:
428: /*
429: * Get size of the output screen.
430: */
431: #if OS2
432: public void
433: scrsize()
434: {
435: int s[2];
436:
437: _scrsize(s);
438: sc_width = s[0];
439: sc_height = s[1];
440: }
441:
442: #else
443:
444: public void
445: scrsize()
446: {
447: register char *s;
448: #ifdef TIOCGWINSZ
449: struct winsize w;
450: #else
451: #ifdef WIOCGETD
452: struct uwdata w;
453: #endif
454: #endif
455:
456: #ifdef TIOCGWINSZ
457: if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
458: sc_height = w.ws_row;
459: else
460: #else
461: #ifdef WIOCGETD
462: if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
463: sc_height = w.uw_height/w.uw_vs;
464: else
465: #endif
466: #endif
467: if ((s = getenv("LINES")) != NULL)
468: sc_height = atoi(s);
469: else
470: sc_height = tgetnum("li");
471:
472: if (sc_height <= 0)
473: sc_height = 24;
474:
475: #ifdef TIOCGWINSZ
476: if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
477: sc_width = w.ws_col;
478: else
479: #ifdef WIOCGETD
480: if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
481: sc_width = w.uw_width/w.uw_hs;
482: else
483: #endif
484: #endif
485: if ((s = getenv("COLUMNS")) != NULL)
486: sc_width = atoi(s);
487: else
488: sc_width = tgetnum("co");
489:
490: if (sc_width <= 0)
491: sc_width = 80;
492: }
493: #endif /* OS2 */
494:
495: /*
496: * Take care of the "variable" keys.
497: * Certain keys send escape sequences which differ on different terminals
498: * (such as the arrow keys, INSERT, DELETE, etc.)
499: * Construct the commands based on these keys.
500: */
501: public void
502: get_editkeys()
503: {
504: char *sp;
505: char *s;
506: char tbuf[40];
507:
508: static char kfcmdtable[400];
509: int sz_kfcmdtable = 0;
510: static char kecmdtable[400];
511: int sz_kecmdtable = 0;
512:
513: #define put_cmd(str,action,tbl,sz) { \
514: strcpy(tbl+sz, str); \
515: sz += strlen(str) + 1; \
516: tbl[sz++] = action; }
517: #define put_esc_cmd(str,action,tbl,sz) { \
518: tbl[sz++] = ESC; \
519: put_cmd(str,action,tbl,sz); }
520:
521: #define put_fcmd(str,action) put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
522: #define put_ecmd(str,action) put_cmd(str,action,kecmdtable,sz_kecmdtable)
523: #define put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
524: #define put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
525:
526: /*
527: * Look at some interesting keys and see what strings they send.
528: * Create commands (both command keys and line-edit keys).
529: */
530:
531: /* RIGHT ARROW */
532: sp = tbuf;
533: if ((s = tgetstr("kr", &sp)) != NULL)
534: {
535: put_ecmd(s, EC_RIGHT);
536: put_esc_ecmd(s, EC_W_RIGHT);
537: }
538:
539: /* LEFT ARROW */
540: sp = tbuf;
541: if ((s = tgetstr("kl", &sp)) != NULL)
542: {
543: put_ecmd(s, EC_LEFT);
544: put_esc_ecmd(s, EC_W_LEFT);
545: }
546:
547: /* UP ARROW */
548: sp = tbuf;
549: if ((s = tgetstr("ku", &sp)) != NULL)
550: {
551: put_ecmd(s, EC_UP);
552: put_fcmd(s, A_B_LINE);
553: }
554:
555: /* DOWN ARROW */
556: sp = tbuf;
557: if ((s = tgetstr("kd", &sp)) != NULL)
558: {
559: put_ecmd(s, EC_DOWN);
560: put_fcmd(s, A_F_LINE);
561: }
562:
563: /* PAGE UP */
564: sp = tbuf;
565: if ((s = tgetstr("kP", &sp)) != NULL)
566: {
567: put_fcmd(s, A_B_SCREEN);
568: }
569:
570: /* PAGE DOWN */
571: sp = tbuf;
572: if ((s = tgetstr("kN", &sp)) != NULL)
573: {
574: put_fcmd(s, A_F_SCREEN);
575: }
576:
577: /* HOME */
578: sp = tbuf;
579: if ((s = tgetstr("kh", &sp)) != NULL)
580: {
581: put_ecmd(s, EC_HOME);
582: }
583:
584: /* END */
585: sp = tbuf;
586: if ((s = tgetstr("@7", &sp)) != NULL)
587: {
588: put_ecmd(s, EC_END);
589: }
590:
591: /* DELETE */
592: sp = tbuf;
593: if ((s = tgetstr("kD", &sp)) == NULL)
594: {
595: /* Use DEL (\177) if no "kD" termcap. */
596: tbuf[1] = '\177';
597: tbuf[2] = '\0';
598: s = tbuf+1;
599: }
600: put_ecmd(s, EC_DELETE);
601: put_esc_ecmd(s, EC_W_DELETE);
602:
603: /* BACKSPACE */
604: tbuf[0] = ESC;
605: tbuf[1] = erase_char;
606: tbuf[2] = '\0';
607: put_ecmd(tbuf, EC_W_BACKSPACE);
608:
609: if (werase_char != 0)
610: {
611: tbuf[0] = werase_char;
612: tbuf[1] = '\0';
613: put_ecmd(tbuf, EC_W_BACKSPACE);
614: }
615:
616: /*
617: * Register the two tables.
618: */
619: add_fcmd_table(kfcmdtable, sz_kfcmdtable);
620: add_ecmd_table(kecmdtable, sz_kecmdtable);
621: }
622:
623: #if DEBUG
624: static void
625: get_debug_term()
626: {
627: auto_wrap = 1;
628: ignaw = 1;
629: so_s_width = so_e_width = 0;
630: bo_s_width = bo_e_width = 0;
631: ul_s_width = ul_e_width = 0;
632: bl_s_width = bl_e_width = 0;
633: sc_s_keypad = "(InitKey)";
634: sc_e_keypad = "(DeinitKey)";
635: sc_init = "(InitTerm)";
636: sc_deinit = "(DeinitTerm)";
637: sc_eol_clear = "(ClearEOL)";
638: sc_eos_clear = "(ClearEOS)";
639: sc_clear = "(ClearScreen)";
640: sc_move = "(Move<%d,%d>)";
641: sc_s_in = "(SO+)";
642: sc_s_out = "(SO-)";
643: sc_u_in = "(UL+)";
644: sc_u_out = "(UL-)";
645: sc_b_in = "(BO+)";
646: sc_b_out = "(BO-)";
647: sc_bl_in = "(BL+)";
648: sc_bl_out = "(BL-)";
649: sc_visual_bell ="(VBell)";
650: sc_backspace = "(BS)";
651: sc_home = "(Home)";
652: sc_lower_left = "(LL)";
653: sc_addline = "(AddLine)";
654: }
655: #endif
656:
657: /*
658: * Get terminal capabilities via termcap.
659: */
660: public void
661: get_term()
662: {
663: char *sp;
664: register char *t1, *t2;
665: register int hard;
666: char *term;
667: char termbuf[2048];
668:
669: static char sbuf[1024];
670:
671: #ifdef OS2
672: /*
673: * Make sure the termcap database is available.
674: */
675: sp = getenv("TERMCAP");
676: if (sp == NULL || *sp == '\0')
677: {
678: char *termcap;
679: if ((sp = homefile("termcap.dat")) != NULL)
680: {
681: termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
682: sprintf(termcap, "TERMCAP=%s", sp);
683: free(sp);
684: putenv(termcap);
685: }
686: }
687: #endif
688: /*
689: * Find out what kind of terminal this is.
690: */
691: if ((term = getenv("TERM")) == NULL)
692: term = DEFAULT_TERM;
693: if (tgetent(termbuf, term) <= 0)
694: strcpy(termbuf, "dumb:hc:");
695:
696: hard = tgetflag("hc");
697:
698: /*
699: * Get size of the screen.
700: */
701: scrsize();
702: pos_init();
703:
704: #if DEBUG
705: if (strncmp(term,"LESSDEBUG",9) == 0)
706: {
707: get_debug_term();
708: return;
709: }
710: #endif /* DEBUG */
711:
712: auto_wrap = tgetflag("am");
713: ignaw = tgetflag("xn");
714: above_mem = tgetflag("da");
715: below_mem = tgetflag("db");
716:
717: /*
718: * Assumes termcap variable "sg" is the printing width of:
719: * the standout sequence, the end standout sequence,
720: * the underline sequence, the end underline sequence,
721: * the boldface sequence, and the end boldface sequence.
722: */
723: if ((so_s_width = tgetnum("sg")) < 0)
724: so_s_width = 0;
725: so_e_width = so_s_width;
726:
727: bo_s_width = bo_e_width = so_s_width;
728: ul_s_width = ul_e_width = so_s_width;
729: bl_s_width = bl_e_width = so_s_width;
730:
731: #if HILITE_SEARCH
732: if (so_s_width > 0 || so_e_width > 0)
733: /*
734: * Disable highlighting by default on magic cookie terminals.
735: * Turning on highlighting might change the displayed width
736: * of a line, causing the display to get messed up.
737: * The user can turn it back on with -g,
738: * but she won't like the results.
739: */
740: hilite_search = 0;
741: #endif
742:
743: /*
744: * Get various string-valued capabilities.
745: */
746: sp = sbuf;
747:
748: #if HAVE_OSPEED
749: sc_pad = tgetstr("pc", &sp);
750: if (sc_pad != NULL)
751: PC = *sc_pad;
752: #endif
753:
754: sc_s_keypad = tgetstr("ks", &sp);
755: if (sc_s_keypad == NULL)
756: sc_s_keypad = "";
757: sc_e_keypad = tgetstr("ke", &sp);
758: if (sc_e_keypad == NULL)
759: sc_e_keypad = "";
760:
1.2 etheisen 761: /*
762: * This loses for terminals with termcap entries with ti/te strings
763: * that switch to/from an alternate screen, and we're in quit_at_eof
764: * (eg, more(1)).
765: */
1.3 etheisen 766: if (!more_mode) {
1.2 etheisen 767: sc_init = tgetstr("ti", &sp);
768: sc_deinit = tgetstr("te", &sp);
769: }
1.1 etheisen 770: if (sc_init == NULL)
771: sc_init = "";
772: if (sc_deinit == NULL)
773: sc_deinit = "";
774:
775: sc_eol_clear = tgetstr("ce", &sp);
776: if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
777: {
778: cannot("clear to end of line");
779: sc_eol_clear = "";
780: }
781:
782: sc_eos_clear = tgetstr("cd", &sp);
783: if (below_mem &&
784: (hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
785: {
786: cannot("clear to end of screen");
787: sc_eol_clear = "";
788: }
789:
790: sc_clear = tgetstr("cl", &sp);
791: if (hard || sc_clear == NULL || *sc_clear == '\0')
792: {
793: cannot("clear screen");
794: sc_clear = "\n\n";
795: }
796:
797: sc_move = tgetstr("cm", &sp);
798: if (hard || sc_move == NULL || *sc_move == '\0')
799: {
800: /*
801: * This is not an error here, because we don't
802: * always need sc_move.
803: * We need it only if we don't have home or lower-left.
804: */
805: sc_move = "";
806: can_goto_line = 0;
807: } else
808: can_goto_line = 1;
809:
810: sc_s_in = tgetstr("so", &sp);
811: if (hard || sc_s_in == NULL)
812: sc_s_in = "";
813:
814: sc_s_out = tgetstr("se", &sp);
815: if (hard || sc_s_out == NULL)
816: sc_s_out = "";
817:
818: sc_u_in = tgetstr("us", &sp);
819: if (hard || sc_u_in == NULL)
820: sc_u_in = sc_s_in;
821:
822: sc_u_out = tgetstr("ue", &sp);
823: if (hard || sc_u_out == NULL)
824: sc_u_out = sc_s_out;
825:
826: sc_b_in = tgetstr("md", &sp);
827: if (hard || sc_b_in == NULL)
828: {
829: sc_b_in = sc_s_in;
830: sc_b_out = sc_s_out;
831: } else
832: {
833: sc_b_out = tgetstr("me", &sp);
834: if (hard || sc_b_out == NULL)
835: sc_b_out = "";
836: }
837:
838: sc_bl_in = tgetstr("mb", &sp);
839: if (hard || sc_bl_in == NULL)
840: {
841: sc_bl_in = sc_s_in;
842: sc_bl_out = sc_s_out;
843: } else
844: {
845: sc_bl_out = tgetstr("me", &sp);
846: if (hard || sc_bl_out == NULL)
847: sc_bl_out = "";
848: }
849:
850: sc_visual_bell = tgetstr("vb", &sp);
851: if (hard || sc_visual_bell == NULL)
852: sc_visual_bell = "";
853:
854: if (tgetflag("bs"))
855: sc_backspace = "\b";
856: else
857: {
858: sc_backspace = tgetstr("bc", &sp);
859: if (sc_backspace == NULL || *sc_backspace == '\0')
860: sc_backspace = "\b";
861: }
862:
863: /*
864: * Choose between using "ho" and "cm" ("home" and "cursor move")
865: * to move the cursor to the upper left corner of the screen.
866: */
867: t1 = tgetstr("ho", &sp);
868: if (hard || t1 == NULL)
869: t1 = "";
870: if (*sc_move == '\0')
871: t2 = "";
872: else
873: {
874: strcpy(sp, tgoto(sc_move, 0, 0));
875: t2 = sp;
876: sp += strlen(sp) + 1;
877: }
878: sc_home = cheaper(t1, t2, "home cursor", "|\b^");
879:
880: /*
881: * Choose between using "ll" and "cm" ("lower left" and "cursor move")
882: * to move the cursor to the lower left corner of the screen.
883: */
884: t1 = tgetstr("ll", &sp);
885: if (hard || t1 == NULL)
886: t1 = "";
887: if (*sc_move == '\0')
888: t2 = "";
889: else
890: {
891: strcpy(sp, tgoto(sc_move, 0, sc_height-1));
892: t2 = sp;
893: sp += strlen(sp) + 1;
894: }
895: sc_lower_left = cheaper(t1, t2,
896: "move cursor to lower left of screen", "\r");
897:
898: /*
899: * Choose between using "al" or "sr" ("add line" or "scroll reverse")
900: * to add a line at the top of the screen.
901: */
902: t1 = tgetstr("al", &sp);
903: if (hard || t1 == NULL)
904: t1 = "";
905: t2 = tgetstr("sr", &sp);
906: if (hard || t2 == NULL)
907: t2 = "";
908: #if OS2
909: if (*t1 == '\0' && *t2 == '\0')
910: sc_addline = "";
911: else
912: #endif
913: if (above_mem)
914: sc_addline = t1;
915: else
916: sc_addline = cheaper(t1, t2, "scroll backwards", "");
917: if (*sc_addline == '\0')
918: {
919: /*
920: * Force repaint on any backward movement.
921: */
922: back_scroll = 0;
923: }
924: }
925:
926: /*
927: * Return the cost of displaying a termcap string.
928: * We use the trick of calling tputs, but as a char printing function
929: * we give it inc_costcount, which just increments "costcount".
930: * This tells us how many chars would be printed by using this string.
931: * {{ Couldn't we just use strlen? }}
932: */
933: static int costcount;
934:
935: /*ARGSUSED*/
936: static int
937: inc_costcount(c)
938: int c;
939: {
940: costcount++;
941: return (c);
942: }
943:
944: static int
945: cost(t)
946: char *t;
947: {
948: costcount = 0;
949: tputs(t, sc_height, inc_costcount);
950: return (costcount);
951: }
952:
953: /*
954: * Return the "best" of the two given termcap strings.
955: * The best, if both exist, is the one with the lower
956: * cost (see cost() function).
957: */
958: static char *
959: cheaper(t1, t2, doit, def)
960: char *t1, *t2;
961: char *doit;
962: char *def;
963: {
964: if (*t1 == '\0' && *t2 == '\0')
965: {
966: cannot(doit);
967: return (def);
968: }
969: if (*t1 == '\0')
970: return (t2);
971: if (*t2 == '\0')
972: return (t1);
973: if (cost(t1) < cost(t2))
974: return (t1);
975: return (t2);
976: }
977:
978:
979: /*
980: * Below are the functions which perform all the
981: * terminal-specific screen manipulation.
982: */
983:
984:
985: /*
986: * Initialize terminal
987: */
988: public void
989: init()
990: {
991: if (no_init)
992: return;
993: tputs(sc_init, sc_height, putchr);
1.2 etheisen 994: #if 0
1.1 etheisen 995: tputs(sc_s_keypad, sc_height, putchr);
1.2 etheisen 996: #endif
1.1 etheisen 997: init_done = 1;
998: }
999:
1000: /*
1001: * Deinitialize terminal
1002: */
1003: public void
1004: deinit()
1005: {
1006: if (no_init)
1007: return;
1008: if (!init_done)
1009: return;
1.2 etheisen 1010: #if 0
1.1 etheisen 1011: tputs(sc_e_keypad, sc_height, putchr);
1.2 etheisen 1012: #endif
1.1 etheisen 1013: tputs(sc_deinit, sc_height, putchr);
1014: init_done = 0;
1015: }
1016:
1017: /*
1018: * Home cursor (move to upper left corner of screen).
1019: */
1020: public void
1021: home()
1022: {
1023: tputs(sc_home, 1, putchr);
1024: }
1025:
1026: /*
1027: * Add a blank line (called with cursor at home).
1028: * Should scroll the display down.
1029: */
1030: public void
1031: add_line()
1032: {
1033: tputs(sc_addline, sc_height, putchr);
1034: }
1035:
1036: /*
1037: * Move cursor to lower left corner of screen.
1038: */
1039: public void
1040: lower_left()
1041: {
1042: tputs(sc_lower_left, 1, putchr);
1043: }
1044:
1045: /*
1046: * Goto a specific line on the screen.
1047: */
1048: public void
1049: goto_line(slinenum)
1050: int slinenum;
1051: {
1052: char *sc_goto;
1053:
1054: sc_goto = tgoto(sc_move, 0, slinenum);
1055: tputs(sc_goto, 1, putchr);
1056: }
1057:
1058: /*
1059: * Ring the terminal bell.
1060: */
1061: public void
1062: bell()
1063: {
1064: if (quiet == VERY_QUIET)
1065: vbell();
1066: else
1067: putchr('\7');
1068: }
1069:
1070: /*
1071: * Output the "visual bell", if there is one.
1072: */
1073: public void
1074: vbell()
1075: {
1076: if (*sc_visual_bell == '\0')
1077: return;
1078: tputs(sc_visual_bell, sc_height, putchr);
1079: }
1080:
1081: /*
1082: * Clear the screen.
1083: */
1084: public void
1085: clear()
1086: {
1087: tputs(sc_clear, sc_height, putchr);
1088: }
1089:
1090: /*
1091: * Clear from the cursor to the end of the cursor's line.
1092: * {{ This must not move the cursor. }}
1093: */
1094: public void
1095: clear_eol()
1096: {
1097: tputs(sc_eol_clear, 1, putchr);
1098: }
1099:
1100: /*
1101: * Clear the bottom line of the display.
1102: * Leave the cursor at the beginning of the bottom line.
1103: */
1104: public void
1105: clear_bot()
1106: {
1107: lower_left();
1108: if (below_mem)
1109: tputs(sc_eos_clear, 1, putchr);
1110: else
1111: tputs(sc_eol_clear, 1, putchr);
1112: }
1113:
1114: /*
1115: * Begin "standout" (bold, underline, or whatever).
1116: */
1117: public void
1118: so_enter()
1119: {
1120: tputs(sc_s_in, 1, putchr);
1121: }
1122:
1123: /*
1124: * End "standout".
1125: */
1126: public void
1127: so_exit()
1128: {
1129: tputs(sc_s_out, 1, putchr);
1130: }
1131:
1132: /*
1133: * Begin "underline" (hopefully real underlining,
1134: * otherwise whatever the terminal provides).
1135: */
1136: public void
1137: ul_enter()
1138: {
1139: tputs(sc_u_in, 1, putchr);
1140: }
1141:
1142: /*
1143: * End "underline".
1144: */
1145: public void
1146: ul_exit()
1147: {
1148: tputs(sc_u_out, 1, putchr);
1149: }
1150:
1151: /*
1152: * Begin "bold"
1153: */
1154: public void
1155: bo_enter()
1156: {
1157: tputs(sc_b_in, 1, putchr);
1158: }
1159:
1160: /*
1161: * End "bold".
1162: */
1163: public void
1164: bo_exit()
1165: {
1166: tputs(sc_b_out, 1, putchr);
1167: }
1168:
1169: /*
1170: * Begin "blink"
1171: */
1172: public void
1173: bl_enter()
1174: {
1175: tputs(sc_bl_in, 1, putchr);
1176: }
1177:
1178: /*
1179: * End "blink".
1180: */
1181: public void
1182: bl_exit()
1183: {
1184: tputs(sc_bl_out, 1, putchr);
1185: }
1186:
1187: /*
1188: * Erase the character to the left of the cursor
1189: * and move the cursor left.
1190: */
1191: public void
1192: backspace()
1193: {
1194: /*
1195: * Try to erase the previous character by overstriking with a space.
1196: */
1197: tputs(sc_backspace, 1, putchr);
1198: putchr(' ');
1199: tputs(sc_backspace, 1, putchr);
1200: }
1201:
1202: /*
1203: * Output a plain backspace, without erasing the previous char.
1204: */
1205: public void
1206: putbs()
1207: {
1208: tputs(sc_backspace, 1, putchr);
1209: }