Subject: pdksh: escaping special characters when expanding
To: None <pdksh@cs.mun.ca>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: tech-userlevel
Date: 10/07/1999 11:00:40
--ELM939286840-2653-0_
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit

Hi,
I've cleaned up the patches to backslash-quote special shell characters
when expanding the file name (either via tab in vi-tabcomplete
or ^[^[ in emacs mode). I made them work in emacs mode as well and
the value of IFS is now honored.

I think this would be valueable addition to pdksh. It's very comfortable
when dealing with directories or files containing e.g. spaces
(all praise well-known "Program Files").

The patch is in attachment. Please let me know if the change is
okay.

Best regards

Jaromir
-- 
Jaromir Dolecek <jdolecek@NetBSD.org>      http://www.ics.muni.cz/~dolecek/
"The only way how to get rid temptation is to yield to it." -- Oscar Wilde

--ELM939286840-2653-0_
Content-Type: text/plain; charset=ISO-8859-2
Content-Disposition: attachment; filename=pdksh.quoteexp.diff
Content-Description: f
Content-Transfer-Encoding: 7bit

Index: edit.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/edit.c,v
retrieving revision 1.4
diff -u -r1.4 edit.c
--- edit.c	1998/11/04 18:27:21	1.4
+++ edit.c	1999/10/07 08:57:49
@@ -537,7 +537,7 @@
 {
 	char *toglob;
 	char **words;
-	int nwords;
+	int nwords, i, idx, escaping;
 	XPtrV w;
 	struct source *s, *sold;
 
@@ -546,6 +546,20 @@
 
 	toglob = add_glob(str, slen);
 
+	/* remove all escaping backward slashes */
+	escaping = 0;
+	for(i = 0, idx = 0; toglob[i]; i++) {
+		if (toglob[i] == '\\' && !escaping) {
+			escaping = 1;
+			continue;
+		}
+
+		toglob[idx] = toglob[i];
+		idx++;
+		if (escaping) escaping = 0;
+	}
+	toglob[idx] = '\0';
+
 	/*
 	 * Convert "foo*" (toglob) to an array of strings (words)
 	 */
@@ -740,11 +754,14 @@
 	/* Keep going backwards to start of word (has effect of allowing
 	 * one blank after the end of a word)
 	 */
-	for (; start > 0 && IS_WORDC(buf[start - 1]); start--)
+	for (; (start > 0 && IS_WORDC(buf[start - 1]))
+		|| (start > 1 && buf[start-2] == '\\'); start--)
 		;
 	/* Go forwards to end of word */
-	for (end = start; end < buflen && IS_WORDC(buf[end]); end++)
-		;
+	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
+		if (buf[end] == '\\' && (end+1) < buflen && buf[end+1] == ' ')
+			end++;
+	}
 
 	if (is_commandp) {
 		int iscmd;
@@ -1011,4 +1028,40 @@
 	Xfree(xs, xp);
 }
 
+/*
+ * if argument string contains any special characters, they will
+ * be escaped and the result will be put into edit buffer by
+ * keybinding-specific function
+ */
+int
+x_escape(s, len, putbuf_func)
+	const char *s;
+	size_t len;
+	int putbuf_func ARGS((const char *s, size_t len));
+{
+	size_t add, wlen;
+	const char *ifs = str_val(local("IFS", 0));
+	int rval=0;
+
+	for (add = 0, wlen = len; wlen - add > 0; add++) {
+		if (strchr("\\$(){}*&;|<>\"'", s[add]) || strchr(ifs, s[add])) {
+			if (putbuf_func(s, add) != 0) {
+				rval = -1;
+				break;
+			}
+
+			putbuf_func("\\", 1);
+			putbuf_func(&s[add], 1);
+
+			add++;
+			wlen -= add;
+			s += add;
+			add = -1; /* after the increment it will go to 0 */
+		}
+	}
+	if (wlen > 0 && rval == 0)
+		rval = putbuf_func(s, wlen);
+
+	return (rval);
+}
 #endif /* EDIT */
Index: edit.h
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/edit.h,v
retrieving revision 1.2
diff -u -r1.2 edit.h
--- edit.h	1997/01/12 19:11:45	1.2
+++ edit.h	1999/10/07 08:57:49
@@ -57,6 +57,7 @@
 int	x_longest_prefix ARGS((int nwords, char *const *words));
 int	x_basename ARGS((const char *s, const char *se));
 void	x_free_words ARGS((int nwords, char **words));
+int	x_escape ARGS((const char *, size_t, int (*)(const char *s, size_t len)));
 /* emacs.c */
 int 	x_emacs		ARGS((char *buf, size_t len));
 void 	x_init_emacs	ARGS((void));
Index: emacs.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/emacs.c,v
retrieving revision 1.5
diff -u -r1.5 emacs.c
--- emacs.c	1998/11/04 18:27:21	1.5
+++ emacs.c	1999/10/07 08:57:52
@@ -144,6 +144,7 @@
 static char	*x_lastcp ARGS((void));
 static void	do_complete ARGS((int flags, Comp_type type));
 static int 	x_do_ins    ARGS((const char *, int));
+static int	x_emacs_putbuf	ARGS((const char *s, size_t len));
 
 
 /* The lines between START-FUNC-TAB .. END-FUNC-TAB are run through a
@@ -472,6 +473,21 @@
 	return 0;
 }
 
+/*
+ * this is used for x_escape() in do_complete()
+ */
+static int
+x_emacs_putbuf(s, len)
+	const char *s;
+	size_t len;
+{
+	int rval;
+
+	if ((rval = x_do_ins(s, len)) != 0)
+		return (rval);
+	return (rval);
+}
+
 static int
 x_del_back(c)
 	int c;
@@ -1806,8 +1822,8 @@
 			if (nlen > 0) {
 				x_goto(xbuf + start);
 				x_delete(end - start, FALSE);
-				words[0][nlen] = '\0';
-				x_ins(words[0]);
+				x_escape(words[0], nlen, x_emacs_putbuf);
+				x_adjust();
 				/* If single match is not a directory, add a
 				 * space to the end...
 				 */
Index: vi.c
===================================================================
RCS file: /cvsroot/basesrc/bin/ksh/vi.c,v
retrieving revision 1.3
diff -u -r1.3 vi.c
--- vi.c	1998/11/04 18:27:21	1.3
+++ vi.c	1999/10/07 08:57:52
@@ -65,6 +65,7 @@
 static void	vi_pprompt ARGS((int full));
 static void	vi_error ARGS((void));
 static void	vi_macro_reset ARGS((void));
+static int	x_vi_putbuf	ARGS((const char *s, size_t len));
 
 #define C_	0x1		/* a valid command that isn't a M_, E_, U_ */
 #define M_	0x2		/* movement command (h, l, etc.) */
@@ -1471,6 +1472,17 @@
 	holdlen = 0;
 }
 
+/*
+ * this is used for calling x_escape() in complete_word()
+ */
+static int
+x_vi_putbuf(s, len)
+	const char *s;
+	size_t len;
+{
+	return putbuf(s, len, 0);
+}
+
 static int
 putbuf(buf, len, repl)
 	const char *buf;
@@ -2069,9 +2081,12 @@
 	buf = save_edstate(es);
 	del_range(start, end);
 	es->cursor = start;
-	if (putbuf(match, match_len, 0) != 0)
-		rval = -1;
-	else if (is_unique) {
+
+	/* escape all shell-sensitive characters and put the result into
+	 * command buffer */
+	rval = x_escape(match, match_len, x_vi_putbuf);
+
+	if (rval == 0 && is_unique) {
 		/* If exact match, don't undo.  Allows directory completions
 		 * to be used (ie, complete the next portion of the path).
 		 */

--ELM939286840-2653-0_--