Martin Hedenfalk
2006-03-22 13:32:06 UTC
Here is a patch adding a history-search-backward and -forward function
to ksh that many bash-users might be accustomed to. It's an implicitly
anchored version of "search-history" using the prefix of the current
line up to the cursor as search pattern, discarding duplicate matches.
Can be bound to up/down arrow.
The patch is also available at
http://hedenfalk.se/patches/ksh-history.patch in case it gets messed up
in the mail.
I've been using this patch for a week now. It seems to be stable.
/Martin
Index: emacs.c
===================================================================
RCS file: /cvs/src/bin/ksh/emacs.c,v
retrieving revision 1.39
diff -u -d -r1.39 emacs.c
--- emacs.c 26 Sep 2005 19:25:22 -0000 1.39
+++ emacs.c 16 Mar 2006 07:10:31 -0000
@@ -106,6 +106,7 @@
static int x_curprefix;
static char *macroptr;
static int prompt_skip;
+static char x_search_hist_pattern [256+1];
static int x_ins(char *);
static void x_delete(int, int);
@@ -119,6 +120,8 @@
static void x_zotc(int);
static void x_load_hist(char **);
static int x_search(char *, int, int);
+static int x_search_delta(char *, int, int, int);
+static int x_search_delta_distinct(char *, int, int, int);
static int x_match(char *, char *);
static void x_redraw(int);
static void x_push(int);
@@ -190,6 +193,8 @@
{ x_search_char_forw, "search-character-forward", XF_ARG },
{ x_search_char_back, "search-character-backward", XF_ARG },
{ x_search_hist, "search-history", 0 },
+ { x_search_hist_backward, "history-search-backward", 0 },
+ { x_search_hist_forward, "history-search-forward", 0 },
{ x_set_mark, "set-mark-command", 0 },
{ x_stuff, "stuff", 0 },
{ x_stuffreset, "stuff-reset", 0 },
@@ -860,9 +865,7 @@
if ((c = x_e_getc()) < 0)
return KSTD;
f = x_tab[0][c&CHARMASK];
- if (c == CTRL('['))
- break;
- else if (f == XFUNC_search_hist)
+ if (f == XFUNC_search_hist)
offset = x_search(pat, 0, offset);
else if (f == XFUNC_del_back) {
if (p == pat) {
@@ -904,14 +907,90 @@
return KSTD;
}
-/* search backward from current line */
+/* determine if prefix history searching should be performed, or plain
+ * up/down history traversing */
static int
-x_search(char *pat, int sameline, int offset)
+x_search_hist_has_prefix(void)
+{
+ size_t pat_len;
+
+ /* skip prefix history search if at beginning of line */
+ if (xcp == xbuf)
+ return 0;
+
+ /* prepare the prefix search pattern */
+ pat_len = xcp - xbuf;
+ x_search_hist_pattern[0] = '^';
+ if (pat_len > 256)
+ pat_len = 256;
+ strlcpy(x_search_hist_pattern + 1, xbuf, pat_len + 1);
+ return 1;
+}
+
+/* a modal version of (up|down)-history, necessary in order not to
+ * confuse with prefix history searches */
+static void
+x_search_hist_modal(int delta)
+{
+ int c;
+ u_char f;
+
+ x_load_hist(x_histp + delta);
+
+ while(1) {
+ x_flush();
+ if ((c = x_e_getc()) < 0)
+ break;
+ f = x_tab[x_curprefix][c&CHARMASK];
+ if (f == XFUNC_search_hist_backward) {
+ x_load_hist(x_histp - 1);
+ x_curprefix = 0;
+ } else if (f == XFUNC_search_hist_forward) {
+ x_load_hist(x_histp + 1);
+ x_curprefix = 0;
+ } else if (f == XFUNC_meta1)
+ x_meta1(0);
+ else if (f == XFUNC_meta2)
+ x_meta2(0);
+ else {
+ x_e_ungetc(c);
+ break;
+ }
+ }
+}
+
+/* search history backwards for line with same prefix */
+static int
+x_search_hist_backward(int c)
+{
+ if (x_search_hist_has_prefix())
+ x_search_delta_distinct(x_search_hist_pattern, 0, 0, -1);
+ else
+ x_search_hist_modal(-1);
+ return KSTD;
+}
+
+/* search history forwards for line with same prefix */
+static int
+x_search_hist_forward(int c)
+{
+ if (x_search_hist_has_prefix())
+ x_search_delta_distinct(x_search_hist_pattern, 0, 0, +1);
+ else
+ x_search_hist_modal(+1);
+ return KSTD;
+}
+
+/* search backward or forward from current line depending on delta */
+static int
+x_search_delta(char *pat, int sameline, int offset, int delta)
{
char **hp;
int i;
- for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) {
+ for (hp = x_histp + (sameline ? 0 : delta) ;
+ hp >= history && hp <= histptr ;
+ hp += delta) {
i = x_match(*hp, pat);
if (i >= 0) {
if (offset < 0)
@@ -924,6 +1003,31 @@
x_e_putc(BEL);
x_histp = histptr;
return -1;
+}
+
+/* just as x_search_delta, but skips duplicate matches */
+static int
+x_search_delta_distinct(char *pat, int sameline, int offset, int delta)
+{
+ char *orig_xbuf;
+ int rc;
+
+ orig_xbuf = strdup(xbuf);
+
+ while(1) {
+ rc = x_search_delta(pat, sameline, offset, delta);
+ if(rc < 0 || strcmp(orig_xbuf, *x_histp) != 0)
+ break;
+ }
+ free(orig_xbuf);
+ return rc;
+}
+
+/* search backward from current line */
+static int
+x_search(char *pat, int sameline, int offset)
+{
+ return x_search_delta(pat, sameline, offset, -1);
}
/* return position of first match of pattern in string, else -1 */
Index: ksh.1
===================================================================
RCS file: /cvs/src/bin/ksh/ksh.1,v
retrieving revision 1.110
diff -u -d -r1.110 ksh.1
--- ksh.1 7 Mar 2006 09:31:02 -0000 1.110
+++ ksh.1 16 Mar 2006 07:10:31 -0000
@@ -4837,6 +4837,14 @@
.Xc
Goes to history number
.Ar n .
+.It history-search-backward:
+Search the internal history list backwards for a line beginning with the
+current content of the input buffer up to the cursor. Duplicate matches
+are skipped.
+.It history-search-forward:
+Search the internal history list forwards for a line beginning with the
+current content of the input buffer up to the cursor. Duplicate matches
+are skipped.
.It kill-line: KILL
Deletes the entire input line.
.It kill-region: ^W
to ksh that many bash-users might be accustomed to. It's an implicitly
anchored version of "search-history" using the prefix of the current
line up to the cursor as search pattern, discarding duplicate matches.
Can be bound to up/down arrow.
The patch is also available at
http://hedenfalk.se/patches/ksh-history.patch in case it gets messed up
in the mail.
I've been using this patch for a week now. It seems to be stable.
/Martin
Index: emacs.c
===================================================================
RCS file: /cvs/src/bin/ksh/emacs.c,v
retrieving revision 1.39
diff -u -d -r1.39 emacs.c
--- emacs.c 26 Sep 2005 19:25:22 -0000 1.39
+++ emacs.c 16 Mar 2006 07:10:31 -0000
@@ -106,6 +106,7 @@
static int x_curprefix;
static char *macroptr;
static int prompt_skip;
+static char x_search_hist_pattern [256+1];
static int x_ins(char *);
static void x_delete(int, int);
@@ -119,6 +120,8 @@
static void x_zotc(int);
static void x_load_hist(char **);
static int x_search(char *, int, int);
+static int x_search_delta(char *, int, int, int);
+static int x_search_delta_distinct(char *, int, int, int);
static int x_match(char *, char *);
static void x_redraw(int);
static void x_push(int);
@@ -190,6 +193,8 @@
{ x_search_char_forw, "search-character-forward", XF_ARG },
{ x_search_char_back, "search-character-backward", XF_ARG },
{ x_search_hist, "search-history", 0 },
+ { x_search_hist_backward, "history-search-backward", 0 },
+ { x_search_hist_forward, "history-search-forward", 0 },
{ x_set_mark, "set-mark-command", 0 },
{ x_stuff, "stuff", 0 },
{ x_stuffreset, "stuff-reset", 0 },
@@ -860,9 +865,7 @@
if ((c = x_e_getc()) < 0)
return KSTD;
f = x_tab[0][c&CHARMASK];
- if (c == CTRL('['))
- break;
- else if (f == XFUNC_search_hist)
+ if (f == XFUNC_search_hist)
offset = x_search(pat, 0, offset);
else if (f == XFUNC_del_back) {
if (p == pat) {
@@ -904,14 +907,90 @@
return KSTD;
}
-/* search backward from current line */
+/* determine if prefix history searching should be performed, or plain
+ * up/down history traversing */
static int
-x_search(char *pat, int sameline, int offset)
+x_search_hist_has_prefix(void)
+{
+ size_t pat_len;
+
+ /* skip prefix history search if at beginning of line */
+ if (xcp == xbuf)
+ return 0;
+
+ /* prepare the prefix search pattern */
+ pat_len = xcp - xbuf;
+ x_search_hist_pattern[0] = '^';
+ if (pat_len > 256)
+ pat_len = 256;
+ strlcpy(x_search_hist_pattern + 1, xbuf, pat_len + 1);
+ return 1;
+}
+
+/* a modal version of (up|down)-history, necessary in order not to
+ * confuse with prefix history searches */
+static void
+x_search_hist_modal(int delta)
+{
+ int c;
+ u_char f;
+
+ x_load_hist(x_histp + delta);
+
+ while(1) {
+ x_flush();
+ if ((c = x_e_getc()) < 0)
+ break;
+ f = x_tab[x_curprefix][c&CHARMASK];
+ if (f == XFUNC_search_hist_backward) {
+ x_load_hist(x_histp - 1);
+ x_curprefix = 0;
+ } else if (f == XFUNC_search_hist_forward) {
+ x_load_hist(x_histp + 1);
+ x_curprefix = 0;
+ } else if (f == XFUNC_meta1)
+ x_meta1(0);
+ else if (f == XFUNC_meta2)
+ x_meta2(0);
+ else {
+ x_e_ungetc(c);
+ break;
+ }
+ }
+}
+
+/* search history backwards for line with same prefix */
+static int
+x_search_hist_backward(int c)
+{
+ if (x_search_hist_has_prefix())
+ x_search_delta_distinct(x_search_hist_pattern, 0, 0, -1);
+ else
+ x_search_hist_modal(-1);
+ return KSTD;
+}
+
+/* search history forwards for line with same prefix */
+static int
+x_search_hist_forward(int c)
+{
+ if (x_search_hist_has_prefix())
+ x_search_delta_distinct(x_search_hist_pattern, 0, 0, +1);
+ else
+ x_search_hist_modal(+1);
+ return KSTD;
+}
+
+/* search backward or forward from current line depending on delta */
+static int
+x_search_delta(char *pat, int sameline, int offset, int delta)
{
char **hp;
int i;
- for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) {
+ for (hp = x_histp + (sameline ? 0 : delta) ;
+ hp >= history && hp <= histptr ;
+ hp += delta) {
i = x_match(*hp, pat);
if (i >= 0) {
if (offset < 0)
@@ -924,6 +1003,31 @@
x_e_putc(BEL);
x_histp = histptr;
return -1;
+}
+
+/* just as x_search_delta, but skips duplicate matches */
+static int
+x_search_delta_distinct(char *pat, int sameline, int offset, int delta)
+{
+ char *orig_xbuf;
+ int rc;
+
+ orig_xbuf = strdup(xbuf);
+
+ while(1) {
+ rc = x_search_delta(pat, sameline, offset, delta);
+ if(rc < 0 || strcmp(orig_xbuf, *x_histp) != 0)
+ break;
+ }
+ free(orig_xbuf);
+ return rc;
+}
+
+/* search backward from current line */
+static int
+x_search(char *pat, int sameline, int offset)
+{
+ return x_search_delta(pat, sameline, offset, -1);
}
/* return position of first match of pattern in string, else -1 */
Index: ksh.1
===================================================================
RCS file: /cvs/src/bin/ksh/ksh.1,v
retrieving revision 1.110
diff -u -d -r1.110 ksh.1
--- ksh.1 7 Mar 2006 09:31:02 -0000 1.110
+++ ksh.1 16 Mar 2006 07:10:31 -0000
@@ -4837,6 +4837,14 @@
.Xc
Goes to history number
.Ar n .
+.It history-search-backward:
+Search the internal history list backwards for a line beginning with the
+current content of the input buffer up to the cursor. Duplicate matches
+are skipped.
+.It history-search-forward:
+Search the internal history list forwards for a line beginning with the
+current content of the input buffer up to the cursor. Duplicate matches
+are skipped.
.It kill-line: KILL
Deletes the entire input line.
.It kill-region: ^W