Module Name:    src
Committed By:   abhinav
Date:           Sun Sep  8 05:50:58 UTC 2019

Modified Files:
        src/lib/libedit: filecomplete.c
        src/lib/libedit/TEST: test_filecompletion.c

Log Message:
PR lib/54510: Fix file completion inside quotes which broke in rev 1.53

While there also fix handling character appending in the file completions when
inside quotes. For example when inside a quote, if the completion is a 
directory then
append a '/' but don't close the quote. On the other hand when inside a quote 
if the
completion is a file name and it is the only match then we can close the quote.


To generate a diff of this commit:
cvs rdiff -u -r1.57 -r1.58 src/lib/libedit/filecomplete.c
cvs rdiff -u -r1.4 -r1.5 src/lib/libedit/TEST/test_filecompletion.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libedit/filecomplete.c
diff -u src/lib/libedit/filecomplete.c:1.57 src/lib/libedit/filecomplete.c:1.58
--- src/lib/libedit/filecomplete.c:1.57	Sun Jul 28 09:27:29 2019
+++ src/lib/libedit/filecomplete.c	Sun Sep  8 05:50:58 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: filecomplete.c,v 1.57 2019/07/28 09:27:29 christos Exp $	*/
+/*	$NetBSD: filecomplete.c,v 1.58 2019/09/08 05:50:58 abhinav Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include "config.h"
 #if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: filecomplete.c,v 1.57 2019/07/28 09:27:29 christos Exp $");
+__RCSID("$NetBSD: filecomplete.c,v 1.58 2019/09/08 05:50:58 abhinav Exp $");
 #endif /* not lint && not SCCSID */
 
 #include <sys/types.h>
@@ -192,7 +192,8 @@ unescape_string(const wchar_t *string, s
 }
 
 static char *
-escape_filename(EditLine * el, const char *filename)
+escape_filename(EditLine * el, const char *filename, int single_match,
+		const char *(*app_func)(const char *))
 {
 	size_t original_len = 0;
 	size_t escaped_character_count = 0;
@@ -204,6 +205,7 @@ escape_filename(EditLine * el, const cha
 	size_t d_quoted = 0;	/* does the input contain a double quote */
 	char *escaped_str;
 	wchar_t *temp = el->el_line.buffer;
+	const char *append_char = NULL;
 
 	if (filename == NULL)
 		return NULL;
@@ -246,6 +248,9 @@ escape_filename(EditLine * el, const cha
 	if (s_quoted || d_quoted)
 		newlen++;
 
+	if (single_match && app_func)
+		newlen++;
+
 	if ((escaped_str = el_malloc(newlen)) == NULL)
 		return NULL;
 
@@ -285,11 +290,24 @@ escape_filename(EditLine * el, const cha
 		escaped_str[offset++] = c;
 	}
 
-	/* close the quotes */
-	if (s_quoted)
-		escaped_str[offset++] = '\'';
-	else if (d_quoted)
-		escaped_str[offset++] = '"';
+	if (single_match && app_func) {
+		escaped_str[offset] = 0;
+		append_char = app_func(escaped_str);
+		/* we want to append space only if we are not inside quotes */
+		if (append_char[0] == ' ') {
+			if (!s_quoted && !d_quoted)
+				escaped_str[offset++] = append_char[0];
+		} else
+			escaped_str[offset++] = append_char[0];
+	}
+
+	/* close the quotes if single match and the match is not a directory */
+	if (single_match && (append_char && append_char[0] == ' ')) { 
+		if (s_quoted)
+			escaped_str[offset++] = '\'';
+		else if (d_quoted)
+			escaped_str[offset++] = '"';
+	}
 
 	escaped_str[offset] = 0;
 	return escaped_str;
@@ -596,6 +614,10 @@ find_word_to_complete(const wchar_t * cu
 			if (ctemp - buffer >= 2 && ctemp[-2] == '\\') {
 				ctemp -= 2;
 				continue;
+			} else if (ctemp - buffer >= 2 &&
+			    (ctemp[-2] == '\'' || ctemp[-2] == '"')) {
+				ctemp--;
+				continue;
 			} else
 				break;
 		}
@@ -605,6 +627,10 @@ find_word_to_complete(const wchar_t * cu
 	}
 
 	len = (size_t) (cursor - ctemp);
+	if (len == 1 && (ctemp[0] == '\'' || ctemp[0] == '"')) {
+		len = 0;
+		ctemp++;
+	}
 	*length = len;
 	wchar_t *unescaped_word = unescape_string(ctemp, len);
 	if (unescaped_word == NULL)
@@ -689,31 +715,29 @@ fn_complete(EditLine *el,
 		retval = CC_REFRESH;
 
 		if (matches[0][0] != '\0') {
-			el_deletestr(el, (int) len);
+			el_deletestr(el, (int)len);
 			if (!attempted_completion_function)
-				completion = escape_filename(el, matches[0]);
+				completion = escape_filename(el, matches[0],
+				    single_match, app_func);
 			else
 				completion = strdup(matches[0]);
 			if (completion == NULL)
 				goto out;
 			if (single_match) {
-				/*
-				 * We found exact match. Add a space after
-				 * it, unless we do filename completion and the
-				 * object is a directory. Also do necessary escape quoting
+				/* We found exact match. Add a space after it,
+				 * unless we do filename completion and the
+				 * object is a directory. Also do necessary
+				 * escape quoting
 				 */
 				el_winsertstr(el,
-					ct_decode_string(completion, &el->el_scratch));
-				el_winsertstr(el,
-						ct_decode_string((*app_func)(completion),
-							&el->el_scratch));
+				    ct_decode_string(completion, &el->el_scratch));
 			} else {
-				/*
-				 * Only replace the completed string with common part of
-				 * possible matches if there is possible completion.
+				/* Only replace the completed string with
+				 * common part of possible matches if there is
+				 * possible completion.
 				 */
 				el_winsertstr(el,
-					ct_decode_string(completion, &el->el_scratch));
+				    ct_decode_string(completion, &el->el_scratch));
 			}
 			free(completion);
 		}

Index: src/lib/libedit/TEST/test_filecompletion.c
diff -u src/lib/libedit/TEST/test_filecompletion.c:1.4 src/lib/libedit/TEST/test_filecompletion.c:1.5
--- src/lib/libedit/TEST/test_filecompletion.c:1.4	Sun Mar 31 03:04:57 2019
+++ src/lib/libedit/TEST/test_filecompletion.c	Sun Sep  8 05:50:58 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: test_filecompletion.c,v 1.4 2019/03/31 03:04:57 abhinav Exp $	*/
+/*	$NetBSD: test_filecompletion.c,v 1.5 2019/09/08 05:50:58 abhinav Exp $	*/
 
 /*-
  * Copyright (c) 2017 Abhinav Upadhyay <abhi...@netbsd.org>
@@ -62,14 +62,14 @@ static test_input inputs[] = {
 		L"ls \"dq_ang",
 		"dq_ang",
 		{"dq_ang<ular>test", NULL},
-		L"ls \"dq_ang<ular>test\" "
+		L"ls \"dq_ang<ular>test\""
 	},
 	{
 		/* test angular bracket inside singlq quotes: ls "sq_ang */
 		L"ls 'sq_ang",
 		"sq_ang",
 		{"sq_ang<ular>test", NULL},
-		L"ls 'sq_ang<ular>test' "
+		L"ls 'sq_ang<ular>test'"
 	},
 	{
 		/* simple test for backslash */
@@ -83,14 +83,14 @@ static test_input inputs[] = {
 		L"ls 'sback",
 		"sback",
 		{"sbackslash\\test", NULL},
-		L"ls 'sbackslash\\test' "
+		L"ls 'sbackslash\\test'"
 	},
 	{
 		/* backslash inside double quotes */
 		L"ls \"dback",
 		"dback",
 		{"dbackslash\\test", NULL},
-		L"ls \"dbackslash\\\\test\" "
+		L"ls \"dbackslash\\\\test\""
 	},
 	{
 		/* test braces */
@@ -104,14 +104,14 @@ static test_input inputs[] = {
 		L"ls 'sbr",
 		"sbr",
 		{"sbraces{test}", NULL},
-		L"ls 'sbraces{test}' "
+		L"ls 'sbraces{test}'"
 	},
 	{
 		/* test braces inside double quotes */
 		L"ls \"dbr",
 		"dbr",
 		{"dbraces{test}", NULL},
-		L"ls \"dbraces{test}\" "
+		L"ls \"dbraces{test}\""
 	},
 	{
 		/* test dollar */
@@ -125,14 +125,14 @@ static test_input inputs[] = {
 		L"ls 'sdoll",
 		"sdoll",
 		{"sdoll$artest", NULL},
-		L"ls 'sdoll$artest' "
+		L"ls 'sdoll$artest'"
 	},
 	{
 		/* test dollar inside double quotes */
 		L"ls \"ddoll",
 		"ddoll",
 		{"ddoll$artest", NULL},
-		L"ls \"ddoll\\$artest\" "
+		L"ls \"ddoll\\$artest\""
 	},
 	{
 		/* test equals */
@@ -146,14 +146,14 @@ static test_input inputs[] = {
 		L"ls 'seq",
 		"seq",
 		{"sequals==test", NULL},
-		L"ls 'sequals==test' "
+		L"ls 'sequals==test'"
 	},
 	{
 		/* test equals inside double quotes */
 		L"ls \"deq",
 		"deq",
 		{"dequals==test", NULL},
-		L"ls \"dequals==test\" "
+		L"ls \"dequals==test\""
 	},
 	{
 		/* test \n */
@@ -167,14 +167,14 @@ static test_input inputs[] = {
 		L"ls 'snew",
 		"snew",
 		{"snew\nline", NULL},
-		L"ls 'snew\nline' "
+		L"ls 'snew\nline'"
 	},
 	{
 		/* test \n inside double quotes */
 		L"ls \"dnew",
 		"dnew",
 		{"dnew\nline", NULL},
-		L"ls \"dnew\nline\" "
+		L"ls \"dnew\nline\""
 	},
 	{
 		/* test single space */
@@ -188,14 +188,14 @@ static test_input inputs[] = {
 		L"ls 's_spac",
 		"s_spac",
 		{"s_space test", NULL},
-		L"ls 's_space test' "
+		L"ls 's_space test'"
 	},
 	{
 		/* test single space inside double quotes */
 		L"ls \"d_spac",
 		"d_spac",
 		{"d_space test", NULL},
-		L"ls \"d_space test\" "
+		L"ls \"d_space test\""
 	},
 	{
 		/* test multiple spaces */
@@ -209,14 +209,14 @@ static test_input inputs[] = {
 		L"ls 's_multi",
 		"s_multi",
 		{"s_multi space  test", NULL},
-		L"ls 's_multi space  test' "
+		L"ls 's_multi space  test'"
 	},
 	{
 		/* test multiple spaces inside double quotes */
 		L"ls \"d_multi",
 		"d_multi",
 		{"d_multi space  test", NULL},
-		L"ls \"d_multi space  test\" "
+		L"ls \"d_multi space  test\""
 	},
 	{
 		/* test double quotes */
@@ -230,14 +230,14 @@ static test_input inputs[] = {
 		L"ls 's_doub",
 		"s_doub",
 		{"s_doub\"quotes", NULL},
-		L"ls 's_doub\"quotes' "
+		L"ls 's_doub\"quotes'"
 	},
 	{
 		/* test double quotes inside double quotes */
 		L"ls \"d_doub",
 		"d_doub",
 		{"d_doub\"quotes", NULL},
-		L"ls \"d_doub\\\"quotes\" "
+		L"ls \"d_doub\\\"quotes\""
 	},
 	{
 		/* test multiple double quotes */
@@ -251,14 +251,14 @@ static test_input inputs[] = {
 		L"ls 'smud",
 		"smud",
 		{"smud\"qu\"otes\"", NULL},
-		L"ls 'smud\"qu\"otes\"' "
+		L"ls 'smud\"qu\"otes\"'"
 	},
 	{
 		/* test multiple double quotes inside double quotes */
 		L"ls \"dmud",
 		"dmud",
 		{"dmud\"qu\"otes\"", NULL},
-		L"ls \"dmud\\\"qu\\\"otes\\\"\" "
+		L"ls \"dmud\\\"qu\\\"otes\\\"\""
 	},
 	{
 		/* test one single quote */
@@ -272,14 +272,14 @@ static test_input inputs[] = {
 		L"ls 'ssing",
 		"ssing",
 		{"ssingle'quote", NULL},
-		L"ls 'ssingle'\\''quote' "
+		L"ls 'ssingle'\\''quote'"
 	},
 	{
 		/* test one single quote inside double quote */
 		L"ls \"dsing",
 		"dsing",
 		{"dsingle'quote", NULL},
-		L"ls \"dsingle'quote\" "
+		L"ls \"dsingle'quote\""
 	},
 	{
 		/* test multiple single quotes */
@@ -293,14 +293,14 @@ static test_input inputs[] = {
 		L"ls 'smu_sing",
 		"smu_sing",
 		{"smu_single''quotes''", NULL},
-		L"ls 'smu_single'\\'''\\''quotes'\\\'''\\''' "
+		L"ls 'smu_single'\\'''\\''quotes'\\\'''\\'''"
 	},
 	{
 		/* test multiple single quotes inside double quote */
 		L"ls \"dmu_sing",
 		"dmu_sing",
 		{"dmu_single''quotes''", NULL},
-		L"ls \"dmu_single''quotes''\" "
+		L"ls \"dmu_single''quotes''\""
 	},
 	{
 		/* test parenthesis */
@@ -314,14 +314,14 @@ static test_input inputs[] = {
 		L"ls 'sparen",
 		"sparen",
 		{"sparen(test)", NULL},
-		L"ls 'sparen(test)' "
+		L"ls 'sparen(test)'"
 	},
 	{
 		/* test parenthesis inside double quote */
 		L"ls \"dparen",
 		"dparen",
 		{"dparen(test)", NULL},
-		L"ls \"dparen(test)\" "
+		L"ls \"dparen(test)\""
 	},
 	{
 		/* test pipe */
@@ -335,14 +335,14 @@ static test_input inputs[] = {
 		L"ls 'spip",
 		"spip",
 		{"spipe|test", NULL},
-		L"ls 'spipe|test' ",
+		L"ls 'spipe|test'",
 	},
 	{
 		/* test pipe inside double quote */
 		L"ls \"dpip",
 		"dpip",
 		{"dpipe|test", NULL},
-		L"ls \"dpipe|test\" "
+		L"ls \"dpipe|test\""
 	},
 	{
 		/* test tab */
@@ -356,14 +356,14 @@ static test_input inputs[] = {
 		L"ls 'sta",
 		"sta",
 		{"stab\ttest", NULL},
-		L"ls 'stab\ttest' "
+		L"ls 'stab\ttest'"
 	},
 	{
 		/* test tab inside double quote */
 		L"ls \"dta",
 		"dta",
 		{"dtab\ttest", NULL},
-		L"ls \"dtab\ttest\" "
+		L"ls \"dtab\ttest\""
 	},
 	{
 		/* test back tick */
@@ -377,14 +377,14 @@ static test_input inputs[] = {
 		L"ls 'stic",
 		"stic",
 		{"stick`test`", NULL},
-		L"ls 'stick`test`' "
+		L"ls 'stick`test`'"
 	},
 	{
 		/* test back tick inside double quote */
 		L"ls \"dtic",
 		"dtic",
 		{"dtick`test`", NULL},
-		L"ls \"dtick\\`test\\`\" "
+		L"ls \"dtick\\`test\\`\""
 	},
 	{
 		/* test for @ */
@@ -398,14 +398,14 @@ static test_input inputs[] = {
 		L"ls 'sat",
 		"sat",
 		{"satthe@rate", NULL},
-		L"ls 'satthe@rate' "
+		L"ls 'satthe@rate'"
 	},
 	{
 		/* test for @ inside double quote */
 		L"ls \"dat",
 		"dat",
 		{"datthe@rate", NULL},
-		L"ls \"datthe@rate\" "
+		L"ls \"datthe@rate\""
 	},
 	{
 		/* test ; */
@@ -419,14 +419,14 @@ static test_input inputs[] = {
 		L"ls 'ssemi",
 		"ssemi",
 		{"ssemi;colon;test", NULL},
-		L"ls 'ssemi;colon;test' "
+		L"ls 'ssemi;colon;test'"
 	},
 	{
 		/* test ; inside double quote */
 		L"ls \"dsemi",
 		"dsemi",
 		{"dsemi;colon;test", NULL},
-		L"ls \"dsemi;colon;test\" "
+		L"ls \"dsemi;colon;test\""
 	},
 	{
 		/* test & */
@@ -440,14 +440,14 @@ static test_input inputs[] = {
 		L"ls 'samp",
 		"samp",
 		{"sampers&and", NULL},
-		L"ls 'sampers&and' "
+		L"ls 'sampers&and'"
 	},
 	{
 		/* test & inside double quote */
 		L"ls \"damp",
 		"damp",
 		{"dampers&and", NULL},
-		L"ls \"dampers&and\" "
+		L"ls \"dampers&and\""
 	},
 	{
 		/* test completion when cursor at \ */

Reply via email to