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

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