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

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