=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/less/cmdbuf.c,v retrieving revision 1.4 retrieving revision 1.5 diff -c -r1.4 -r1.5 *** src/usr.bin/less/cmdbuf.c 2003/04/13 18:26:25 1.4 --- src/usr.bin/less/cmdbuf.c 2011/09/16 18:12:09 1.5 *************** *** 1,5 **** /* ! * Copyright (C) 1984-2002 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. --- 1,5 ---- /* ! * Copyright (C) 1984-2011 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. *************** *** 16,23 **** --- 16,28 ---- #include "less.h" #include "cmd.h" + #include "charset.h" + #if HAVE_STAT + #include + #endif extern int sc_width; + extern int utf_mode; static char cmdbuf[CMDBUF_SIZE]; /* Buffer for holding a multi-char command */ static int cmd_col; /* Current column of the cursor */ *************** *** 48,53 **** --- 53,64 ---- #endif #if CMD_HISTORY + + /* History file */ + #define HISTFILE_FIRST_LINE ".less-history-file:" + #define HISTFILE_SEARCH_SECTION ".search" + #define HISTFILE_SHELL_SECTION ".shell" + /* * A mlist structure represents a command history. */ *************** *** 57,78 **** struct mlist *prev; struct mlist *curr_mp; char *string; }; /* * These are the various command histories that exist. */ struct mlist mlist_search = ! { &mlist_search, &mlist_search, &mlist_search, NULL }; public void * constant ml_search = (void *) &mlist_search; struct mlist mlist_examine = ! { &mlist_examine, &mlist_examine, &mlist_examine, NULL }; public void * constant ml_examine = (void *) &mlist_examine; #if SHELL_ESCAPE || PIPEC struct mlist mlist_shell = ! { &mlist_shell, &mlist_shell, &mlist_shell, NULL }; public void * constant ml_shell = (void *) &mlist_shell; #endif --- 68,90 ---- struct mlist *prev; struct mlist *curr_mp; char *string; + int modified; }; /* * These are the various command histories that exist. */ struct mlist mlist_search = ! { &mlist_search, &mlist_search, &mlist_search, NULL, 0 }; public void * constant ml_search = (void *) &mlist_search; struct mlist mlist_examine = ! { &mlist_examine, &mlist_examine, &mlist_examine, NULL, 0 }; public void * constant ml_examine = (void *) &mlist_examine; #if SHELL_ESCAPE || PIPEC struct mlist mlist_shell = ! { &mlist_shell, &mlist_shell, &mlist_shell, NULL, 0 }; public void * constant ml_shell = (void *) &mlist_shell; #endif *************** *** 93,99 **** --- 105,115 ---- static struct mlist *curr_mlist = NULL; static int curr_cmdflags; + static char cmd_mbc_buf[MAX_UTF_CHAR_LEN]; + static int cmd_mbc_buf_len; + static int cmd_mbc_buf_index; + /* * Reset command buffer (to empty). */ *************** *** 105,120 **** cmd_col = 0; cmd_offset = 0; literal = 0; } /* ! * Clear command line on display. */ public void clear_cmd() { - clear_bot(); cmd_col = prompt_col = 0; } /* --- 121,137 ---- cmd_col = 0; cmd_offset = 0; literal = 0; + cmd_mbc_buf_len = 0; } /* ! * Clear command line. */ public void clear_cmd() { cmd_col = prompt_col = 0; + cmd_mbc_buf_len = 0; } /* *************** *** 124,132 **** cmd_putstr(s) char *s; { ! putstr(s); ! cmd_col += strlen(s); ! prompt_col += strlen(s); } /* --- 141,171 ---- cmd_putstr(s) char *s; { ! LWCHAR prev_ch = 0; ! LWCHAR ch; ! char *endline = s + strlen(s); ! while (*s != '\0') ! { ! char *ns = s; ! ch = step_char(&ns, +1, endline); ! while (s < ns) ! putchr(*s++); ! if (!utf_mode) ! { ! cmd_col++; ! prompt_col++; ! } ! #if !SMALL ! else if (!is_composing_char(ch) && ! !is_combining_char(prev_ch, ch)) ! { ! int width = is_wide_char(ch) ? 2 : 1; ! cmd_col += width; ! prompt_col += width; ! } ! #endif /* !SMALL */ ! prev_ch = ch; ! } } /* *************** *** 135,144 **** public int len_cmdbuf() { ! return (strlen(cmdbuf)); } /* * Repaint the line from cp onwards. * Then position the cursor just after the char old_cp (a pointer into cmdbuf). */ --- 174,292 ---- public int len_cmdbuf() { ! char *s = cmdbuf; ! char *endline = s + strlen(s); ! int len = 0; ! ! while (*s != '\0') ! { ! step_char(&s, +1, endline); ! len++; ! } ! return (len); } /* + * Common part of cmd_step_right() and cmd_step_left(). + */ + static char * + cmd_step_common(p, ch, len, pwidth, bswidth) + char *p; + LWCHAR ch; + int len; + int *pwidth; + int *bswidth; + { + char *pr; + + if (len == 1) + { + pr = prchar((int) ch); + if (pwidth != NULL || bswidth != NULL) + { + int len = strlen(pr); + if (pwidth != NULL) + *pwidth = len; + if (bswidth != NULL) + *bswidth = len; + } + } + #if !SMALL + else + { + pr = prutfchar(ch); + if (pwidth != NULL || bswidth != NULL) + { + if (is_composing_char(ch)) + { + if (pwidth != NULL) + *pwidth = 0; + if (bswidth != NULL) + *bswidth = 0; + } else if (is_ubin_char(ch)) + { + int len = strlen(pr); + if (pwidth != NULL) + *pwidth = len; + if (bswidth != NULL) + *bswidth = len; + } else + { + LWCHAR prev_ch = step_char(&p, -1, cmdbuf); + if (is_combining_char(prev_ch, ch)) + { + if (pwidth != NULL) + *pwidth = 0; + if (bswidth != NULL) + *bswidth = 0; + } else + { + if (pwidth != NULL) + *pwidth = is_wide_char(ch) + ? 2 + : 1; + if (bswidth != NULL) + *bswidth = 1; + } + } + } + } + #endif /* !SMALL */ + + return (pr); + } + + /* + * Step a pointer one character right in the command buffer. + */ + static char * + cmd_step_right(pp, pwidth, bswidth) + char **pp; + int *pwidth; + int *bswidth; + { + char *p = *pp; + LWCHAR ch = step_char(pp, +1, p + strlen(p)); + + return cmd_step_common(p, ch, *pp - p, pwidth, bswidth); + } + + /* + * Step a pointer one character left in the command buffer. + */ + static char * + cmd_step_left(pp, pwidth, bswidth) + char **pp; + int *pwidth; + int *bswidth; + { + char *p = *pp; + LWCHAR ch = step_char(pp, -1, cmdbuf); + + return cmd_step_common(*pp, ch, p - *pp, pwidth, bswidth); + } + + /* * Repaint the line from cp onwards. * Then position the cursor just after the char old_cp (a pointer into cmdbuf). */ *************** *** 146,165 **** cmd_repaint(old_cp) char *old_cp; { - char *p; - /* * Repaint the line from the current position. */ clear_eol(); ! for ( ; *cp != '\0'; cp++) { ! p = prchar(*cp); ! if (cmd_col + (int)strlen(p) >= sc_width) break; ! putstr(p); ! cmd_col += strlen(p); } /* * Back up the cursor to the correct position. --- 294,324 ---- cmd_repaint(old_cp) char *old_cp; { /* * Repaint the line from the current position. */ clear_eol(); ! while (*cp != '\0') { ! char *np = cp; ! int width; ! char *pr = cmd_step_right(&np, &width, NULL); ! if (cmd_col + width >= sc_width) break; ! cp = np; ! putstr(pr); ! cmd_col += width; } + while (*cp != '\0') + { + char *np = cp; + int width; + char *pr = cmd_step_right(&np, &width, NULL); + if (width > 0) + break; + cp = np; + putstr(pr); + } /* * Back up the cursor to the correct position. *************** *** 177,184 **** { while (cmd_col > prompt_col) { ! putbs(); ! cmd_col--; } cp = &cmdbuf[cmd_offset]; --- 336,347 ---- { while (cmd_col > prompt_col) { ! int width, bswidth; ! ! cmd_step_left(&cp, &width, &bswidth); ! while (bswidth-- > 0) ! putbs(); ! cmd_col -= width; } cp = &cmdbuf[cmd_offset]; *************** *** 201,207 **** s = cmdbuf + cmd_offset; cols = 0; while (cols < (sc_width - prompt_col) / 2 && *s != '\0') ! cols += strlen(prchar(*s++)); cmd_offset = s - cmdbuf; save_cp = cp; --- 364,383 ---- s = cmdbuf + cmd_offset; cols = 0; while (cols < (sc_width - prompt_col) / 2 && *s != '\0') ! { ! int width; ! cmd_step_right(&s, &width, NULL); ! cols += width; ! } ! while (*s != '\0') ! { ! int width; ! char *ns = s; ! cmd_step_right(&ns, &width, NULL); ! if (width > 0) ! break; ! s = ns; ! } cmd_offset = s - cmdbuf; save_cp = cp; *************** *** 216,222 **** cmd_rshift() { char *s; - char *p; char *save_cp; int cols; --- 392,397 ---- *************** *** 229,236 **** cols = 0; while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) { ! p = prchar(*--s); ! cols += strlen(p); } cmd_offset = s - cmdbuf; --- 404,412 ---- cols = 0; while (cols < (sc_width - prompt_col) / 2 && s > cmdbuf) { ! int width; ! cmd_step_left(&s, &width, NULL); ! cols += width; } cmd_offset = s - cmdbuf; *************** *** 245,267 **** static int cmd_right() { ! char *p; if (*cp == '\0') { ! /* ! * Already at the end of the line. ! */ return (CC_OK); } ! p = prchar(*cp); ! if (cmd_col + (int)strlen(p) >= sc_width) cmd_lshift(); ! else if (cmd_col + (int)strlen(p) == sc_width - 1 && cp[1] != '\0') cmd_lshift(); ! cp++; ! putstr(p); ! cmd_col += strlen(p); return (CC_OK); } --- 421,452 ---- static int cmd_right() { ! char *pr; ! char *ncp; ! int width; if (*cp == '\0') { ! /* Already at the end of the line. */ return (CC_OK); } ! ncp = cp; ! pr = cmd_step_right(&ncp, &width, NULL); ! if (cmd_col + width >= sc_width) cmd_lshift(); ! else if (cmd_col + width == sc_width - 1 && cp[1] != '\0') cmd_lshift(); ! cp = ncp; ! cmd_col += width; ! putstr(pr); ! while (*cp != '\0') ! { ! pr = cmd_step_right(&ncp, &width, NULL); ! if (width > 0) ! break; ! putstr(pr); ! cp = ncp; ! } return (CC_OK); } *************** *** 271,289 **** static int cmd_left() { ! char *p; if (cp <= cmdbuf) { /* Already at the beginning of the line */ return (CC_OK); } ! p = prchar(cp[-1]); ! if (cmd_col < prompt_col + (int)strlen(p)) cmd_rshift(); ! cp--; ! cmd_col -= strlen(p); ! while (*p++ != '\0') putbs(); return (CC_OK); } --- 456,481 ---- static int cmd_left() { ! char *ncp; ! int width, bswidth; if (cp <= cmdbuf) { /* Already at the beginning of the line */ return (CC_OK); } ! ncp = cp; ! while (ncp > cmdbuf) ! { ! cmd_step_left(&ncp, &width, &bswidth); ! if (width > 0) ! break; ! } ! if (cmd_col < prompt_col + width) cmd_rshift(); ! cp = ncp; ! cmd_col -= width; ! while (bswidth-- > 0) putbs(); return (CC_OK); } *************** *** 292,318 **** * Insert a char into the command buffer, at the current position. */ static int ! cmd_ichar(c) ! int c; { char *s; ! if (strlen(cmdbuf) >= sizeof(cmdbuf)-2) { ! /* ! * No room in the command buffer for another char. ! */ bell(); return (CC_ERROR); } /* ! * Insert the character into the buffer. */ for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) ! s[1] = s[0]; ! *cp = c; /* * Reprint the tail of the line from the inserted char. */ cmd_repaint(cp); --- 484,513 ---- * Insert a char into the command buffer, at the current position. */ static int ! cmd_ichar(cs, clen) ! char *cs; ! int clen; { char *s; ! if (strlen(cmdbuf) + clen >= sizeof(cmdbuf)-1) { ! /* No room in the command buffer for another char. */ bell(); return (CC_ERROR); } /* ! * Make room for the new character (shift the tail of the buffer right). */ for (s = &cmdbuf[strlen(cmdbuf)]; s >= cp; s--) ! s[clen] = s[0]; /* + * Insert the character into the buffer. + */ + for (s = cp; s < cp + clen; s++) + *s = *cs++; + /* * Reprint the tail of the line from the inserted char. */ cmd_repaint(cp); *************** *** 328,333 **** --- 523,529 ---- cmd_erase() { register char *s; + int clen; if (cp == cmdbuf) { *************** *** 340,351 **** /* * Move cursor left (to the char being erased). */ cmd_left(); /* * Remove the char from the buffer (shift the buffer left). */ ! for (s = cp; *s != '\0'; s++) ! s[0] = s[1]; /* * Repaint the buffer after the erased char. */ --- 536,555 ---- /* * Move cursor left (to the char being erased). */ + s = cp; cmd_left(); + clen = s - cp; + /* * Remove the char from the buffer (shift the buffer left). */ ! for (s = cp; ; s++) ! { ! s[0] = s[clen]; ! if (s[0] == '\0') ! break; ! } ! /* * Repaint the buffer after the erased char. */ *************** *** 368,376 **** { if (*cp == '\0') { ! /* ! * At end of string; there is no char under the cursor. ! */ return (CC_OK); } /* --- 572,578 ---- { if (*cp == '\0') { ! /* At end of string; there is no char under the cursor. */ return (CC_OK); } /* *************** *** 441,449 **** { if (cmdbuf[0] == '\0') { ! /* ! * Buffer is already empty; abort the current command. ! */ return (CC_QUIT); } cmd_offset = 0; --- 643,649 ---- { if (cmdbuf[0] == '\0') { ! /* Buffer is already empty; abort the current command. */ return (CC_QUIT); } cmd_offset = 0; *************** *** 468,475 **** --- 668,681 ---- void *mlist; int cmdflags; { + #if CMD_HISTORY curr_mlist = (struct mlist *) mlist; curr_cmdflags = cmdflags; + + /* Make sure the next up-arrow moves to the last string in the mlist. */ + if (curr_mlist != NULL) + curr_mlist->curr_mp = curr_mlist; + #endif } #if CMD_HISTORY *************** *** 505,516 **** s = curr_mlist->curr_mp->string; if (s == NULL) s = ""; ! for (cp = cmdbuf; *s != '\0'; s++) ! { ! *cp = *s; cmd_right(); - } - *cp = '\0'; return (CC_OK); } #endif --- 711,719 ---- s = curr_mlist->curr_mp->string; if (s == NULL) s = ""; ! strlcpy(cmdbuf, s, sizeof(cmdbuf)); ! for (cp = cmdbuf; *cp != '\0'; ) cmd_right(); return (CC_OK); } #endif *************** *** 531,548 **** */ if (strlen(cmd) == 0) return; /* ! * Don't save if a duplicate of a command which is already ! * in the history. ! * But select the one already in the history to be current. */ ! for (ml = mlist->next; ml != mlist; ml = ml->next) { - if (strcmp(ml->string, cmd) == 0) - break; - } - if (ml == mlist) - { /* * Did not find command in history. * Save the command and put it at the end of the history list. --- 734,747 ---- */ if (strlen(cmd) == 0) return; + /* ! * Save the command unless it's a duplicate of the ! * last command in the history. */ ! ml = mlist->prev; ! if (ml == mlist || strcmp(ml->string, cmd) != 0) { /* * Did not find command in history. * Save the command and put it at the end of the history list. *************** *** 576,581 **** --- 775,781 ---- if (curr_mlist == NULL) return; cmd_addhist(curr_mlist, cmdbuf); + curr_mlist->modified = 1; #endif } *************** *** 663,668 **** --- 863,872 ---- case EC_LINEKILL: not_in_completion(); return (cmd_kill()); + case EC_ABORT: + not_in_completion(); + (void) cmd_kill(); + return (CC_QUIT); case EC_W_BACKSPACE: not_in_completion(); return (cmd_werase()); *************** *** 705,714 **** { char *s; int action; ! for (s = str; *s != '\0'; s++) { ! action = cmd_ichar(*s); if (action != CC_OK) { bell(); --- 909,921 ---- { char *s; int action; + char *endline = str + strlen(str); ! for (s = str; *s != '\0'; ) { ! char *os = s; ! step_char(&s, +1, endline); ! action = cmd_ichar(os, s - os); if (action != CC_OK) { bell(); *************** *** 995,1014 **** int c; { int action; if (literal) { /* * Insert the char, even if it is a line-editing char. */ literal = 0; ! return (cmd_ichar(c)); } /* ! * See if it is a special line-editing character. */ ! if (in_mca()) { action = cmd_edit(c); switch (action) --- 1202,1274 ---- int c; { int action; + int len; + if (!utf_mode) + { + cmd_mbc_buf[0] = c; + len = 1; + } + #if !SMALL + else + { + /* Perform strict validation in all possible cases. */ + if (cmd_mbc_buf_len == 0) + { + retry: + cmd_mbc_buf_index = 1; + *cmd_mbc_buf = c; + if (IS_ASCII_OCTET(c)) + cmd_mbc_buf_len = 1; + else if (IS_UTF8_LEAD(c)) + { + cmd_mbc_buf_len = utf_len(c); + return (CC_OK); + } else + { + /* UTF8_INVALID or stray UTF8_TRAIL */ + bell(); + return (CC_ERROR); + } + } else if (IS_UTF8_TRAIL(c)) + { + cmd_mbc_buf[cmd_mbc_buf_index++] = c; + if (cmd_mbc_buf_index < cmd_mbc_buf_len) + return (CC_OK); + if (!is_utf8_well_formed(cmd_mbc_buf)) + { + /* complete, but not well formed (non-shortest form), sequence */ + cmd_mbc_buf_len = 0; + bell(); + return (CC_ERROR); + } + } else + { + /* Flush incomplete (truncated) sequence. */ + cmd_mbc_buf_len = 0; + bell(); + /* Handle new char. */ + goto retry; + } + + len = cmd_mbc_buf_len; + cmd_mbc_buf_len = 0; + } + #endif /* !SMALL */ + if (literal) { /* * Insert the char, even if it is a line-editing char. */ literal = 0; ! return (cmd_ichar(cmd_mbc_buf, len)); } /* ! * See if it is a line-editing character. */ ! if (in_mca() && len == 1) { action = cmd_edit(c); switch (action) *************** *** 1024,1043 **** /* * Insert the char into the command buffer. */ ! return (cmd_ichar(c)); } /* * Return the number currently in the command buffer. */ public LINENUM ! cmd_int() { ! register char *p; LINENUM n = 0; ! for (p = cmdbuf; *p != '\0'; p++) ! n = (10 * n) + (*p - '0'); return (n); } --- 1284,1311 ---- /* * Insert the char into the command buffer. */ ! return (cmd_ichar(cmd_mbc_buf, len)); } /* * Return the number currently in the command buffer. */ public LINENUM ! cmd_int(frac) ! long *frac; { ! char *p; LINENUM n = 0; + int err; ! for (p = cmdbuf; *p >= '0' && *p <= '9'; p++) ! n = (n * 10) + (*p - '0'); ! *frac = 0; ! if (*p++ == '.') ! { ! *frac = getfraction(&p, NULL, &err); ! /* {{ do something if err is set? }} */ ! } return (n); } *************** *** 1048,1051 **** --- 1316,1512 ---- get_cmdbuf() { return (cmdbuf); + } + + #if CMD_HISTORY + /* + * Return the last (most recent) string in the current command history. + */ + public char * + cmd_lastpattern() + { + if (curr_mlist == NULL) + return (NULL); + return (curr_mlist->curr_mp->prev->string); + } + #endif + + #if CMD_HISTORY + /* + * Get the name of the history file. + */ + static char * + histfile_name() + { + char *home; + char *name; + int len; + + /* See if filename is explicitly specified by $LESSHISTFILE. */ + name = lgetenv("LESSHISTFILE"); + if (name != NULL && *name != '\0') + { + if (strcmp(name, "-") == 0 || strcmp(name, "/dev/null") == 0) + /* $LESSHISTFILE == "-" means don't use a history file. */ + return (NULL); + return (save(name)); + } + + /* Otherwise, file is in $HOME. */ + home = lgetenv("HOME"); + if (home == NULL || *home == '\0') + { + #if OS2 + home = lgetenv("INIT"); + if (home == NULL || *home == '\0') + #endif + return (NULL); + } + len = strlen(home) + strlen(LESSHISTFILE) + 2; + name = (char *) ecalloc(len, sizeof(char)); + SNPRINTF2(name, len, "%s/%s", home, LESSHISTFILE); + return (name); + } + #endif /* CMD_HISTORY */ + + /* + * Initialize history from a .lesshist file. + */ + public void + init_cmdhist() + { + #if CMD_HISTORY + struct mlist *ml = NULL; + char line[CMDBUF_SIZE]; + char *filename; + FILE *f; + char *p; + + filename = histfile_name(); + if (filename == NULL) + return; + f = fopen(filename, "r"); + free(filename); + if (f == NULL) + return; + if (fgets(line, sizeof(line), f) == NULL || + strncmp(line, HISTFILE_FIRST_LINE, strlen(HISTFILE_FIRST_LINE)) != 0) + { + fclose(f); + return; + } + while (fgets(line, sizeof(line), f) != NULL) + { + for (p = line; *p != '\0'; p++) + { + if (*p == '\n' || *p == '\r') + { + *p = '\0'; + break; + } + } + if (strcmp(line, HISTFILE_SEARCH_SECTION) == 0) + ml = &mlist_search; + else if (strcmp(line, HISTFILE_SHELL_SECTION) == 0) + { + #if SHELL_ESCAPE || PIPEC + ml = &mlist_shell; + #else + ml = NULL; + #endif + } else if (*line == '"') + { + if (ml != NULL) + cmd_addhist(ml, line+1); + } + } + fclose(f); + #endif /* CMD_HISTORY */ + } + + /* + * + */ + #if CMD_HISTORY + static void + save_mlist(ml, f) + struct mlist *ml; + FILE *f; + { + int histsize = 0; + int n; + char *s; + + s = lgetenv("LESSHISTSIZE"); + if (s != NULL) + histsize = atoi(s); + if (histsize == 0) + histsize = 100; + + ml = ml->prev; + for (n = 0; n < histsize; n++) + { + if (ml->string == NULL) + break; + ml = ml->prev; + } + for (ml = ml->next; ml->string != NULL; ml = ml->next) + fprintf(f, "\"%s\n", ml->string); + } + #endif /* CMD_HISTORY */ + + /* + * + */ + public void + save_cmdhist() + { + #if CMD_HISTORY + char *filename; + FILE *f; + int modified = 0; + + filename = histfile_name(); + if (filename == NULL) + return; + if (mlist_search.modified) + modified = 1; + #if SHELL_ESCAPE || PIPEC + if (mlist_shell.modified) + modified = 1; + #endif + if (!modified) + return; + f = fopen(filename, "w"); + free(filename); + if (f == NULL) + return; + #if HAVE_FCHMOD + { + /* Make history file readable only by owner. */ + int do_chmod = 1; + #if HAVE_STAT + struct stat statbuf; + int r = fstat(fileno(f), &statbuf); + if (r < 0 || !S_ISREG(statbuf.st_mode)) + /* Don't chmod if not a regular file. */ + do_chmod = 0; + #endif + if (do_chmod) + fchmod(fileno(f), 0600); + } + #endif + + fprintf(f, "%s\n", HISTFILE_FIRST_LINE); + + fprintf(f, "%s\n", HISTFILE_SEARCH_SECTION); + save_mlist(&mlist_search, f); + + #if SHELL_ESCAPE || PIPEC + fprintf(f, "%s\n", HISTFILE_SHELL_SECTION); + save_mlist(&mlist_shell, f); + #endif + + fclose(f); + #endif /* CMD_HISTORY */ }