[BACK]Return to screen.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Annotation of src/usr.bin/less/screen.c, Revision 1.18

1.1       etheisen    1: /*
1.17      shadchin    2:  * Copyright (C) 1984-2012  Mark Nudelman
1.1       etheisen    3:  *
1.11      millert     4:  * You may distribute under the terms of either the GNU General Public
                      5:  * License or the Less License, as specified in the README file.
1.1       etheisen    6:  *
1.17      shadchin    7:  * For more information, see the README file.
1.1       etheisen    8:  */
1.18    ! nicm        9: /*
        !            10:  * Modified for use with illumos.
        !            11:  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
        !            12:  */
1.1       etheisen   13:
                     14: /*
                     15:  * Routines which deal with the characteristics of the terminal.
                     16:  * Uses termcap to be as terminal-independent as possible.
                     17:  */
                     18:
                     19: #include "less.h"
                     20: #include "cmd.h"
                     21:
1.15      shadchin   22: #include <termios.h>
1.18    ! nicm       23: #include <term.h>
1.11      millert    24:
1.1       etheisen   25: #define        DEFAULT_TERM            "unknown"
1.11      millert    26:
1.1       etheisen   27: /*
                     28:  * Strings passed to tputs() to do various terminal functions.
                     29:  */
                     30: static char
                     31:        *sc_home,               /* Cursor home */
                     32:        *sc_addline,            /* Add line, scroll down following lines */
                     33:        *sc_lower_left,         /* Cursor to last line, first column */
1.15      shadchin   34:        *sc_return,             /* Cursor to beginning of current line */
1.1       etheisen   35:        *sc_move,               /* General cursor positioning */
                     36:        *sc_clear,              /* Clear screen */
                     37:        *sc_eol_clear,          /* Clear to end of line */
                     38:        *sc_eos_clear,          /* Clear to end of screen */
                     39:        *sc_s_in,               /* Enter standout (highlighted) mode */
                     40:        *sc_s_out,              /* Exit standout mode */
                     41:        *sc_u_in,               /* Enter underline mode */
                     42:        *sc_u_out,              /* Exit underline mode */
                     43:        *sc_b_in,               /* Enter bold mode */
                     44:        *sc_b_out,              /* Exit bold mode */
                     45:        *sc_bl_in,              /* Enter blink mode */
                     46:        *sc_bl_out,             /* Exit blink mode */
                     47:        *sc_visual_bell,        /* Visual bell (flash screen) sequence */
                     48:        *sc_backspace,          /* Backspace cursor */
                     49:        *sc_s_keypad,           /* Start keypad mode */
                     50:        *sc_e_keypad,           /* End keypad mode */
                     51:        *sc_init,               /* Startup terminal initialization */
                     52:        *sc_deinit;             /* Exit terminal de-initialization */
                     53:
                     54: static int init_done = 0;
                     55:
1.18    ! nicm       56: int auto_wrap;                 /* Terminal does \r\n when write past margin */
        !            57: int ignaw;                     /* Terminal ignores \n immediately after wrap */
        !            58: int erase_char;                        /* The user's erase char */
        !            59: int erase2_char;               /* The user's other erase char */
        !            60: int kill_char;                 /* The user's line-kill char */
        !            61: int werase_char;               /* The user's word-erase char */
        !            62: int sc_width, sc_height;       /* Height & width of screen */
        !            63: int bo_s_width, bo_e_width;    /* Printing width of boldface seq */
        !            64: int ul_s_width, ul_e_width;    /* Printing width of underline seq */
        !            65: int so_s_width, so_e_width;    /* Printing width of standout seq */
        !            66: int bl_s_width, bl_e_width;    /* Printing width of blink seq */
        !            67: int can_goto_line;             /* Can move cursor to any line */
        !            68: int missing_cap = 0;           /* Some capability is missing */
        !            69: static int above_mem;          /* Memory retained above screen */
        !            70: static int below_mem;          /* Memory retained below screen */
1.11      millert    71:
                     72: static int attrmode = AT_NORMAL;
1.15      shadchin   73: extern int binattr;
1.1       etheisen   74:
1.18    ! nicm       75: static char *cheaper(char *, char *, char *);
        !            76: static void tmodes(char *, char *, char **, char **, char *, char *);
1.1       etheisen   77:
                     78: extern int quiet;              /* If VERY_QUIET, use visual bell for bell */
1.11      millert    79: extern int no_back_scroll;
1.1       etheisen   80: extern int swindow;
                     81: extern int no_init;
1.11      millert    82: extern int no_keypad;
1.16      millert    83: extern volatile sig_atomic_t sigs;
1.11      millert    84: extern int wscroll;
                     85: extern int screen_trashed;
                     86: extern int tty;
1.15      shadchin   87: extern int top_scroll;
                     88: extern int oldbot;
1.1       etheisen   89: extern int hilite_search;
                     90:
                     91: /*
                     92:  * Change terminal to "raw mode", or restore to "normal" mode.
1.18    ! nicm       93:  * "Raw mode" means
1.1       etheisen   94:  *     1. An outstanding read will complete on receipt of a single keystroke.
1.18    ! nicm       95:  *     2. Input is not echoed.
1.1       etheisen   96:  *     3. On output, \n is mapped to \r\n.
                     97:  *     4. \t is NOT expanded into spaces.
                     98:  *     5. Signal-causing characters such as ctrl-C (interrupt),
                     99:  *        etc. are NOT disabled.
                    100:  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
                    101:  */
1.18    ! nicm      102: void
        !           103: raw_mode(int on)
1.1       etheisen  104: {
                    105:        static int curr_on = 0;
1.18    ! nicm      106:        struct termios s;
        !           107:        static struct termios save_term;
        !           108:        static int saved_term = 0;
1.1       etheisen  109:
                    110:        if (on == curr_on)
                    111:                return;
1.15      shadchin  112:        erase2_char = '\b'; /* in case OS doesn't know about erase2 */
1.1       etheisen  113:
1.18    ! nicm      114:        if (on) {
1.1       etheisen  115:                /*
                    116:                 * Get terminal modes.
                    117:                 */
1.18    ! nicm      118:                (void) tcgetattr(tty, &s);
1.1       etheisen  119:
                    120:                /*
                    121:                 * Save modes and set certain variables dependent on modes.
                    122:                 */
1.18    ! nicm      123:                if (!saved_term) {
1.11      millert   124:                        save_term = s;
                    125:                        saved_term = 1;
                    126:                }
1.18    ! nicm      127:
1.1       etheisen  128:                erase_char = s.c_cc[VERASE];
1.15      shadchin  129: #ifdef VERASE2
                    130:                erase2_char = s.c_cc[VERASE2];
                    131: #endif
1.1       etheisen  132:                kill_char = s.c_cc[VKILL];
                    133: #ifdef VWERASE
                    134:                werase_char = s.c_cc[VWERASE];
                    135: #endif
                    136:
                    137:                /*
                    138:                 * Set the modes to the way we want them.
                    139:                 */
1.18    ! nicm      140:                s.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL);
1.1       etheisen  141:
1.18    ! nicm      142: #ifndef        TAB3
        !           143: #define        TAB3    0       /* Its a lie, but TAB3 isn't defined by POSIX. */
1.1       etheisen  144: #endif
1.18    ! nicm      145:                s.c_oflag |= (TAB3 | OPOST | ONLCR);
        !           146:                s.c_oflag &= ~(OCRNL | ONOCR | ONLRET);
1.1       etheisen  147:                s.c_cc[VMIN] = 1;
                    148:                s.c_cc[VTIME] = 0;
1.11      millert   149: #ifdef VLNEXT
                    150:                s.c_cc[VLNEXT] = 0;
                    151: #endif
                    152: #ifdef VDSUSP
                    153:                s.c_cc[VDSUSP] = 0;
                    154: #endif
1.18    ! nicm      155:        } else {
1.11      millert   156:                /*
                    157:                 * Restore saved modes.
                    158:                 */
                    159:                s = save_term;
                    160:        }
1.18    ! nicm      161:        (void) tcsetattr(tty, TCSASOFT | TCSADRAIN, &s);
        !           162:        (void) fsync(tty);
1.1       etheisen  163:        curr_on = on;
                    164: }
                    165:
1.11      millert   166: /*
                    167:  * Some glue to prevent calling termcap functions if tgetent() failed.
                    168:  */
                    169: static int hardcopy;
                    170:
1.1       etheisen  171: /*
1.11      millert   172:  * Get size of the output screen.
1.1       etheisen  173:  */
1.18    ! nicm      174: static void
        !           175: scrsize(void)
1.1       etheisen  176: {
1.18    ! nicm      177:        char *s;
1.11      millert   178:        int sys_height;
                    179:        int sys_width;
                    180:        int n;
1.18    ! nicm      181: #ifdef TIOCGWINSIZE
        !           182:        struct winsize w;
1.11      millert   183: #endif
1.1       etheisen  184:
1.11      millert   185: #define        DEF_SC_WIDTH    80
                    186: #define        DEF_SC_HEIGHT   24
1.1       etheisen  187:
1.11      millert   188:        sys_width = sys_height = 0;
1.1       etheisen  189:
1.18    ! nicm      190: #ifdef TIOCGWINSIZE
        !           191:        if (ioctl(2, TIOCGWINSZ, &w) == 0) {
        !           192:                if (w.ws_row > 0)
        !           193:                        sys_height = w.ws_row;
        !           194:                if (w.ws_col > 0)
        !           195:                        sys_width = w.ws_col;
1.1       etheisen  196:        }
1.11      millert   197: #endif
1.1       etheisen  198:
1.11      millert   199:        if (sys_height > 0)
                    200:                sc_height = sys_height;
                    201:        else if ((s = lgetenv("LINES")) != NULL)
                    202:                sc_height = atoi(s);
1.18    ! nicm      203:        else if (!hardcopy && (n = lines) > 0)
        !           204:                sc_height = n;
1.17      shadchin  205:        if (sc_height <= 0)
1.11      millert   206:                sc_height = DEF_SC_HEIGHT;
1.1       etheisen  207:
1.11      millert   208:        if (sys_width > 0)
                    209:                sc_width = sys_width;
                    210:        else if ((s = lgetenv("COLUMNS")) != NULL)
                    211:                sc_width = atoi(s);
1.18    ! nicm      212:        else if (!hardcopy && (n = columns) > 0)
        !           213:                sc_width = n;
1.17      shadchin  214:        if (sc_width <= 0)
1.11      millert   215:                sc_width = DEF_SC_WIDTH;
                    216: }
1.1       etheisen  217:
                    218: /*
1.11      millert   219:  * Return the characters actually input by a "special" key.
1.1       etheisen  220:  */
1.18    ! nicm      221: char *
        !           222: special_key_str(int key)
1.1       etheisen  223: {
1.11      millert   224:        char *s;
1.18    ! nicm      225:        static char ctrlk[] = { CONTROL('K'), 0 };
        !           226:
        !           227:        if (hardcopy)
        !           228:                return (NULL);
1.1       etheisen  229:
1.18    ! nicm      230:        switch (key) {
1.11      millert   231:        case SK_RIGHT_ARROW:
1.18    ! nicm      232:                s = key_right;
1.11      millert   233:                break;
                    234:        case SK_LEFT_ARROW:
1.18    ! nicm      235:                s = key_left;
1.11      millert   236:                break;
                    237:        case SK_UP_ARROW:
1.18    ! nicm      238:                s = key_up;
1.11      millert   239:                break;
                    240:        case SK_DOWN_ARROW:
1.18    ! nicm      241:                s = key_down;
1.11      millert   242:                break;
                    243:        case SK_PAGE_UP:
1.18    ! nicm      244:                s = key_ppage;
1.11      millert   245:                break;
                    246:        case SK_PAGE_DOWN:
1.18    ! nicm      247:                s = key_npage;
1.11      millert   248:                break;
                    249:        case SK_HOME:
1.18    ! nicm      250:                s = key_home;
1.11      millert   251:                break;
                    252:        case SK_END:
1.18    ! nicm      253:                s = key_end;
1.11      millert   254:                break;
                    255:        case SK_DELETE:
1.18    ! nicm      256:                s = key_dc;
        !           257:                if (s == NULL) {
        !           258:                        s = "\177\0";
1.11      millert   259:                }
                    260:                break;
                    261:        case SK_CONTROL_K:
1.18    ! nicm      262:                s = ctrlk;
1.11      millert   263:                break;
                    264:        default:
                    265:                return (NULL);
                    266:        }
                    267:        return (s);
                    268: }
                    269:
                    270: /*
                    271:  * Get terminal capabilities via termcap.
                    272:  */
1.18    ! nicm      273: void
        !           274: get_term(void)
1.11      millert   275: {
1.18    ! nicm      276:        char *t1, *t2;
1.11      millert   277:        char *term;
                    278:
1.1       etheisen  279:        /*
                    280:         * Find out what kind of terminal this is.
                    281:         */
1.18    ! nicm      282:        if ((term = lgetenv("TERM")) == NULL)
        !           283:                term = DEFAULT_TERM;
1.11      millert   284:        hardcopy = 0;
1.18    ! nicm      285:
        !           286:        if (setupterm(term, 1, NULL) < 0) {
        !           287:                hardcopy = 1;
        !           288:        }
        !           289:        if (hard_copy == 1)
1.11      millert   290:                hardcopy = 1;
1.1       etheisen  291:
                    292:        /*
                    293:         * Get size of the screen.
                    294:         */
                    295:        scrsize();
                    296:        pos_init();
                    297:
1.18    ! nicm      298:        auto_wrap = hardcopy ? 0 : auto_right_margin;
        !           299:        ignaw = hardcopy ? 0 : eat_newline_glitch;
        !           300:        above_mem = hardcopy ? 0 : memory_above;
        !           301:        below_mem = hardcopy ? 0 : memory_below;
1.1       etheisen  302:
                    303:        /*
                    304:         * Assumes termcap variable "sg" is the printing width of:
                    305:         * the standout sequence, the end standout sequence,
                    306:         * the underline sequence, the end underline sequence,
                    307:         * the boldface sequence, and the end boldface sequence.
                    308:         */
1.18    ! nicm      309:        if (hardcopy || (so_s_width = magic_cookie_glitch) < 0)
1.1       etheisen  310:                so_s_width = 0;
                    311:        so_e_width = so_s_width;
                    312:
                    313:        bo_s_width = bo_e_width = so_s_width;
                    314:        ul_s_width = ul_e_width = so_s_width;
                    315:        bl_s_width = bl_e_width = so_s_width;
                    316:
                    317:        if (so_s_width > 0 || so_e_width > 0)
                    318:                /*
                    319:                 * Disable highlighting by default on magic cookie terminals.
                    320:                 * Turning on highlighting might change the displayed width
                    321:                 * of a line, causing the display to get messed up.
1.18    ! nicm      322:                 * The user can turn it back on with -g,
1.1       etheisen  323:                 * but she won't like the results.
                    324:                 */
                    325:                hilite_search = 0;
                    326:
                    327:        /*
                    328:         * Get various string-valued capabilities.
                    329:         */
                    330:
1.18    ! nicm      331:        sc_s_keypad = keypad_xmit;
        !           332:        if (hardcopy || sc_s_keypad == NULL)
1.1       etheisen  333:                sc_s_keypad = "";
1.18    ! nicm      334:        sc_e_keypad = keypad_local;
        !           335:        if (hardcopy || sc_e_keypad == NULL)
1.1       etheisen  336:                sc_e_keypad = "";
1.18    ! nicm      337:
        !           338:        sc_init = enter_ca_mode;
        !           339:        if (hardcopy || sc_init == NULL)
1.1       etheisen  340:                sc_init = "";
1.11      millert   341:
1.18    ! nicm      342:        sc_deinit = exit_ca_mode;
        !           343:        if (hardcopy || sc_deinit == NULL)
1.1       etheisen  344:                sc_deinit = "";
                    345:
1.18    ! nicm      346:        sc_eol_clear = clr_eol;
        !           347:        if (hardcopy || sc_eol_clear == NULL || *sc_eol_clear == '\0') {
1.11      millert   348:                missing_cap = 1;
1.1       etheisen  349:                sc_eol_clear = "";
                    350:        }
                    351:
1.18    ! nicm      352:        sc_eos_clear = clr_eos;
        !           353:        if (below_mem &&
        !           354:            (hardcopy || sc_eos_clear == NULL || *sc_eos_clear == '\0')) {
1.11      millert   355:                missing_cap = 1;
1.15      shadchin  356:                sc_eos_clear = "";
1.1       etheisen  357:        }
                    358:
1.18    ! nicm      359:        sc_clear = clear_screen;
        !           360:        if (hardcopy || sc_clear == NULL || *sc_clear == '\0') {
1.11      millert   361:                missing_cap = 1;
1.1       etheisen  362:                sc_clear = "\n\n";
                    363:        }
                    364:
1.18    ! nicm      365:        sc_move = cursor_address;
        !           366:        if (hardcopy || sc_move == NULL || *sc_move == '\0') {
1.1       etheisen  367:                /*
1.18    ! nicm      368:                 * This is not an error here, because we don't
1.1       etheisen  369:                 * always need sc_move.
                    370:                 * We need it only if we don't have home or lower-left.
                    371:                 */
                    372:                sc_move = "";
                    373:                can_goto_line = 0;
1.18    ! nicm      374:        } else {
1.1       etheisen  375:                can_goto_line = 1;
1.18    ! nicm      376:        }
1.1       etheisen  377:
1.18    ! nicm      378:        tmodes(enter_standout_mode, exit_standout_mode, &sc_s_in, &sc_s_out,
        !           379:            "", "");
        !           380:        tmodes(enter_underline_mode, exit_underline_mode, &sc_u_in, &sc_u_out,
        !           381:            sc_s_in, sc_s_out);
        !           382:        tmodes(enter_bold_mode, exit_attribute_mode, &sc_b_in, &sc_b_out,
        !           383:            sc_s_in, sc_s_out);
        !           384:        tmodes(enter_blink_mode, exit_attribute_mode, &sc_bl_in, &sc_bl_out,
        !           385:            sc_s_in, sc_s_out);
1.1       etheisen  386:
1.18    ! nicm      387:        sc_visual_bell = flash_screen;
        !           388:        if (hardcopy || sc_visual_bell == NULL)
1.1       etheisen  389:                sc_visual_bell = "";
                    390:
1.18    ! nicm      391:        sc_backspace = "\b";
1.1       etheisen  392:
                    393:        /*
                    394:         * Choose between using "ho" and "cm" ("home" and "cursor move")
                    395:         * to move the cursor to the upper left corner of the screen.
                    396:         */
1.18    ! nicm      397:        t1 = cursor_home;
        !           398:        if (hardcopy || t1 == NULL)
1.1       etheisen  399:                t1 = "";
1.18    ! nicm      400:        if (*sc_move == '\0') {
1.1       etheisen  401:                t2 = "";
1.18    ! nicm      402:        } else {
        !           403:                t2 = estrdup(tparm(sc_move, 0, 0, 0, 0, 0, 0, 0, 0, 0));
1.1       etheisen  404:        }
1.11      millert   405:        sc_home = cheaper(t1, t2, "|\b^");
1.1       etheisen  406:
                    407:        /*
                    408:         * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
                    409:         * to move the cursor to the lower left corner of the screen.
                    410:         */
1.18    ! nicm      411:        t1 = cursor_to_ll;
        !           412:        if (hardcopy || t1 == NULL)
1.1       etheisen  413:                t1 = "";
1.18    ! nicm      414:        if (*sc_move == '\0') {
1.1       etheisen  415:                t2 = "";
1.18    ! nicm      416:        } else {
        !           417:                t2 = estrdup(tparm(sc_move, sc_height-1,
        !           418:                    0, 0, 0, 0, 0, 0, 0, 0));
1.1       etheisen  419:        }
1.11      millert   420:        sc_lower_left = cheaper(t1, t2, "\r");
1.1       etheisen  421:
                    422:        /*
1.15      shadchin  423:         * Get carriage return string.
                    424:         */
1.18    ! nicm      425:        sc_return = carriage_return;
        !           426:        if (hardcopy || sc_return == NULL)
1.15      shadchin  427:                sc_return = "\r";
                    428:
                    429:        /*
1.18    ! nicm      430:         * Choose between using insert_line or scroll_reverse
1.1       etheisen  431:         * to add a line at the top of the screen.
                    432:         */
1.18    ! nicm      433:        t1 = insert_line;
        !           434:        if (hardcopy || t1 == NULL)
1.1       etheisen  435:                t1 = "";
1.18    ! nicm      436:        t2 = scroll_reverse;
        !           437:        if (hardcopy || t2 == NULL)
1.1       etheisen  438:                t2 = "";
                    439:        if (above_mem)
                    440:                sc_addline = t1;
                    441:        else
1.11      millert   442:                sc_addline = cheaper(t1, t2, "");
1.18    ! nicm      443:        if (*sc_addline == '\0') {
1.1       etheisen  444:                /*
                    445:                 * Force repaint on any backward movement.
                    446:                 */
1.11      millert   447:                no_back_scroll = 1;
1.1       etheisen  448:        }
                    449: }
                    450:
                    451: /*
                    452:  * Return the cost of displaying a termcap string.
                    453:  * We use the trick of calling tputs, but as a char printing function
                    454:  * we give it inc_costcount, which just increments "costcount".
                    455:  * This tells us how many chars would be printed by using this string.
                    456:  * {{ Couldn't we just use strlen? }}
                    457:  */
                    458: static int costcount;
                    459:
1.18    ! nicm      460: static int
        !           461: inc_costcount(int c)
1.1       etheisen  462: {
                    463:        costcount++;
                    464:        return (c);
                    465: }
                    466:
1.18    ! nicm      467: static int
        !           468: cost(char *t)
1.1       etheisen  469: {
                    470:        costcount = 0;
1.18    ! nicm      471:        (void) tputs(t, sc_height, inc_costcount);
1.1       etheisen  472:        return (costcount);
                    473: }
                    474:
                    475: /*
                    476:  * Return the "best" of the two given termcap strings.
1.18    ! nicm      477:  * The best, if both exist, is the one with the lower
1.1       etheisen  478:  * cost (see cost() function).
                    479:  */
1.18    ! nicm      480: static char *
        !           481: cheaper(char *t1, char *t2, char *def)
1.1       etheisen  482: {
1.18    ! nicm      483:        if (*t1 == '\0' && *t2 == '\0') {
1.11      millert   484:                missing_cap = 1;
1.1       etheisen  485:                return (def);
                    486:        }
                    487:        if (*t1 == '\0')
                    488:                return (t2);
                    489:        if (*t2 == '\0')
                    490:                return (t1);
                    491:        if (cost(t1) < cost(t2))
                    492:                return (t1);
                    493:        return (t2);
                    494: }
                    495:
1.18    ! nicm      496: static void
        !           497: tmodes(char *incap, char *outcap, char **instr, char **outstr,
        !           498:     char *def_instr, char *def_outstr)
1.11      millert   499: {
1.18    ! nicm      500:        if (hardcopy) {
        !           501:                *instr = "";
        !           502:                *outstr = "";
        !           503:                return;
        !           504:        }
        !           505:
        !           506:        *instr = incap;
        !           507:        *outstr = outcap;
        !           508:
        !           509:        if (*instr == NULL) {
1.11      millert   510:                /* Use defaults. */
                    511:                *instr = def_instr;
                    512:                *outstr = def_outstr;
                    513:                return;
                    514:        }
                    515:
                    516:        if (*outstr == NULL)
1.18    ! nicm      517:                /* No specific out capability; use exit_attribute_mode. */
        !           518:                *outstr = exit_attribute_mode;
1.11      millert   519:        if (*outstr == NULL)
1.18    ! nicm      520:                /* Don't even have that, use an empty string */
1.11      millert   521:                *outstr = "";
                    522: }
                    523:
1.1       etheisen  524: /*
1.18    ! nicm      525:  * Below are the functions which perform all the
1.1       etheisen  526:  * terminal-specific screen manipulation.
                    527:  */
                    528:
                    529: /*
                    530:  * Initialize terminal
                    531:  */
1.18    ! nicm      532: void
        !           533: init(void)
1.1       etheisen  534: {
1.11      millert   535:        if (!no_init)
1.18    ! nicm      536:                (void) tputs(sc_init, sc_height, putchr);
1.11      millert   537:        if (!no_keypad)
1.18    ! nicm      538:                (void) tputs(sc_s_keypad, sc_height, putchr);
        !           539:        if (top_scroll) {
1.15      shadchin  540:                int i;
                    541:
                    542:                /*
                    543:                 * This is nice to terminals with no alternate screen,
                    544:                 * but with saved scrolled-off-the-top lines.  This way,
                    545:                 * no previous line is lost, but we start with a whole
                    546:                 * screen to ourself.
                    547:                 */
                    548:                for (i = 1; i < sc_height; i++)
1.18    ! nicm      549:                        (void) putchr('\n');
1.15      shadchin  550:        } else
                    551:                line_left();
1.1       etheisen  552:        init_done = 1;
                    553: }
                    554:
                    555: /*
                    556:  * Deinitialize terminal
                    557:  */
1.18    ! nicm      558: void
        !           559: deinit(void)
1.1       etheisen  560: {
                    561:        if (!init_done)
                    562:                return;
1.11      millert   563:        if (!no_keypad)
1.18    ! nicm      564:                (void) tputs(sc_e_keypad, sc_height, putchr);
1.11      millert   565:        if (!no_init)
1.18    ! nicm      566:                (void) tputs(sc_deinit, sc_height, putchr);
1.1       etheisen  567:        init_done = 0;
                    568: }
                    569:
                    570: /*
                    571:  * Home cursor (move to upper left corner of screen).
                    572:  */
1.18    ! nicm      573: void
        !           574: home(void)
1.1       etheisen  575: {
1.18    ! nicm      576:        (void) tputs(sc_home, 1, putchr);
1.1       etheisen  577: }
                    578:
                    579: /*
                    580:  * Add a blank line (called with cursor at home).
                    581:  * Should scroll the display down.
                    582:  */
1.18    ! nicm      583: void
        !           584: add_line(void)
1.11      millert   585: {
1.18    ! nicm      586:        (void) tputs(sc_addline, sc_height, putchr);
1.11      millert   587: }
1.1       etheisen  588:
                    589: /*
                    590:  * Move cursor to lower left corner of screen.
                    591:  */
1.18    ! nicm      592: void
        !           593: lower_left(void)
1.1       etheisen  594: {
1.18    ! nicm      595:        (void) tputs(sc_lower_left, 1, putchr);
1.11      millert   596: }
                    597:
                    598: /*
1.15      shadchin  599:  * Move cursor to left position of current line.
                    600:  */
1.18    ! nicm      601: void
        !           602: line_left(void)
1.15      shadchin  603: {
1.18    ! nicm      604:        (void) tputs(sc_return, 1, putchr);
1.15      shadchin  605: }
                    606:
                    607: /*
1.18    ! nicm      608:  * Goto a specific line on the screen.
1.11      millert   609:  */
1.18    ! nicm      610: void
        !           611: goto_line(int slinenum)
1.11      millert   612: {
1.18    ! nicm      613:        (void) tputs(tparm(sc_move, slinenum, 0, 0, 0, 0, 0, 0, 0, 0), 1,
        !           614:            putchr);
1.1       etheisen  615: }
                    616:
                    617: /*
                    618:  * Output the "visual bell", if there is one.
                    619:  */
1.18    ! nicm      620: void
        !           621: vbell(void)
1.1       etheisen  622: {
                    623:        if (*sc_visual_bell == '\0')
                    624:                return;
1.18    ! nicm      625:        (void) tputs(sc_visual_bell, sc_height, putchr);
1.11      millert   626: }
                    627:
                    628: /*
                    629:  * Make a noise.
                    630:  */
1.18    ! nicm      631: static void
        !           632: beep(void)
1.11      millert   633: {
1.18    ! nicm      634:        (void) putchr(CONTROL('G'));
1.11      millert   635: }
                    636:
                    637: /*
                    638:  * Ring the terminal bell.
                    639:  */
1.18    ! nicm      640: void
        !           641: ring_bell(void)
1.11      millert   642: {
                    643:        if (quiet == VERY_QUIET)
                    644:                vbell();
                    645:        else
                    646:                beep();
1.1       etheisen  647: }
                    648:
                    649: /*
                    650:  * Clear the screen.
                    651:  */
1.18    ! nicm      652: void
        !           653: do_clear(void)
1.1       etheisen  654: {
1.18    ! nicm      655:        (void) tputs(sc_clear, sc_height, putchr);
1.1       etheisen  656: }
                    657:
                    658: /*
                    659:  * Clear from the cursor to the end of the cursor's line.
                    660:  * {{ This must not move the cursor. }}
                    661:  */
1.18    ! nicm      662: void
        !           663: clear_eol(void)
1.1       etheisen  664: {
1.18    ! nicm      665:        (void) tputs(sc_eol_clear, 1, putchr);
1.11      millert   666: }
                    667:
                    668: /*
                    669:  * Clear the current line.
                    670:  * Clear the screen if there's off-screen memory below the display.
                    671:  */
1.18    ! nicm      672: static void
        !           673: clear_eol_bot(void)
1.11      millert   674: {
                    675:        if (below_mem)
1.18    ! nicm      676:                (void) tputs(sc_eos_clear, 1, putchr);
1.11      millert   677:        else
1.18    ! nicm      678:                (void) tputs(sc_eol_clear, 1, putchr);
1.1       etheisen  679: }
                    680:
                    681: /*
                    682:  * Clear the bottom line of the display.
                    683:  * Leave the cursor at the beginning of the bottom line.
                    684:  */
1.18    ! nicm      685: void
        !           686: clear_bot(void)
1.1       etheisen  687: {
1.11      millert   688:        /*
                    689:         * If we're in a non-normal attribute mode, temporarily exit
                    690:         * the mode while we do the clear.  Some terminals fill the
                    691:         * cleared area with the current attribute.
                    692:         */
1.15      shadchin  693:        if (oldbot)
                    694:                lower_left();
                    695:        else
                    696:                line_left();
                    697:
                    698:        if (attrmode == AT_NORMAL)
                    699:                clear_eol_bot();
                    700:        else
1.11      millert   701:        {
1.15      shadchin  702:                int saved_attrmode = attrmode;
                    703:
                    704:                at_exit();
1.11      millert   705:                clear_eol_bot();
1.15      shadchin  706:                at_enter(saved_attrmode);
1.11      millert   707:        }
1.1       etheisen  708: }
                    709:
1.18    ! nicm      710: void
        !           711: at_enter(int attr)
1.1       etheisen  712: {
1.15      shadchin  713:        attr = apply_at_specials(attr);
                    714:
                    715:        /* The one with the most priority is last.  */
                    716:        if (attr & AT_UNDERLINE)
1.18    ! nicm      717:                (void) tputs(sc_u_in, 1, putchr);
1.15      shadchin  718:        if (attr & AT_BOLD)
1.18    ! nicm      719:                (void) tputs(sc_b_in, 1, putchr);
1.15      shadchin  720:        if (attr & AT_BLINK)
1.18    ! nicm      721:                (void) tputs(sc_bl_in, 1, putchr);
1.15      shadchin  722:        if (attr & AT_STANDOUT)
1.18    ! nicm      723:                (void) tputs(sc_s_in, 1, putchr);
1.15      shadchin  724:
                    725:        attrmode = attr;
1.1       etheisen  726: }
                    727:
1.18    ! nicm      728: void
        !           729: at_exit(void)
1.1       etheisen  730: {
1.15      shadchin  731:        /* Undo things in the reverse order we did them.  */
                    732:        if (attrmode & AT_STANDOUT)
1.18    ! nicm      733:                (void) tputs(sc_s_out, 1, putchr);
1.15      shadchin  734:        if (attrmode & AT_BLINK)
1.18    ! nicm      735:                (void) tputs(sc_bl_out, 1, putchr);
1.15      shadchin  736:        if (attrmode & AT_BOLD)
1.18    ! nicm      737:                (void) tputs(sc_b_out, 1, putchr);
1.15      shadchin  738:        if (attrmode & AT_UNDERLINE)
1.18    ! nicm      739:                (void) tputs(sc_u_out, 1, putchr);
1.15      shadchin  740:
1.11      millert   741:        attrmode = AT_NORMAL;
1.1       etheisen  742: }
                    743:
1.18    ! nicm      744: void
        !           745: at_switch(int attr)
1.1       etheisen  746: {
1.15      shadchin  747:        int new_attrmode = apply_at_specials(attr);
                    748:        int ignore_modes = AT_ANSI;
1.1       etheisen  749:
1.18    ! nicm      750:        if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes)) {
1.15      shadchin  751:                at_exit();
                    752:                at_enter(attr);
                    753:        }
1.1       etheisen  754: }
                    755:
1.18    ! nicm      756: int
        !           757: is_at_equiv(int attr1, int attr2)
1.1       etheisen  758: {
1.15      shadchin  759:        attr1 = apply_at_specials(attr1);
                    760:        attr2 = apply_at_specials(attr2);
1.1       etheisen  761:
1.15      shadchin  762:        return (attr1 == attr2);
1.1       etheisen  763: }
                    764:
1.18    ! nicm      765: int
        !           766: apply_at_specials(int attr)
1.1       etheisen  767: {
1.15      shadchin  768:        if (attr & AT_BINARY)
                    769:                attr |= binattr;
                    770:        if (attr & AT_HILITE)
                    771:                attr |= AT_STANDOUT;
                    772:        attr &= ~(AT_BINARY|AT_HILITE);
1.1       etheisen  773:
1.18    ! nicm      774:        return (attr);
1.1       etheisen  775: }
                    776:
                    777: /*
                    778:  * Output a plain backspace, without erasing the previous char.
                    779:  */
1.18    ! nicm      780: void
        !           781: putbs(void)
1.15      shadchin  782: {
1.18    ! nicm      783:        (void) tputs(sc_backspace, 1, putchr);
1.11      millert   784: }