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

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