Skip to content

Commit 98684cb

Browse files
committed
Improve word detection for ESC . and ESC _ (emacs) and _ (vi)
These line editor commands insert the last word of the lsat command in the history. About that, @davies42 reports: > afaict M-. currently inserts the last word of the last command as > defined only by splitting on spaces > > it would be nice to have at least a little of the improvement > readline provides for this action, e.g. that it should also split > on redirect instructions -- if i do > > ... >file > cat <M-.> > > then in bash the second command is 'cat file', but in ksh, it's > 'cat >file', which is actively harmful, since it wipes 'file' src/cmd/ksh93/edit/{hexpand,history}.c: - Move the static is_wordboundary function from hexpand.c to an extern hist_iswordbndry in history.c; we need it for hist_word. - hist_word(): - Instead of isspace, use hist_iswordbndry for word boundaries. This fixes the reported problem. - While we're here, fix another issue: add code to correctly parse $'...' quotes, so that a qutoed word like $'one \' two' is parsed correctly. (re: 7439e3d) Resolves: #802
1 parent b533bc2 commit 98684cb

File tree

4 files changed

+28
-15
lines changed

4 files changed

+28
-15
lines changed

NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
77
- Fixed crash upon resizing the terminal window after 'stty -echo', including
88
when invoking ksh as a child shell in an editor such as emacs (M-x shell).
99

10+
- The ESC . and ESC _ commands in emacs/gmacs and the _ command in vi, which
11+
insert the last word of the last command, now:
12+
- recognise not only spaces but also the following shell grammar
13+
characters as word boundaries: |&;()`<>
14+
- correctly recognise a word that is quoted with the $'...' syntax,
15+
even when it includes a backslash-escaped single quote.
16+
1017
2025-03-24:
1118

1219
- Fixed a crash that occurred in specific circumstances when processing a

src/cmd/ksh93/edit/hexpand.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,6 @@ static char *parse_subst(const char *s, struct subst *sb)
126126
return cp;
127127
}
128128

129-
/*
130-
* return true if c is a word boundary character, i.e. the
131-
* character following c is considered to start a new word
132-
*/
133-
134-
static int is_wordboundary(char c)
135-
{
136-
return isspace(c) || strchr("|&;()`<>",c);
137-
}
138-
139129
/*
140130
* assign history expansion characters to an array of 3
141131
*/
@@ -212,7 +202,7 @@ int hist_expand(const char *ln, char **xp)
212202

213203
if(hc[2] && *cp == hc[2])
214204
{
215-
if(cp == ln || is_wordboundary(cp[-1]))
205+
if(cp == ln || hist_iswordbndry(cp[-1]))
216206
{
217207
/* word begins with history comment character; skip rest of line */
218208
sfputr(sh.stk,cp,0);

src/cmd/ksh93/edit/history.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -998,13 +998,23 @@ int hist_copy(char *s1,int size,int command,int line)
998998
return count;
999999
}
10001000

1001+
/*
1002+
* return true if c is a word boundary character, i.e. the
1003+
* character following c is considered to start a new word
1004+
*/
1005+
1006+
int hist_iswordbndry(char c)
1007+
{
1008+
return isspace(c) || strchr("|&;()`<>",c);
1009+
}
1010+
10011011
/*
10021012
* return word number <word> from command number <command>
10031013
*/
10041014
char *hist_word(char *string,int size,int word)
10051015
{
10061016
int c;
1007-
int is_space;
1017+
int is_boundary;
10081018
int quoted;
10091019
char *s1 = string;
10101020
unsigned char *cp = (unsigned char*)s1;
@@ -1015,15 +1025,15 @@ char *hist_word(char *string,int size,int word)
10151025
hist_copy(string,size,(int)hp->histind-1,-1);
10161026
for(quoted=0;c = *cp;cp++)
10171027
{
1018-
is_space = isspace(c) && !quoted;
1019-
if(is_space && flag)
1028+
is_boundary = !quoted && hist_iswordbndry(c);
1029+
if(is_boundary && flag)
10201030
{
10211031
*cp = 0;
10221032
if(--word==0)
10231033
break;
10241034
flag = 0;
10251035
}
1026-
else if(is_space==0 && flag==0)
1036+
else if(is_boundary==0 && flag==0)
10271037
{
10281038
s1 = (char*)cp;
10291039
flag++;
@@ -1038,6 +1048,11 @@ char *hist_word(char *string,int size,int word)
10381048
for(cp++;*cp && (*cp != c || quoted);cp++)
10391049
quoted = *cp=='\\' ? !quoted : 0;
10401050
}
1051+
else if (c=='$' && cp[1]=='\'' && !quoted)
1052+
{
1053+
for(cp+=2; *cp && (*cp != '\'' || quoted); cp++)
1054+
quoted = *cp=='\\' ? !quoted : 0;
1055+
}
10411056
quoted = *cp=='\\' ? !quoted : 0;
10421057
}
10431058
*cp = 0;

src/cmd/ksh93/include/history.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ extern void hist_list(History_t*,Sfio_t*, off_t, int, char*);
8383
extern int hist_match(History_t*,off_t, char*, int*);
8484
extern off_t hist_tell(History_t*,int);
8585
extern off_t hist_seek(History_t*,int);
86+
extern int hist_iswordbndry(char);
8687
extern char *hist_word(char*, int, int);
8788
#if !_BLD_ksh || SHOPT_ESH
8889
extern Histloc_t hist_locate(History_t*,int, int, int);

0 commit comments

Comments
 (0)