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

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