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

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