Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 15 Jun 2010 21:34:57 +0000 (UTC)
From:      Jilles Tjoelker <jilles@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r209219 - in head: include lib/libedit
Message-ID:  <201006152134.o5FLYvTO000216@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Tue Jun 15 21:34:57 2010
New Revision: 209219
URL: http://svn.freebsd.org/changeset/base/209219

Log:
  libedit: Allow simple quoting in filename completion.
  
  The completer recognizes characters escaped with backslashes as being
  literal parts of a word, and adds backslashes to avoid almost all
  misinterpretation. In particular, filenames containing spaces can be
  completed correctly.
  
  For bug compatibility with the NetBSD version, the improved completion
  function has a new name, _el_fn_sh_complete, and _el_fn_complete is
  unchanged.
  
  Submitted by:	Guy Yur

Modified:
  head/include/histedit.h
  head/lib/libedit/filecomplete.c
  head/lib/libedit/filecomplete.h

Modified: head/include/histedit.h
==============================================================================
--- head/include/histedit.h	Tue Jun 15 21:11:51 2010	(r209218)
+++ head/include/histedit.h	Tue Jun 15 21:34:57 2010	(r209219)
@@ -106,6 +106,7 @@ int		 el_parse(EditLine *, int, const ch
 int		 el_set(EditLine *, int, ...);
 int		 el_get(EditLine *, int, ...);
 unsigned char	_el_fn_complete(EditLine *, int);
+unsigned char	_el_fn_sh_complete(EditLine *, int);
 
 /*
  * el_set/el_get parameters

Modified: head/lib/libedit/filecomplete.c
==============================================================================
--- head/lib/libedit/filecomplete.c	Tue Jun 15 21:11:51 2010	(r209218)
+++ head/lib/libedit/filecomplete.c	Tue Jun 15 21:34:57 2010	(r209219)
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
 
 static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
     '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
+/* Tilde is deliberately omitted here, we treat it specially. */
+static char extra_quote_chars[] = { ')', '}', '\0' };
 
 
 /********************************/
@@ -380,10 +382,14 @@ fn_complete(EditLine *el,
 	char **(*attempted_completion_function)(const char *, int, int),
 	const char *word_break, const char *special_prefixes,
 	const char *(*app_func)(const char *), size_t query_items,
-	int *completion_type, int *over, int *point, int *end)
+	int *completion_type, int *over, int *point, int *end,
+	const char *(*find_word_start_func)(const char *, const char *),
+	char *(*dequoting_func)(const char *),
+	char *(*quoting_func)(const char *))
 {
 	const LineInfo *li;
 	char *temp;
+	char *dequoted_temp;
 	char **matches;
 	const char *ctemp;
 	size_t len;
@@ -404,11 +410,15 @@ fn_complete(EditLine *el,
 
 	/* We now look backwards for the start of a filename/variable word */
 	li = el_line(el);
-	ctemp = li->cursor;
-	while (ctemp > li->buffer
-	    && !strchr(word_break, ctemp[-1])
-	    && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
-		ctemp--;
+	if (find_word_start_func)
+		ctemp = find_word_start_func(li->buffer, li->cursor);
+	else {
+		ctemp = li->cursor;
+		while (ctemp > li->buffer
+		    && !strchr(word_break, ctemp[-1])
+		    && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
+			ctemp--;
+	}
 
 	len = li->cursor - ctemp;
 #if defined(__SSP__) || defined(__SSP_ALL__)
@@ -421,6 +431,13 @@ fn_complete(EditLine *el,
 	(void)strncpy(temp, ctemp, len);
 	temp[len] = '\0';
 
+	if (dequoting_func) {
+		dequoted_temp = dequoting_func(temp);
+		if (dequoted_temp == NULL)
+			return retval;
+	} else
+		dequoted_temp = NULL;
+
 	/* these can be used by function called in completion_matches() */
 	/* or (*attempted_completion_function)() */
 	if (point != 0)
@@ -430,13 +447,13 @@ fn_complete(EditLine *el,
 
 	if (attempted_completion_function) {
 		int cur_off = (int)(li->cursor - li->buffer);
-		matches = (*attempted_completion_function) (temp,
+		matches = (*attempted_completion_function) (dequoted_temp ? dequoted_temp : temp,
 		    (int)(cur_off - len), cur_off);
 	} else
 		matches = 0;
 	if (!attempted_completion_function || 
 	    (over != NULL && !*over && !matches))
-		matches = completion_matches(temp, complet_func);
+		matches = completion_matches(dequoted_temp ? dequoted_temp : temp, complet_func);
 
 	if (over != NULL)
 		*over = 0;
@@ -451,8 +468,18 @@ fn_complete(EditLine *el,
 		 * possible matches if there is possible completion.
 		 */
 		if (matches[0][0] != '\0') {
+			char *quoted_match;
+			if (quoting_func) {
+				quoted_match = quoting_func(matches[0]);
+				if (quoted_match == NULL)
+					goto free_matches;
+			} else
+				quoted_match = NULL;
+
 			el_deletestr(el, (int) len);
-			el_insertstr(el, matches[0]);
+			el_insertstr(el, quoted_match ? quoted_match : matches[0]);
+
+			free(quoted_match);
 		}
 
 		if (what_to_do == '?')
@@ -514,12 +541,14 @@ fn_complete(EditLine *el,
 			retval = CC_NORM;
 		}
 
+free_matches:
 		/* free elements of array and the array itself */
 		for (i = 0; matches[i]; i++)
 			free(matches[i]);
 		free(matches);
 		matches = NULL;
 	}
+	free(dequoted_temp);
 #if defined(__SSP__) || defined(__SSP_ALL__)
 	free(temp);
 #endif
@@ -536,5 +565,99 @@ _el_fn_complete(EditLine *el, int ch __a
 {
 	return (unsigned char)fn_complete(el, NULL, NULL,
 	    break_chars, NULL, NULL, 100,
-	    NULL, NULL, NULL, NULL);
+	    NULL, NULL, NULL, NULL,
+	    NULL, NULL, NULL);
+}
+
+
+static const char *
+sh_find_word_start(const char *buffer, const char *cursor)
+{
+	const char *word_start = buffer;
+
+	while (buffer < cursor) {
+		if (*buffer == '\\')
+			buffer++;
+		else if (strchr(break_chars, *buffer))
+			word_start = buffer + 1;
+
+		buffer++;
+	}
+
+	return word_start;
+}
+
+
+static char *
+sh_quote(const char *str)
+{
+	const char *src;
+	int extra_len = 0;
+	char *quoted_str, *dst;
+
+	for (src = str; *src != '\0'; src++)
+		if (strchr(break_chars, *src) ||
+		    strchr(extra_quote_chars, *src))
+			extra_len++;
+
+	quoted_str = malloc(sizeof(*quoted_str) *
+	    (strlen(str) + extra_len + 1));
+	if (quoted_str == NULL)
+		return NULL;
+
+	dst = quoted_str;
+	for (src = str; *src != '\0'; src++) {
+		if (strchr(break_chars, *src) ||
+		    strchr(extra_quote_chars, *src))
+			*dst++ = '\\';
+		*dst++ = *src;
+	}
+	*dst = '\0';
+
+	return quoted_str;
+}
+
+
+static char *
+sh_dequote(const char *str)
+{
+	char *dequoted_str, *dst;
+
+	/* save extra space to replace \~ with ./~ */
+	dequoted_str = malloc(sizeof(*dequoted_str) * (strlen(str) + 1 + 1));
+	if (dequoted_str == NULL)
+		return NULL;
+
+	dst = dequoted_str;
+
+	/* dequote \~ at start as ./~ */
+	if (*str == '\\' && str[1] == '~') {
+		str++;
+		*dst++ = '.';
+		*dst++ = '/';
+	}
+
+	while (*str) {
+		if (*str == '\\')
+			str++;
+		if (*str)
+			*dst++ = *str++;
+	}
+	*dst = '\0';
+
+	return dequoted_str;
+}
+
+
+/*
+ * completion function using sh quoting rules; for key binding
+ */
+/* ARGSUSED */
+unsigned char
+_el_fn_sh_complete(EditLine *el, int ch __attribute__((__unused__)))
+{
+	return (unsigned char)fn_complete(el, NULL, NULL,
+	    break_chars, NULL, NULL, 100,
+	    NULL, NULL, NULL, NULL,
+	    sh_find_word_start, sh_dequote, sh_quote);
 }

Modified: head/lib/libedit/filecomplete.h
==============================================================================
--- head/lib/libedit/filecomplete.h	Tue Jun 15 21:11:51 2010	(r209218)
+++ head/lib/libedit/filecomplete.h	Tue Jun 15 21:34:57 2010	(r209219)
@@ -36,7 +36,10 @@ int fn_complete(EditLine *,
     char *(*)(const char *, int),
     char **(*)(const char *, int, int),
     const char *, const char *, const char *(*)(const char *), size_t,
-    int *, int *, int *, int *);
+    int *, int *, int *, int *,
+    const char *(*)(const char *, const char *),
+    char *(*)(const char *),
+    char *(*)(const char *));
 
 void fn_display_match_list(EditLine *, char **, size_t, size_t);
 char *fn_tilde_expand(const char *);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201006152134.o5FLYvTO000216>