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

Annotation of src/usr.bin/less/cmdbuf.c, Revision 1.10

1.1       etheisen    1: /*
1.7       shadchin    2:  * Copyright (C) 1984-2012  Mark Nudelman
1.9       nicm        3:  * Modified for use with illumos by Garrett D'Amore.
                      4:  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
1.1       etheisen    5:  *
1.4       millert     6:  * You may distribute under the terms of either the GNU General Public
                      7:  * License or the Less License, as specified in the README file.
1.1       etheisen    8:  *
1.7       shadchin    9:  * For more information, see the README file.
1.8       nicm       10:  */
1.1       etheisen   11:
                     12: /*
                     13:  * Functions which manipulate the command buffer.
                     14:  * Used only by command() and related functions.
                     15:  */
                     16:
                     17: #include "less.h"
                     18: #include "cmd.h"
1.5       shadchin   19: #include "charset.h"
                     20: #include <sys/stat.h>
1.1       etheisen   21:
                     22: extern int sc_width;
1.5       shadchin   23: extern int utf_mode;
1.1       etheisen   24:
1.4       millert    25: static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */
                     26: static int cmd_col;            /* Current column of the cursor */
                     27: static int prompt_col;         /* Column of cursor just after prompt */
1.1       etheisen   28: static char *cp;               /* Pointer into cmdbuf */
1.4       millert    29: static int cmd_offset;         /* Index into cmdbuf of first displayed char */
                     30: static int literal;            /* Next input char should not be interpreted */
1.7       shadchin   31: static int updown_match = -1;  /* Prefix length in up/down movement */
1.1       etheisen   32:
1.8       nicm       33: static int cmd_complete(int);
1.1       etheisen   34: /*
                     35:  * These variables are statics used by cmd_complete.
                     36:  */
                     37: static int in_completion = 0;
                     38: static char *tk_text;
                     39: static char *tk_original;
                     40: static char *tk_ipoint;
                     41: static char *tk_trial;
                     42: static struct textlist tk_tlist;
1.4       millert    43:
1.8       nicm       44: static int cmd_left(void);
                     45: static int cmd_right(void);
1.4       millert    46:
1.8       nicm       47: char openquote = '"';
                     48: char closequote = '"';
1.5       shadchin   49:
                     50: /* History file */
1.8       nicm       51: #define        HISTFILE_FIRST_LINE     ".less-history-file:"
                     52: #define        HISTFILE_SEARCH_SECTION ".search"
                     53: #define        HISTFILE_SHELL_SECTION  ".shell"
1.5       shadchin   54:
1.1       etheisen   55: /*
                     56:  * A mlist structure represents a command history.
                     57:  */
                     58: struct mlist
                     59: {
                     60:        struct mlist *next;
                     61:        struct mlist *prev;
                     62:        struct mlist *curr_mp;
                     63:        char *string;
1.5       shadchin   64:        int modified;
1.1       etheisen   65: };
                     66:
                     67: /*
                     68:  * These are the various command histories that exist.
                     69:  */
1.8       nicm       70: struct mlist mlist_search =
1.5       shadchin   71:        { &mlist_search,  &mlist_search,  &mlist_search,  NULL, 0 };
1.8       nicm       72: void * const ml_search = (void *) &mlist_search;
1.4       millert    73:
1.8       nicm       74: struct mlist mlist_examine =
1.5       shadchin   75:        { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 };
1.8       nicm       76: void * const ml_examine = (void *) &mlist_examine;
1.4       millert    77:
1.8       nicm       78: struct mlist mlist_shell =
1.5       shadchin   79:        { &mlist_shell,   &mlist_shell,   &mlist_shell,   NULL, 0 };
1.8       nicm       80: void * const ml_shell = (void *) &mlist_shell;
1.1       etheisen   81:
                     82: /*
                     83:  * History for the current command.
                     84:  */
                     85: static struct mlist *curr_mlist = NULL;
1.4       millert    86: static int curr_cmdflags;
1.1       etheisen   87:
1.5       shadchin   88: static char cmd_mbc_buf[MAX_UTF_CHAR_LEN];
                     89: static int cmd_mbc_buf_len;
                     90: static int cmd_mbc_buf_index;
                     91:
1.1       etheisen   92:
                     93: /*
                     94:  * Reset command buffer (to empty).
                     95:  */
1.8       nicm       96: void
                     97: cmd_reset(void)
1.1       etheisen   98: {
                     99:        cp = cmdbuf;
                    100:        *cp = '\0';
                    101:        cmd_col = 0;
1.4       millert   102:        cmd_offset = 0;
1.1       etheisen  103:        literal = 0;
1.5       shadchin  104:        cmd_mbc_buf_len = 0;
1.7       shadchin  105:        updown_match = -1;
1.1       etheisen  106: }
                    107:
                    108: /*
1.5       shadchin  109:  * Clear command line.
1.4       millert   110:  */
1.8       nicm      111: void
                    112: clear_cmd(void)
1.4       millert   113: {
                    114:        cmd_col = prompt_col = 0;
1.5       shadchin  115:        cmd_mbc_buf_len = 0;
1.7       shadchin  116:        updown_match = -1;
1.4       millert   117: }
                    118:
                    119: /*
                    120:  * Display a string, usually as a prompt for input into the command buffer.
                    121:  */
1.8       nicm      122: void
                    123: cmd_putstr(char *s)
1.4       millert   124: {
1.5       shadchin  125:        LWCHAR prev_ch = 0;
                    126:        LWCHAR ch;
                    127:        char *endline = s + strlen(s);
1.8       nicm      128:        while (*s != '\0') {
1.5       shadchin  129:                char *ns = s;
                    130:                ch = step_char(&ns, +1, endline);
                    131:                while (s < ns)
                    132:                        putchr(*s++);
1.8       nicm      133:                if (!utf_mode) {
1.5       shadchin  134:                        cmd_col++;
                    135:                        prompt_col++;
1.8       nicm      136:                } else if (!is_composing_char(ch) &&
                    137:                    !is_combining_char(prev_ch, ch)) {
1.5       shadchin  138:                        int width = is_wide_char(ch) ? 2 : 1;
                    139:                        cmd_col += width;
                    140:                        prompt_col += width;
                    141:                }
                    142:                prev_ch = ch;
                    143:        }
1.4       millert   144: }
                    145:
                    146: /*
1.1       etheisen  147:  * How many characters are in the command buffer?
                    148:  */
1.8       nicm      149: int
                    150: len_cmdbuf(void)
1.1       etheisen  151: {
1.5       shadchin  152:        char *s = cmdbuf;
                    153:        char *endline = s + strlen(s);
                    154:        int len = 0;
                    155:
1.8       nicm      156:        while (*s != '\0') {
1.5       shadchin  157:                step_char(&s, +1, endline);
                    158:                len++;
                    159:        }
                    160:        return (len);
                    161: }
                    162:
                    163: /*
                    164:  * Common part of cmd_step_right() and cmd_step_left().
                    165:  */
1.8       nicm      166: static char *
                    167: cmd_step_common(char *p, LWCHAR ch, int len, int *pwidth, int *bswidth)
1.5       shadchin  168: {
                    169:        char *pr;
                    170:
1.8       nicm      171:        if (len == 1) {
                    172:                pr = prchar((int)ch);
                    173:                if (pwidth != NULL || bswidth != NULL) {
1.5       shadchin  174:                        int len = strlen(pr);
                    175:                        if (pwidth != NULL)
                    176:                                *pwidth = len;
                    177:                        if (bswidth != NULL)
                    178:                                *bswidth = len;
                    179:                }
1.8       nicm      180:        } else {
1.5       shadchin  181:                pr = prutfchar(ch);
1.8       nicm      182:                if (pwidth != NULL || bswidth != NULL) {
                    183:                        if (is_composing_char(ch)) {
1.5       shadchin  184:                                if (pwidth != NULL)
                    185:                                        *pwidth = 0;
                    186:                                if (bswidth != NULL)
                    187:                                        *bswidth = 0;
1.8       nicm      188:                        } else if (is_ubin_char(ch)) {
1.5       shadchin  189:                                int len = strlen(pr);
                    190:                                if (pwidth != NULL)
                    191:                                        *pwidth = len;
                    192:                                if (bswidth != NULL)
                    193:                                        *bswidth = len;
1.8       nicm      194:                        } else {
1.5       shadchin  195:                                LWCHAR prev_ch = step_char(&p, -1, cmdbuf);
1.8       nicm      196:                                if (is_combining_char(prev_ch, ch)) {
1.5       shadchin  197:                                        if (pwidth != NULL)
                    198:                                                *pwidth = 0;
                    199:                                        if (bswidth != NULL)
                    200:                                                *bswidth = 0;
1.8       nicm      201:                                } else {
1.5       shadchin  202:                                        if (pwidth != NULL)
                    203:                                                *pwidth = is_wide_char(ch)
1.8       nicm      204:                                                    ? 2 : 1;
1.5       shadchin  205:                                        if (bswidth != NULL)
                    206:                                                *bswidth = 1;
                    207:                                }
                    208:                        }
                    209:                }
                    210:        }
                    211:
                    212:        return (pr);
                    213: }
                    214:
                    215: /*
                    216:  * Step a pointer one character right in the command buffer.
                    217:  */
1.8       nicm      218: static char *
                    219: cmd_step_right(char **pp, int *pwidth, int *bswidth)
1.5       shadchin  220: {
                    221:        char *p = *pp;
                    222:        LWCHAR ch = step_char(pp, +1, p + strlen(p));
                    223:
1.8       nicm      224:        return (cmd_step_common(p, ch, *pp - p, pwidth, bswidth));
1.5       shadchin  225: }
                    226:
                    227: /*
                    228:  * Step a pointer one character left in the command buffer.
                    229:  */
1.8       nicm      230: static char *
                    231: cmd_step_left(char **pp, int *pwidth, int *bswidth)
1.5       shadchin  232: {
                    233:        char *p = *pp;
                    234:        LWCHAR ch = step_char(pp, -1, cmdbuf);
                    235:
1.8       nicm      236:        return (cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth));
1.1       etheisen  237: }
                    238:
                    239: /*
1.4       millert   240:  * Repaint the line from cp onwards.
                    241:  * Then position the cursor just after the char old_cp (a pointer into cmdbuf).
                    242:  */
1.8       nicm      243: static void
                    244: cmd_repaint(char *old_cp)
1.4       millert   245: {
                    246:        /*
                    247:         * Repaint the line from the current position.
                    248:         */
                    249:        clear_eol();
1.8       nicm      250:        while (*cp != '\0') {
1.5       shadchin  251:                char *np = cp;
                    252:                int width;
                    253:                char *pr = cmd_step_right(&np, &width, NULL);
                    254:                if (cmd_col + width >= sc_width)
1.4       millert   255:                        break;
1.5       shadchin  256:                cp = np;
                    257:                putstr(pr);
                    258:                cmd_col += width;
                    259:        }
1.8       nicm      260:        while (*cp != '\0') {
1.5       shadchin  261:                char *np = cp;
                    262:                int width;
                    263:                char *pr = cmd_step_right(&np, &width, NULL);
                    264:                if (width > 0)
                    265:                        break;
                    266:                cp = np;
                    267:                putstr(pr);
1.4       millert   268:        }
                    269:
                    270:        /*
                    271:         * Back up the cursor to the correct position.
                    272:         */
                    273:        while (cp > old_cp)
                    274:                cmd_left();
                    275: }
                    276:
                    277: /*
                    278:  * Put the cursor at "home" (just after the prompt),
                    279:  * and set cp to the corresponding char in cmdbuf.
                    280:  */
1.8       nicm      281: static void
                    282: cmd_home(void)
1.4       millert   283: {
1.8       nicm      284:        while (cmd_col > prompt_col) {
1.5       shadchin  285:                int width, bswidth;
                    286:
                    287:                cmd_step_left(&cp, &width, &bswidth);
                    288:                while (bswidth-- > 0)
                    289:                        putbs();
                    290:                cmd_col -= width;
1.4       millert   291:        }
                    292:
                    293:        cp = &cmdbuf[cmd_offset];
                    294: }
                    295:
                    296: /*
                    297:  * Shift the cmdbuf display left a half-screen.
                    298:  */
1.8       nicm      299: static void
                    300: cmd_lshift(void)
1.4       millert   301: {
                    302:        char *s;
                    303:        char *save_cp;
                    304:        int cols;
                    305:
                    306:        /*
                    307:         * Start at the first displayed char, count how far to the
                    308:         * right we'd have to move to reach the center of the screen.
                    309:         */
                    310:        s = cmdbuf + cmd_offset;
                    311:        cols = 0;
1.8       nicm      312:        while (cols < (sc_width - prompt_col) / 2 && *s != '\0') {
1.5       shadchin  313:                int width;
                    314:                cmd_step_right(&s, &width, NULL);
                    315:                cols += width;
                    316:        }
1.8       nicm      317:        while (*s != '\0') {
1.5       shadchin  318:                int width;
                    319:                char *ns = s;
                    320:                cmd_step_right(&ns, &width, NULL);
                    321:                if (width > 0)
                    322:                        break;
                    323:                s = ns;
                    324:        }
1.4       millert   325:
                    326:        cmd_offset = s - cmdbuf;
                    327:        save_cp = cp;
                    328:        cmd_home();
                    329:        cmd_repaint(save_cp);
                    330: }
                    331:
                    332: /*
                    333:  * Shift the cmdbuf display right a half-screen.
                    334:  */
1.8       nicm      335: static void
                    336: cmd_rshift(void)
1.4       millert   337: {
                    338:        char *s;
                    339:        char *save_cp;
                    340:        int cols;
                    341:
                    342:        /*
                    343:         * Start at the first displayed char, count how far to the
                    344:         * left we'd have to move to traverse a half-screen width
                    345:         * of displayed characters.
                    346:         */
                    347:        s = cmdbuf + cmd_offset;
                    348:        cols = 0;
1.8       nicm      349:        while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) {
1.5       shadchin  350:                int width;
                    351:                cmd_step_left(&s, &width, NULL);
                    352:                cols += width;
1.4       millert   353:        }
                    354:
                    355:        cmd_offset = s - cmdbuf;
                    356:        save_cp = cp;
                    357:        cmd_home();
                    358:        cmd_repaint(save_cp);
                    359: }
                    360:
                    361: /*
                    362:  * Move cursor right one character.
                    363:  */
1.8       nicm      364: static int
                    365: cmd_right(void)
1.4       millert   366: {
1.5       shadchin  367:        char *pr;
                    368:        char *ncp;
                    369:        int width;
1.8       nicm      370:
                    371:        if (*cp == '\0') {
1.5       shadchin  372:                /* Already at the end of the line. */
1.4       millert   373:                return (CC_OK);
                    374:        }
1.5       shadchin  375:        ncp = cp;
                    376:        pr = cmd_step_right(&ncp, &width, NULL);
                    377:        if (cmd_col + width >= sc_width)
1.4       millert   378:                cmd_lshift();
1.5       shadchin  379:        else if (cmd_col + width == sc_width - 1 && cp[1] != '\0')
1.4       millert   380:                cmd_lshift();
1.5       shadchin  381:        cp = ncp;
                    382:        cmd_col += width;
                    383:        putstr(pr);
1.8       nicm      384:        while (*cp != '\0') {
1.5       shadchin  385:                pr = cmd_step_right(&ncp, &width, NULL);
                    386:                if (width > 0)
                    387:                        break;
                    388:                putstr(pr);
                    389:                cp = ncp;
                    390:        }
1.4       millert   391:        return (CC_OK);
                    392: }
                    393:
                    394: /*
                    395:  * Move cursor left one character.
                    396:  */
1.8       nicm      397: static int
                    398: cmd_left(void)
1.4       millert   399: {
1.5       shadchin  400:        char *ncp;
                    401:        int width, bswidth;
1.8       nicm      402:
                    403:        if (cp <= cmdbuf) {
1.4       millert   404:                /* Already at the beginning of the line */
                    405:                return (CC_OK);
                    406:        }
1.5       shadchin  407:        ncp = cp;
1.8       nicm      408:        while (ncp > cmdbuf) {
1.5       shadchin  409:                cmd_step_left(&ncp, &width, &bswidth);
                    410:                if (width > 0)
                    411:                        break;
                    412:        }
                    413:        if (cmd_col < prompt_col + width)
1.4       millert   414:                cmd_rshift();
1.5       shadchin  415:        cp = ncp;
                    416:        cmd_col -= width;
                    417:        while (bswidth-- > 0)
1.4       millert   418:                putbs();
                    419:        return (CC_OK);
                    420: }
                    421:
                    422: /*
                    423:  * Insert a char into the command buffer, at the current position.
                    424:  */
1.8       nicm      425: static int
                    426: cmd_ichar(char *cs, int clen)
1.4       millert   427: {
                    428:        char *s;
1.8       nicm      429:
                    430:        if (strlen(cmdbuf) + clen >= sizeof (cmdbuf)-1) {
1.5       shadchin  431:                /* No room in the command buffer for another char. */
1.8       nicm      432:                ring_bell();
1.4       millert   433:                return (CC_ERROR);
                    434:        }
1.8       nicm      435:
1.4       millert   436:        /*
1.5       shadchin  437:         * Make room for the new character (shift the tail of the buffer right).
                    438:         */
                    439:        for (s = &cmdbuf[strlen(cmdbuf)];  s >= cp;  s--)
                    440:                s[clen] = s[0];
                    441:        /*
1.4       millert   442:         * Insert the character into the buffer.
                    443:         */
1.5       shadchin  444:        for (s = cp;  s < cp + clen;  s++)
                    445:                *s = *cs++;
1.4       millert   446:        /*
                    447:         * Reprint the tail of the line from the inserted char.
                    448:         */
1.7       shadchin  449:        updown_match = -1;
1.4       millert   450:        cmd_repaint(cp);
                    451:        cmd_right();
                    452:        return (CC_OK);
                    453: }
                    454:
                    455: /*
1.1       etheisen  456:  * Backspace in the command buffer.
                    457:  * Delete the char to the left of the cursor.
                    458:  */
1.8       nicm      459: static int
                    460: cmd_erase(void)
1.1       etheisen  461: {
1.8       nicm      462:        char *s;
1.5       shadchin  463:        int clen;
1.1       etheisen  464:
1.8       nicm      465:        if (cp == cmdbuf) {
1.1       etheisen  466:                /*
                    467:                 * Backspace past beginning of the buffer:
                    468:                 * this usually means abort the command.
                    469:                 */
                    470:                return (CC_QUIT);
                    471:        }
                    472:        /*
1.4       millert   473:         * Move cursor left (to the char being erased).
1.1       etheisen  474:         */
1.5       shadchin  475:        s = cp;
1.4       millert   476:        cmd_left();
1.5       shadchin  477:        clen = s - cp;
                    478:
1.1       etheisen  479:        /*
1.4       millert   480:         * Remove the char from the buffer (shift the buffer left).
1.1       etheisen  481:         */
1.8       nicm      482:        for (s = cp; ; s++) {
1.5       shadchin  483:                s[0] = s[clen];
                    484:                if (s[0] == '\0')
                    485:                        break;
                    486:        }
                    487:
1.1       etheisen  488:        /*
1.4       millert   489:         * Repaint the buffer after the erased char.
1.1       etheisen  490:         */
1.7       shadchin  491:        updown_match = -1;
1.4       millert   492:        cmd_repaint(cp);
1.8       nicm      493:
1.1       etheisen  494:        /*
                    495:         * We say that erasing the entire command string causes us
1.4       millert   496:         * to abort the current command, if CF_QUIT_ON_ERASE is set.
1.1       etheisen  497:         */
1.4       millert   498:        if ((curr_cmdflags & CF_QUIT_ON_ERASE) && cp == cmdbuf && *cp == '\0')
1.1       etheisen  499:                return (CC_QUIT);
                    500:        return (CC_OK);
                    501: }
                    502:
                    503: /*
                    504:  * Delete the char under the cursor.
                    505:  */
1.8       nicm      506: static int
                    507: cmd_delete(void)
1.1       etheisen  508: {
1.8       nicm      509:        if (*cp == '\0') {
1.5       shadchin  510:                /* At end of string; there is no char under the cursor. */
1.1       etheisen  511:                return (CC_OK);
                    512:        }
                    513:        /*
                    514:         * Move right, then use cmd_erase.
                    515:         */
1.4       millert   516:        cmd_right();
1.1       etheisen  517:        cmd_erase();
                    518:        return (CC_OK);
                    519: }
                    520:
                    521: /*
                    522:  * Delete the "word" to the left of the cursor.
                    523:  */
1.8       nicm      524: static int
                    525: cmd_werase(void)
1.1       etheisen  526: {
1.8       nicm      527:        if (cp > cmdbuf && cp[-1] == ' ') {
1.1       etheisen  528:                /*
                    529:                 * If the char left of cursor is a space,
                    530:                 * erase all the spaces left of cursor (to the first non-space).
                    531:                 */
                    532:                while (cp > cmdbuf && cp[-1] == ' ')
                    533:                        (void) cmd_erase();
1.8       nicm      534:        } else {
1.1       etheisen  535:                /*
                    536:                 * If the char left of cursor is not a space,
                    537:                 * erase all the nonspaces left of cursor (the whole "word").
                    538:                 */
                    539:                while (cp > cmdbuf && cp[-1] != ' ')
                    540:                        (void) cmd_erase();
                    541:        }
                    542:        return (CC_OK);
                    543: }
                    544:
                    545: /*
                    546:  * Delete the "word" under the cursor.
                    547:  */
1.8       nicm      548: static int
                    549: cmd_wdelete(void)
1.1       etheisen  550: {
1.8       nicm      551:        if (*cp == ' ') {
1.1       etheisen  552:                /*
                    553:                 * If the char under the cursor is a space,
                    554:                 * delete it and all the spaces right of cursor.
                    555:                 */
                    556:                while (*cp == ' ')
                    557:                        (void) cmd_delete();
1.8       nicm      558:        } else {
1.1       etheisen  559:                /*
                    560:                 * If the char under the cursor is not a space,
                    561:                 * delete it and all nonspaces right of cursor (the whole word).
                    562:                 */
                    563:                while (*cp != ' ' && *cp != '\0')
                    564:                        (void) cmd_delete();
                    565:        }
                    566:        return (CC_OK);
                    567: }
                    568:
                    569: /*
                    570:  * Delete all chars in the command buffer.
                    571:  */
1.8       nicm      572: static int
                    573: cmd_kill(void)
1.1       etheisen  574: {
1.8       nicm      575:        if (cmdbuf[0] == '\0') {
1.5       shadchin  576:                /* Buffer is already empty; abort the current command. */
1.1       etheisen  577:                return (CC_QUIT);
                    578:        }
1.4       millert   579:        cmd_offset = 0;
                    580:        cmd_home();
1.1       etheisen  581:        *cp = '\0';
1.7       shadchin  582:        updown_match = -1;
1.4       millert   583:        cmd_repaint(cp);
                    584:
1.1       etheisen  585:        /*
1.4       millert   586:         * We say that erasing the entire command string causes us
                    587:         * to abort the current command, if CF_QUIT_ON_ERASE is set.
1.1       etheisen  588:         */
1.4       millert   589:        if (curr_cmdflags & CF_QUIT_ON_ERASE)
1.1       etheisen  590:                return (CC_QUIT);
                    591:        return (CC_OK);
                    592: }
                    593:
                    594: /*
                    595:  * Select an mlist structure to be the current command history.
                    596:  */
1.8       nicm      597: void
                    598: set_mlist(void *mlist, int cmdflags)
1.1       etheisen  599: {
1.8       nicm      600:        curr_mlist = (struct mlist *)mlist;
1.4       millert   601:        curr_cmdflags = cmdflags;
1.5       shadchin  602:
                    603:        /* Make sure the next up-arrow moves to the last string in the mlist. */
                    604:        if (curr_mlist != NULL)
                    605:                curr_mlist->curr_mp = curr_mlist;
1.1       etheisen  606: }
                    607:
                    608: /*
                    609:  * Move up or down in the currently selected command history list.
1.7       shadchin  610:  * Only consider entries whose first updown_match chars are equal to
                    611:  * cmdbuf's corresponding chars.
1.1       etheisen  612:  */
1.8       nicm      613: static int
                    614: cmd_updown(int action)
1.1       etheisen  615: {
                    616:        char *s;
1.7       shadchin  617:        struct mlist *ml;
1.8       nicm      618:
                    619:        if (curr_mlist == NULL) {
1.1       etheisen  620:                /*
                    621:                 * The current command has no history list.
                    622:                 */
1.8       nicm      623:                ring_bell();
1.1       etheisen  624:                return (CC_OK);
                    625:        }
1.7       shadchin  626:
1.8       nicm      627:        if (updown_match < 0) {
1.7       shadchin  628:                updown_match = cp - cmdbuf;
                    629:        }
                    630:
1.1       etheisen  631:        /*
1.7       shadchin  632:         * Find the next history entry which matches.
1.1       etheisen  633:         */
1.8       nicm      634:        for (ml = curr_mlist->curr_mp; ; ) {
1.7       shadchin  635:                ml = (action == EC_UP) ? ml->prev : ml->next;
1.8       nicm      636:                if (ml == curr_mlist) {
1.7       shadchin  637:                        /*
                    638:                         * We reached the end (or beginning) of the list.
                    639:                         */
                    640:                        break;
                    641:                }
1.8       nicm      642:                if (strncmp(cmdbuf, ml->string, updown_match) == 0) {
1.7       shadchin  643:                        /*
                    644:                         * This entry matches; stop here.
                    645:                         * Copy the entry into cmdbuf and echo it on the screen.
                    646:                         */
                    647:                        curr_mlist->curr_mp = ml;
                    648:                        s = ml->string;
                    649:                        if (s == NULL)
                    650:                                s = "";
                    651:                        cmd_home();
                    652:                        clear_eol();
1.8       nicm      653:                        strlcpy(cmdbuf, s, sizeof (cmdbuf));
                    654:                        for (cp = cmdbuf; *cp != '\0'; )
1.7       shadchin  655:                                cmd_right();
                    656:                        return (CC_OK);
                    657:                }
                    658:        }
1.1       etheisen  659:        /*
1.7       shadchin  660:         * We didn't find a history entry that matches.
1.1       etheisen  661:         */
1.8       nicm      662:        ring_bell();
1.1       etheisen  663:        return (CC_OK);
                    664: }
                    665:
                    666: /*
1.4       millert   667:  * Add a string to a history list.
1.1       etheisen  668:  */
1.8       nicm      669: void
                    670: cmd_addhist(struct mlist *mlist, const char *cmd)
1.1       etheisen  671: {
                    672:        struct mlist *ml;
1.8       nicm      673:
1.1       etheisen  674:        /*
                    675:         * Don't save a trivial command.
                    676:         */
1.4       millert   677:        if (strlen(cmd) == 0)
1.1       etheisen  678:                return;
1.5       shadchin  679:
1.1       etheisen  680:        /*
1.5       shadchin  681:         * Save the command unless it's a duplicate of the
                    682:         * last command in the history.
1.1       etheisen  683:         */
1.5       shadchin  684:        ml = mlist->prev;
1.8       nicm      685:        if (ml == mlist || strcmp(ml->string, cmd) != 0) {
1.1       etheisen  686:                /*
                    687:                 * Did not find command in history.
                    688:                 * Save the command and put it at the end of the history list.
                    689:                 */
1.8       nicm      690:                ml = ecalloc(1, sizeof (struct mlist));
1.10    ! tedu      691:                ml->string = estrdup(cmd);
1.4       millert   692:                ml->next = mlist;
                    693:                ml->prev = mlist->prev;
                    694:                mlist->prev->next = ml;
                    695:                mlist->prev = ml;
1.1       etheisen  696:        }
                    697:        /*
                    698:         * Point to the cmd just after the just-accepted command.
                    699:         * Thus, an UPARROW will always retrieve the previous command.
                    700:         */
1.4       millert   701:        mlist->curr_mp = ml->next;
1.1       etheisen  702: }
1.4       millert   703:
                    704: /*
                    705:  * Accept the command in the command buffer.
                    706:  * Add it to the currently selected history list.
                    707:  */
1.8       nicm      708: void
                    709: cmd_accept(void)
1.4       millert   710: {
                    711:        /*
                    712:         * Nothing to do if there is no currently selected history list.
                    713:         */
                    714:        if (curr_mlist == NULL)
                    715:                return;
                    716:        cmd_addhist(curr_mlist, cmdbuf);
1.5       shadchin  717:        curr_mlist->modified = 1;
1.4       millert   718: }
1.1       etheisen  719:
                    720: /*
                    721:  * Try to perform a line-edit function on the command buffer,
                    722:  * using a specified char as a line-editing command.
                    723:  * Returns:
                    724:  *     CC_PASS The char does not invoke a line edit function.
                    725:  *     CC_OK   Line edit function done.
                    726:  *     CC_QUIT The char requests the current command to be aborted.
                    727:  */
1.8       nicm      728: static int
                    729: cmd_edit(int c)
1.1       etheisen  730: {
                    731:        int action;
                    732:        int flags;
                    733:
                    734: #define        not_in_completion()     in_completion = 0
1.8       nicm      735:
1.1       etheisen  736:        /*
                    737:         * See if the char is indeed a line-editing command.
                    738:         */
                    739:        flags = 0;
                    740:        if (curr_mlist == NULL)
                    741:                /*
                    742:                 * No current history; don't accept history manipulation cmds.
                    743:                 */
                    744:                flags |= EC_NOHISTORY;
1.4       millert   745:        if (curr_mlist == ml_search)
1.1       etheisen  746:                /*
                    747:                 * In a search command; don't accept file-completion cmds.
                    748:                 */
                    749:                flags |= EC_NOCOMPLETE;
                    750:
                    751:        action = editchar(c, flags);
                    752:
1.8       nicm      753:        switch (action) {
1.1       etheisen  754:        case EC_RIGHT:
                    755:                not_in_completion();
                    756:                return (cmd_right());
                    757:        case EC_LEFT:
                    758:                not_in_completion();
                    759:                return (cmd_left());
                    760:        case EC_W_RIGHT:
                    761:                not_in_completion();
                    762:                while (*cp != '\0' && *cp != ' ')
                    763:                        cmd_right();
                    764:                while (*cp == ' ')
                    765:                        cmd_right();
                    766:                return (CC_OK);
                    767:        case EC_W_LEFT:
                    768:                not_in_completion();
                    769:                while (cp > cmdbuf && cp[-1] == ' ')
                    770:                        cmd_left();
                    771:                while (cp > cmdbuf && cp[-1] != ' ')
                    772:                        cmd_left();
                    773:                return (CC_OK);
                    774:        case EC_HOME:
                    775:                not_in_completion();
1.4       millert   776:                cmd_offset = 0;
                    777:                cmd_home();
                    778:                cmd_repaint(cp);
                    779:                return (CC_OK);
1.1       etheisen  780:        case EC_END:
                    781:                not_in_completion();
                    782:                while (*cp != '\0')
                    783:                        cmd_right();
                    784:                return (CC_OK);
                    785:        case EC_INSERT:
                    786:                not_in_completion();
                    787:                return (CC_OK);
                    788:        case EC_BACKSPACE:
                    789:                not_in_completion();
                    790:                return (cmd_erase());
                    791:        case EC_LINEKILL:
                    792:                not_in_completion();
                    793:                return (cmd_kill());
1.5       shadchin  794:        case EC_ABORT:
                    795:                not_in_completion();
                    796:                (void) cmd_kill();
                    797:                return (CC_QUIT);
1.1       etheisen  798:        case EC_W_BACKSPACE:
                    799:                not_in_completion();
                    800:                return (cmd_werase());
                    801:        case EC_DELETE:
                    802:                not_in_completion();
                    803:                return (cmd_delete());
                    804:        case EC_W_DELETE:
                    805:                not_in_completion();
                    806:                return (cmd_wdelete());
                    807:        case EC_LITERAL:
                    808:                literal = 1;
                    809:                return (CC_OK);
                    810:        case EC_UP:
                    811:        case EC_DOWN:
                    812:                not_in_completion();
                    813:                return (cmd_updown(action));
                    814:        case EC_F_COMPLETE:
                    815:        case EC_B_COMPLETE:
                    816:        case EC_EXPAND:
                    817:                return (cmd_complete(action));
1.4       millert   818:        case EC_NOACTION:
                    819:                return (CC_OK);
1.1       etheisen  820:        default:
                    821:                not_in_completion();
                    822:                return (CC_PASS);
                    823:        }
                    824: }
                    825:
                    826: /*
                    827:  * Insert a string into the command buffer, at the current position.
                    828:  */
1.8       nicm      829: static int
                    830: cmd_istr(char *str)
1.1       etheisen  831: {
                    832:        char *s;
                    833:        int action;
1.5       shadchin  834:        char *endline = str + strlen(str);
1.8       nicm      835:
                    836:        for (s = str; *s != '\0'; ) {
1.5       shadchin  837:                char *os = s;
                    838:                step_char(&s, +1, endline);
                    839:                action = cmd_ichar(os, s - os);
1.8       nicm      840:                if (action != CC_OK) {
                    841:                        ring_bell();
1.1       etheisen  842:                        return (action);
                    843:                }
                    844:        }
                    845:        return (CC_OK);
                    846: }
                    847:
                    848: /*
                    849:  * Find the beginning and end of the "current" word.
                    850:  * This is the word which the cursor (cp) is inside or at the end of.
                    851:  * Return pointer to the beginning of the word and put the
                    852:  * cursor at the end of the word.
                    853:  */
1.8       nicm      854: static char *
                    855: delimit_word(void)
1.1       etheisen  856: {
                    857:        char *word;
1.4       millert   858:        char *p;
                    859:        int delim_quoted = 0;
                    860:        int meta_quoted = 0;
                    861:        char *esc = get_meta_escape();
                    862:        int esclen = strlen(esc);
1.8       nicm      863:
1.1       etheisen  864:        /*
                    865:         * Move cursor to end of word.
                    866:         */
1.8       nicm      867:        if (*cp != ' ' && *cp != '\0') {
1.1       etheisen  868:                /*
                    869:                 * Cursor is on a nonspace.
                    870:                 * Move cursor right to the next space.
                    871:                 */
                    872:                while (*cp != ' ' && *cp != '\0')
                    873:                        cmd_right();
                    874:        }
1.8       nicm      875:
1.1       etheisen  876:        /*
1.4       millert   877:         * Find the beginning of the word which the cursor is in.
1.1       etheisen  878:         */
                    879:        if (cp == cmdbuf)
                    880:                return (NULL);
1.4       millert   881:        /*
                    882:         * If we have an unbalanced quote (that is, an open quote
                    883:         * without a corresponding close quote), we return everything
                    884:         * from the open quote, including spaces.
                    885:         */
                    886:        for (word = cmdbuf;  word < cp;  word++)
                    887:                if (*word != ' ')
1.1       etheisen  888:                        break;
1.4       millert   889:        if (word >= cp)
                    890:                return (cp);
1.8       nicm      891:        for (p = cmdbuf;  p < cp;  p++) {
                    892:                if (meta_quoted) {
1.4       millert   893:                        meta_quoted = 0;
                    894:                } else if (esclen > 0 && p + esclen < cp &&
1.8       nicm      895:                    strncmp(p, esc, esclen) == 0) {
1.4       millert   896:                        meta_quoted = 1;
                    897:                        p += esclen - 1;
1.8       nicm      898:                } else if (delim_quoted) {
1.4       millert   899:                        if (*p == closequote)
                    900:                                delim_quoted = 0;
1.8       nicm      901:                } else { /* (!delim_quoted) */
1.4       millert   902:                        if (*p == openquote)
                    903:                                delim_quoted = 1;
                    904:                        else if (*p == ' ')
                    905:                                word = p+1;
                    906:                }
                    907:        }
1.1       etheisen  908:        return (word);
                    909: }
                    910:
                    911: /*
                    912:  * Set things up to enter completion mode.
1.8       nicm      913:  * Expand the word under the cursor into a list of filenames
1.1       etheisen  914:  * which start with that word, and set tk_text to that list.
                    915:  */
1.8       nicm      916: static void
                    917: init_compl(void)
1.1       etheisen  918: {
                    919:        char *word;
                    920:        char c;
1.8       nicm      921:
1.1       etheisen  922:        /*
                    923:         * Get rid of any previous tk_text.
                    924:         */
1.8       nicm      925:        if (tk_text != NULL) {
1.1       etheisen  926:                free(tk_text);
                    927:                tk_text = NULL;
                    928:        }
                    929:        /*
                    930:         * Find the original (uncompleted) word in the command buffer.
                    931:         */
                    932:        word = delimit_word();
                    933:        if (word == NULL)
                    934:                return;
                    935:        /*
                    936:         * Set the insertion point to the point in the command buffer
                    937:         * where the original (uncompleted) word now sits.
                    938:         */
                    939:        tk_ipoint = word;
                    940:        /*
                    941:         * Save the original (uncompleted) word
                    942:         */
                    943:        if (tk_original != NULL)
                    944:                free(tk_original);
1.8       nicm      945:        tk_original = ecalloc(cp-word+1, sizeof (char));
                    946:        (void) strncpy(tk_original, word, cp-word);
1.1       etheisen  947:        /*
                    948:         * Get the expanded filename.
                    949:         * This may result in a single filename, or
                    950:         * a blank-separated list of filenames.
                    951:         */
                    952:        c = *cp;
                    953:        *cp = '\0';
1.8       nicm      954:        if (*word != openquote) {
1.4       millert   955:                tk_text = fcomplete(word);
1.8       nicm      956:        } else {
1.4       millert   957:                char *qword = shell_quote(word+1);
1.8       nicm      958:                if (qword == NULL) {
1.4       millert   959:                        tk_text = fcomplete(word+1);
1.8       nicm      960:                } else {
1.4       millert   961:                        tk_text = fcomplete(qword);
                    962:                        free(qword);
                    963:                }
                    964:        }
1.1       etheisen  965:        *cp = c;
                    966: }
                    967:
                    968: /*
                    969:  * Return the next word in the current completion list.
                    970:  */
1.8       nicm      971: static char *
                    972: next_compl(int action, char *prev)
1.1       etheisen  973: {
1.8       nicm      974:        switch (action) {
1.1       etheisen  975:        case EC_F_COMPLETE:
                    976:                return (forw_textlist(&tk_tlist, prev));
                    977:        case EC_B_COMPLETE:
                    978:                return (back_textlist(&tk_tlist, prev));
                    979:        }
1.4       millert   980:        /* Cannot happen */
                    981:        return ("?");
1.1       etheisen  982: }
                    983:
                    984: /*
                    985:  * Complete the filename before (or under) the cursor.
                    986:  * cmd_complete may be called multiple times.  The global in_completion
                    987:  * remembers whether this call is the first time (create the list),
                    988:  * or a subsequent time (step thru the list).
                    989:  */
1.8       nicm      990: static int
                    991: cmd_complete(int action)
1.1       etheisen  992: {
1.4       millert   993:        char *s;
1.1       etheisen  994:
1.8       nicm      995:        if (!in_completion || action == EC_EXPAND) {
1.1       etheisen  996:                /*
1.8       nicm      997:                 * Expand the word under the cursor and
                    998:                 * use the first word in the expansion
1.1       etheisen  999:                 * (or the entire expansion if we're doing EC_EXPAND).
                   1000:                 */
                   1001:                init_compl();
1.8       nicm     1002:                if (tk_text == NULL) {
                   1003:                        ring_bell();
1.1       etheisen 1004:                        return (CC_OK);
                   1005:                }
1.8       nicm     1006:                if (action == EC_EXPAND) {
1.1       etheisen 1007:                        /*
                   1008:                         * Use the whole list.
                   1009:                         */
                   1010:                        tk_trial = tk_text;
1.8       nicm     1011:                } else {
1.1       etheisen 1012:                        /*
                   1013:                         * Use the first filename in the list.
                   1014:                         */
                   1015:                        in_completion = 1;
                   1016:                        init_textlist(&tk_tlist, tk_text);
1.8       nicm     1017:                        tk_trial = next_compl(action, NULL);
1.1       etheisen 1018:                }
1.8       nicm     1019:        } else {
1.1       etheisen 1020:                /*
                   1021:                 * We already have a completion list.
                   1022:                 * Use the next/previous filename from the list.
                   1023:                 */
                   1024:                tk_trial = next_compl(action, tk_trial);
                   1025:        }
1.8       nicm     1026:
                   1027:        /*
                   1028:         * Remove the original word, or the previous trial completion.
                   1029:         */
1.1       etheisen 1030:        while (cp > tk_ipoint)
                   1031:                (void) cmd_erase();
1.8       nicm     1032:
                   1033:        if (tk_trial == NULL) {
1.1       etheisen 1034:                /*
                   1035:                 * There are no more trial completions.
                   1036:                 * Insert the original (uncompleted) filename.
                   1037:                 */
                   1038:                in_completion = 0;
                   1039:                if (cmd_istr(tk_original) != CC_OK)
                   1040:                        goto fail;
1.8       nicm     1041:        } else {
1.1       etheisen 1042:                /*
                   1043:                 * Insert trial completion.
                   1044:                 */
                   1045:                if (cmd_istr(tk_trial) != CC_OK)
                   1046:                        goto fail;
1.4       millert  1047:                /*
                   1048:                 * If it is a directory, append a slash.
                   1049:                 */
1.8       nicm     1050:                if (is_dir(tk_trial)) {
1.4       millert  1051:                        if (cp > cmdbuf && cp[-1] == closequote)
                   1052:                                (void) cmd_erase();
                   1053:                        s = lgetenv("LESSSEPARATOR");
                   1054:                        if (s == NULL)
1.8       nicm     1055:                                s = "/";
1.4       millert  1056:                        if (cmd_istr(s) != CC_OK)
                   1057:                                goto fail;
                   1058:                }
1.1       etheisen 1059:        }
1.8       nicm     1060:
1.1       etheisen 1061:        return (CC_OK);
1.8       nicm     1062:
1.1       etheisen 1063: fail:
                   1064:        in_completion = 0;
1.8       nicm     1065:        ring_bell();
1.1       etheisen 1066:        return (CC_OK);
                   1067: }
                   1068:
                   1069: /*
                   1070:  * Process a single character of a multi-character command, such as
                   1071:  * a number, or the pattern of a search command.
                   1072:  * Returns:
                   1073:  *     CC_OK           The char was accepted.
                   1074:  *     CC_QUIT         The char requests the command to be aborted.
                   1075:  *     CC_ERROR        The char could not be accepted due to an error.
                   1076:  */
1.8       nicm     1077: int
                   1078: cmd_char(int c)
1.1       etheisen 1079: {
                   1080:        int action;
1.5       shadchin 1081:        int len;
                   1082:
1.8       nicm     1083:        if (!utf_mode) {
                   1084:                cmd_mbc_buf[0] = c & 0xff;
1.5       shadchin 1085:                len = 1;
1.8       nicm     1086:        } else {
1.5       shadchin 1087:                /* Perform strict validation in all possible cases.  */
1.8       nicm     1088:                if (cmd_mbc_buf_len == 0) {
                   1089: retry:
1.5       shadchin 1090:                        cmd_mbc_buf_index = 1;
1.8       nicm     1091:                        *cmd_mbc_buf = c & 0xff;
1.5       shadchin 1092:                        if (IS_ASCII_OCTET(c))
                   1093:                                cmd_mbc_buf_len = 1;
1.8       nicm     1094:                        else if (IS_UTF8_LEAD(c)) {
1.5       shadchin 1095:                                cmd_mbc_buf_len = utf_len(c);
                   1096:                                return (CC_OK);
1.8       nicm     1097:                        } else {
1.5       shadchin 1098:                                /* UTF8_INVALID or stray UTF8_TRAIL */
1.8       nicm     1099:                                ring_bell();
1.5       shadchin 1100:                                return (CC_ERROR);
                   1101:                        }
1.8       nicm     1102:                } else if (IS_UTF8_TRAIL(c)) {
                   1103:                        cmd_mbc_buf[cmd_mbc_buf_index++] = c & 0xff;
1.5       shadchin 1104:                        if (cmd_mbc_buf_index < cmd_mbc_buf_len)
                   1105:                                return (CC_OK);
1.8       nicm     1106:                        if (!is_utf8_well_formed(cmd_mbc_buf)) {
                   1107:                                /*
                   1108:                                 * complete, but not well formed
                   1109:                                 * (non-shortest form), sequence
                   1110:                                 */
1.5       shadchin 1111:                                cmd_mbc_buf_len = 0;
1.8       nicm     1112:                                ring_bell();
1.5       shadchin 1113:                                return (CC_ERROR);
                   1114:                        }
1.8       nicm     1115:                } else {
1.5       shadchin 1116:                        /* Flush incomplete (truncated) sequence.  */
                   1117:                        cmd_mbc_buf_len = 0;
1.8       nicm     1118:                        ring_bell();
1.5       shadchin 1119:                        /* Handle new char.  */
                   1120:                        goto retry;
                   1121:                }
                   1122:
                   1123:                len = cmd_mbc_buf_len;
                   1124:                cmd_mbc_buf_len = 0;
                   1125:        }
1.1       etheisen 1126:
1.8       nicm     1127:        if (literal) {
1.1       etheisen 1128:                /*
                   1129:                 * Insert the char, even if it is a line-editing char.
                   1130:                 */
                   1131:                literal = 0;
1.5       shadchin 1132:                return (cmd_ichar(cmd_mbc_buf, len));
1.1       etheisen 1133:        }
1.8       nicm     1134:
1.1       etheisen 1135:        /*
1.5       shadchin 1136:         * See if it is a line-editing character.
1.1       etheisen 1137:         */
1.8       nicm     1138:        if (in_mca() && len == 1) {
1.1       etheisen 1139:                action = cmd_edit(c);
1.8       nicm     1140:                switch (action) {
1.1       etheisen 1141:                case CC_OK:
                   1142:                case CC_QUIT:
                   1143:                        return (action);
                   1144:                case CC_PASS:
                   1145:                        break;
                   1146:                }
                   1147:        }
1.8       nicm     1148:
1.1       etheisen 1149:        /*
                   1150:         * Insert the char into the command buffer.
                   1151:         */
1.5       shadchin 1152:        return (cmd_ichar(cmd_mbc_buf, len));
1.1       etheisen 1153: }
                   1154:
                   1155: /*
                   1156:  * Return the number currently in the command buffer.
                   1157:  */
1.8       nicm     1158: LINENUM
                   1159: cmd_int(long *frac)
1.1       etheisen 1160: {
1.5       shadchin 1161:        char *p;
1.4       millert  1162:        LINENUM n = 0;
1.5       shadchin 1163:        int err;
1.1       etheisen 1164:
1.5       shadchin 1165:        for (p = cmdbuf;  *p >= '0' && *p <= '9';  p++)
                   1166:                n = (n * 10) + (*p - '0');
                   1167:        *frac = 0;
1.8       nicm     1168:        if (*p++ == '.') {
1.5       shadchin 1169:                *frac = getfraction(&p, NULL, &err);
                   1170:                /* {{ do something if err is set? }} */
                   1171:        }
1.4       millert  1172:        return (n);
1.1       etheisen 1173: }
                   1174:
                   1175: /*
                   1176:  * Return a pointer to the command buffer.
                   1177:  */
1.8       nicm     1178: char *
                   1179: get_cmdbuf(void)
1.1       etheisen 1180: {
                   1181:        return (cmdbuf);
1.5       shadchin 1182: }
                   1183:
                   1184: /*
                   1185:  * Return the last (most recent) string in the current command history.
                   1186:  */
1.8       nicm     1187: char *
                   1188: cmd_lastpattern(void)
1.5       shadchin 1189: {
                   1190:        if (curr_mlist == NULL)
                   1191:                return (NULL);
                   1192:        return (curr_mlist->curr_mp->prev->string);
                   1193: }
                   1194:
                   1195: /*
                   1196:  * Get the name of the history file.
                   1197:  */
1.8       nicm     1198: static char *
                   1199: histfile_name(void)
1.5       shadchin 1200: {
                   1201:        char *home;
                   1202:        char *name;
1.8       nicm     1203:
1.5       shadchin 1204:        /* See if filename is explicitly specified by $LESSHISTFILE. */
                   1205:        name = lgetenv("LESSHISTFILE");
1.8       nicm     1206:        if (name != NULL && *name != '\0') {
1.5       shadchin 1207:                if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0)
1.8       nicm     1208:                        /* $LESSHISTFILE == "-" means don't use history file */
1.5       shadchin 1209:                        return (NULL);
1.10    ! tedu     1210:                return (estrdup(name));
1.5       shadchin 1211:        }
                   1212:
1.6       nicm     1213:        /* Otherwise, file is in $HOME if enabled. */
1.8       nicm     1214:        if (strcmp(LESSHISTFILE, "-") == 0)
1.6       nicm     1215:                return (NULL);
1.5       shadchin 1216:        home = lgetenv("HOME");
1.8       nicm     1217:        if (home == NULL || *home == '\0') {
                   1218:                return (NULL);
1.5       shadchin 1219:        }
1.8       nicm     1220:        return (easprintf("%s/%s", home, LESSHISTFILE));
1.5       shadchin 1221: }
                   1222:
                   1223: /*
                   1224:  * Initialize history from a .lesshist file.
                   1225:  */
1.8       nicm     1226: void
                   1227: init_cmdhist(void)
1.5       shadchin 1228: {
                   1229:        struct mlist *ml = NULL;
                   1230:        char line[CMDBUF_SIZE];
                   1231:        char *filename;
                   1232:        FILE *f;
                   1233:        char *p;
                   1234:
                   1235:        filename = histfile_name();
                   1236:        if (filename == NULL)
                   1237:                return;
                   1238:        f = fopen(filename, "r");
                   1239:        free(filename);
                   1240:        if (f == NULL)
                   1241:                return;
1.8       nicm     1242:        if (fgets(line, sizeof (line), f) == NULL ||
                   1243:            strncmp(line, HISTFILE_FIRST_LINE,
                   1244:            strlen(HISTFILE_FIRST_LINE)) != 0) {
                   1245:                (void) fclose(f);
1.5       shadchin 1246:                return;
                   1247:        }
1.8       nicm     1248:        while (fgets(line, sizeof (line), f) != NULL) {
                   1249:                for (p = line;  *p != '\0';  p++) {
                   1250:                        if (*p == '\n' || *p == '\r') {
1.5       shadchin 1251:                                *p = '\0';
                   1252:                                break;
                   1253:                        }
                   1254:                }
                   1255:                if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0)
                   1256:                        ml = &mlist_search;
1.8       nicm     1257:                else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) {
1.5       shadchin 1258:                        ml = &mlist_shell;
1.8       nicm     1259:                } else if (*line == '"') {
1.5       shadchin 1260:                        if (ml != NULL)
                   1261:                                cmd_addhist(ml, line+1);
                   1262:                }
                   1263:        }
1.8       nicm     1264:        (void) fclose(f);
1.5       shadchin 1265: }
                   1266:
                   1267: /*
                   1268:  *
                   1269:  */
1.8       nicm     1270: static void
                   1271: save_mlist(struct mlist *ml, FILE *f)
1.5       shadchin 1272: {
                   1273:        int histsize = 0;
                   1274:        int n;
                   1275:        char *s;
                   1276:
                   1277:        s = lgetenv("LESSHISTSIZE");
                   1278:        if (s != NULL)
                   1279:                histsize = atoi(s);
                   1280:        if (histsize == 0)
                   1281:                histsize = 100;
                   1282:
                   1283:        ml = ml->prev;
1.8       nicm     1284:        for (n = 0;  n < histsize;  n++) {
1.5       shadchin 1285:                if (ml->string == NULL)
                   1286:                        break;
                   1287:                ml = ml->prev;
                   1288:        }
                   1289:        for (ml = ml->next;  ml->string != NULL;  ml = ml->next)
1.8       nicm     1290:                (void) fprintf(f, "\"%s\n", ml->string);
1.5       shadchin 1291: }
                   1292:
                   1293: /*
                   1294:  *
                   1295:  */
1.8       nicm     1296: void
                   1297: save_cmdhist(void)
1.5       shadchin 1298: {
                   1299:        char *filename;
                   1300:        FILE *f;
                   1301:        int modified = 0;
1.8       nicm     1302:        int do_chmod = 1;
                   1303:        struct stat statbuf;
                   1304:        int r;
1.5       shadchin 1305:
                   1306:        if (mlist_search.modified)
                   1307:                modified = 1;
                   1308:        if (mlist_shell.modified)
                   1309:                modified = 1;
                   1310:        if (!modified)
1.7       shadchin 1311:                return;
                   1312:        filename = histfile_name();
                   1313:        if (filename == NULL)
1.5       shadchin 1314:                return;
                   1315:        f = fopen(filename, "w");
                   1316:        free(filename);
                   1317:        if (f == NULL)
                   1318:                return;
1.8       nicm     1319:
1.5       shadchin 1320:        /* Make history file readable only by owner. */
1.8       nicm     1321:        r = fstat(fileno(f), &statbuf);
1.5       shadchin 1322:        if (r < 0 || !S_ISREG(statbuf.st_mode))
                   1323:                /* Don't chmod if not a regular file. */
                   1324:                do_chmod = 0;
                   1325:        if (do_chmod)
1.8       nicm     1326:                (void) fchmod(fileno(f), 0600);
1.5       shadchin 1327:
1.8       nicm     1328:        (void) fprintf(f, "%s\n", HISTFILE_FIRST_LINE);
1.5       shadchin 1329:
1.8       nicm     1330:        (void) fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION);
1.5       shadchin 1331:        save_mlist(&mlist_search, f);
                   1332:
1.8       nicm     1333:        (void) fprintf(f, "%s\n", HISTFILE_SHELL_SECTION);
1.5       shadchin 1334:        save_mlist(&mlist_shell, f);
                   1335:
1.8       nicm     1336:        (void) fclose(f);
1.1       etheisen 1337: }