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

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