[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.20

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