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