Subject: fix for pd-ksh globbing
To: None <tech-userlevel@netbsd.org>
From: Simon J. Gerraty <sjg@crufty.net>
List: tech-userlevel
Date: 01/25/2002 01:36:09
Ok, this bug has anoyed me for years. I finally made time to take a
look at it.
In ksh's source directory if you do
echo $PWD/em<ESC><ESC>
it will expand to say:
echo /u3/NetBSD/current/src/bin/ksh/em<SPACE>
instead of
echo /u3/NetBSD/current/src/bin/ksh/emacs
with no trailing space.
With real ksh
echo ~<ESC><ESC>
echo $HOME<ESC><ESC>
both expand with a trailing '/' and no space. In pd-ksh we get a
space instead. This is due to interactions b/w add_glob(), expand()
and do_complete().
The patch below fixes the above - at the cost of an extra stat() call.
While find_match might be overkill but I've had it for years and it works.
One difference b/w pd-ksh and real ksh which I've not addressed -
since its a bug in real ksh, is:
FOO=bar
echo $FOO<ESC><ESC>
real ksh apparently expands this to
echo bar*<SPACE>
whereas pd-ksh expands it
echo bar<SPACE>
which is more correct me thinks.
Thanks
--sjg
Index: edit.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/edit.c,v
retrieving revision 1.6
diff -u -p -r1.6 edit.c
--- edit.c 1999/11/02 22:06:45 1.6
+++ edit.c 2002/01/25 09:26:24
@@ -840,6 +840,40 @@ x_cf_glob(flags, buf, buflen, pos, start
return nwords;
}
+
+static char *
+find_match(const char *s, int have)
+{
+ int want = 0;
+ int nest = 1;
+ int c;
+
+ switch (have) {
+ case '(': want = ')'; break;
+ case '{': want = '}'; break;
+ case '[': want = ']'; break;
+ case '\'':
+ case '"': want = have; break;
+ }
+ if (want == 0 || s == NULL)
+ return NULL;
+
+ while (nest > 0 && (c = *s)) {
+ if (c == '\\') {
+ s++;
+ s++;
+ continue;
+ }
+ if (c == want)
+ nest--;
+ else if (c == have)
+ nest++;
+ if (nest > 0)
+ s++;
+ }
+ return (nest == 0) ? (char *) s : NULL;
+}
+
/* Given a string, copy it and possibly add a '*' to the end. The
* new string is returned.
*/
@@ -859,19 +893,33 @@ add_glob(str, slen)
toglob[slen] = '\0';
/*
- * If the pathname contains a wildcard (an unquoted '*',
- * '?', or '[') or parameter expansion ('$'), or a ~username
- * with no trailing slash, then it is globbed based on that
- * value (i.e., without the appended '*').
+ * If the pathname contains a wildcard (an unquoted '*', '?',
+ * or '[') or parameter expansion ('$') with nothing following
+ * it, or a ~username with no trailing slash, then it is
+ * globbed based on that value (i.e., without the appended
+ * '*').
*/
for (s = toglob; *s; s++) {
if (*s == '\\' && s[1])
s++;
- else if (*s == '*' || *s == '[' || *s == '?' || *s == '$'
+ else if (*s == '*' || *s == '[' || *s == '?'
|| (s[1] == '(' /*)*/ && strchr("*+?@!", *s)))
break;
else if (ISDIRSEP(*s))
saw_slash = TRUE;
+ else if (*s++ == '$') {
+ if (*s == '{') {
+ char *cp;
+
+ if ((cp = find_match(&s[1], '{')))
+ s = ++cp;
+ }
+ if (*s)
+ s += strcspn(s,
+ ".,/?-<>[]{}()'\";:\\|=+*&^%$#@!`~");
+ if (!*s)
+ return toglob;
+ }
}
if (!*s && (*toglob != '~' || saw_slash)) {
toglob[slen] = '*';
Index: emacs.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/emacs.c,v
retrieving revision 1.10
diff -u -p -r1.10 emacs.c
--- emacs.c 1999/11/09 00:01:49 1.10
+++ emacs.c 2002/01/25 09:26:25
@@ -1768,6 +1768,17 @@ x_expand(c)
return KSTD;
}
+
+static int
+is_dir(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) == 0)
+ return S_ISDIR(st.st_mode);
+ return 0;
+}
+
/* type == 0 for list, 1 for complete and 2 for complete-list */
static void
do_complete(flags, type)
@@ -1854,8 +1865,15 @@ do_complete(flags, type)
* space to the end...
*/
if (nwords == 1
- && !ISDIRSEP(words[0][nlen - 1]))
- x_ins(space);
+ && !ISDIRSEP(words[0][nlen - 1])) {
+ /*
+ * we may be here because we
+ * just expanded $HOME in
+ * which case adding '/' is
+ * correct.
+ */
+ x_ins(is_dir(words[0]) ? slash : space);
+ }
} else
x_e_putc(BEL);
}