Fix error reporting in ksh's csh-history

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Fix error reporting in ksh's csh-history

Michael McConville-2
ksh offers csh-style history aliases when you set csh-history. This
feature currently works. However, when an alias doesn't exist ksh
reports only the last digit (thanks to Theo for tipping me off on this):

> mike:/tmp/tech:1$ set -o csh-history
> mike:/tmp/tech:2$ ls
> a  b  c
> mike:/tmp/tech:3$ !2
> a  b  c
> mike:/tmp/tech:4$ !3456789
> ksh: !9: not found

The below diff fixes this. Basically, this feature is implemented in the
lexer, and ksh doesn't rewind through the whole alias after realizing
that it doesn't exist.

I don't entirely understand the lexer, so I did this super defensively.
If anyone knows of a smarter way, please share.

I moved the var declarations farther out because they're now needed in
the outer scope.


Index: lex.c
===================================================================
RCS file: /cvs/src/bin/ksh/lex.c,v
retrieving revision 1.51
diff -u -p -r1.51 lex.c
--- lex.c 10 Sep 2015 22:48:58 -0000 1.51
+++ lex.c 13 Sep 2015 20:44:30 -0000
@@ -163,6 +163,9 @@ yylex(int cf)
  if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) &&
     c == '!') {
  char **replace = NULL;
+ int get, i;
+ char match[200], *str = match;
+ size_t mlen;
 
  c2 = getsc();
  if (c2 == '\0' || c2 == ' ' || c2 == '\t')
@@ -171,8 +174,7 @@ yylex(int cf)
  replace = hist_get_newest(0);
  else if (isdigit(c2) || c2 == '-' ||
     isalpha(c2)) {
- int get = !isalpha(c2);
- char match[200], *str = match;
+ get = !isalpha(c2);
 
  *str++ = c2;
  do {
@@ -216,8 +218,20 @@ yylex(int cf)
  s->u.freeme = NULL;
  source = s;
  continue;
- } else
- ungetsc(c2);
+ } else {
+ /*
+ * We do this the robust, safe, stupid way. We
+ * should just be able to use:
+ *
+ * source->str = MAX(source->start,
+ *     source->str - (strlen(match)-1));
+ */
+ mlen = strlen(match);
+ if (source->start <= (source->str - mlen)) {
+ for (i = mlen-1; i >= 0; i--)
+ ungetsc(match[i]);
+ }
+ }
  }
  if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
  *wp = EOS; /* temporary */

Reply | Threaded
Open this post in threaded view
|

Re: Fix error reporting in ksh's csh-history

Michael McConville-2
Michael McConville wrote:

> ksh offers csh-style history aliases when you set csh-history. This
> feature currently works. However, when an alias doesn't exist ksh
> reports only the last digit (thanks to Theo for tipping me off on this):
>
> > mike:/tmp/tech:1$ set -o csh-history
> > mike:/tmp/tech:2$ ls
> > a  b  c
> > mike:/tmp/tech:3$ !2
> > a  b  c
> > mike:/tmp/tech:4$ !3456789
> > ksh: !9: not found
>
> The below diff fixes this. Basically, this feature is implemented in the
> lexer, and ksh doesn't rewind through the whole alias after realizing
> that it doesn't exist.
>
> I don't entirely understand the lexer, so I did this super defensively.
> If anyone knows of a smarter way, please share.
>
> I moved the var declarations farther out because they're now needed in
> the outer scope.

Ping, with a simpler diff.


Index: lex.c
===================================================================
RCS file: /cvs/src/bin/ksh/lex.c,v
retrieving revision 1.52
diff -u -p -r1.52 lex.c
--- lex.c 15 Sep 2015 18:15:05 -0000 1.52
+++ lex.c 16 Sep 2015 23:11:49 -0000
@@ -163,6 +163,9 @@ yylex(int cf)
  if (Flag(FCSHHISTORY) && (source->flags & SF_TTY) &&
     c == '!') {
  char **replace = NULL;
+ int get, i;
+ char match[200], *str = match;
+ size_t mlen;
 
  c2 = getsc();
  if (c2 == '\0' || c2 == ' ' || c2 == '\t')
@@ -171,8 +174,7 @@ yylex(int cf)
  replace = hist_get_newest(0);
  else if (isdigit(c2) || c2 == '-' ||
     isalpha(c2)) {
- int get = !isalpha(c2);
- char match[200], *str = match;
+ get = !isalpha(c2);
 
  *str++ = c2;
  do {
@@ -216,8 +218,12 @@ yylex(int cf)
  s->u.freeme = NULL;
  source = s;
  continue;
- } else
- ungetsc(c2);
+ } else {
+ /* restore what followed the '!' */
+ mlen = strlen(match);
+ for (i = mlen-1; i >= 0; i--)
+ ungetsc(match[i]);
+ }
  }
  if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
  *wp = EOS; /* temporary */