@@ -, +, @@ 
---
 scp.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 124 insertions(+), 19 deletions(-)
--- a/scp.c	
+++ a/scp.c	
@@ -645,33 +645,62 @@ append(char *cp, char ***ap, size_t *np)
 }
 
 /*
- * Finds the start and end of the first brace pair in the pattern.
+ * Finds the start and end of the first meta (braces or quotes) pair in the pattern.
  * returns 0 on success or -1 for invalid patterns.
  */
 static int
-find_brace(const char *pattern, int *startp, int *endp)
+find_meta_pair(const char *pattern, int *startp, int *endp)
 {
 	int i;
 	int in_bracket, brace_level;
+	int in_single_qoutes, in_double_qoutes;
 
 	*startp = *endp = -1;
 	in_bracket = brace_level = 0;
+	in_single_qoutes = in_double_qoutes = 0;
+
 	for (i = 0; i < INT_MAX && *endp < 0 && pattern[i] != '\0'; i++) {
 		switch (pattern[i]) {
 		case '\\':
 			/* skip next character */
-			if (pattern[i + 1] != '\0')
+			if (!in_single_qoutes && pattern[i + 1] != '\0')
 				i++;
 			break;
+		case '\'':
+			if (in_bracket || in_double_qoutes)
+				break;
+			if (in_single_qoutes) {
+				*endp = i;
+			} else {
+				*startp = i;
+			}
+			in_single_qoutes = !in_single_qoutes;
+			break;
+		case '"':
+			if (in_bracket || in_single_qoutes)
+				break;
+			if (in_double_qoutes) {
+				*endp = i;
+			} else {
+				*startp = i;
+			}
+			in_double_qoutes = !in_double_qoutes;
+			break;
 		case '[':
+			if (in_single_qoutes || in_double_qoutes)
+				break;
 			in_bracket = 1;
 			break;
 		case ']':
+			if (in_single_qoutes || in_double_qoutes)
+				break;
 			in_bracket = 0;
 			break;
 		case '{':
 			if (in_bracket)
 				break;
+			if (in_single_qoutes || in_double_qoutes)
+				break;
 			if (pattern[i + 1] == '}') {
 				/* Protect a single {}, for find(1), like csh */
 				i++; /* skip */
@@ -684,6 +713,8 @@ find_brace(const char *pattern, int *startp, int *endp)
 		case '}':
 			if (in_bracket)
 				break;
+			if (in_single_qoutes || in_double_qoutes)
+				break;
 			if (*startp < 0) {
 				/* Unbalanced brace */
 				return -1;
@@ -696,6 +727,9 @@ find_brace(const char *pattern, int *startp, int *endp)
 	/* unbalanced brackets/braces */
 	if (*endp < 0 && (*startp >= 0 || in_bracket))
 		return -1;
+	/* unbalanced qoutes */
+	if (in_single_qoutes || in_double_qoutes)
+		return -1;
 	return 0;
 }
 
@@ -738,6 +772,56 @@ emit_expansion(const char *pattern, int brace_start, int brace_end,
 	return 0;
 }
 
+
+/* Expand string with qoutes and put the result to *patternsp array.
+ */
+static int
+emit_quote_expansion(const char *pattern, int quote_start, int quote_end,
+    int single_quotes, char ***patternsp, size_t *npatternsp)
+{
+	char *cp;
+	int o = 0, tail_len = strlen(pattern + quote_end + 1);
+	int i;
+
+	if ((cp = malloc(strlen(pattern) + 1)) == NULL)
+		return -1;
+
+	/* Pattern before initial quote */
+	if (quote_start > 0) {
+		memcpy(cp, pattern, quote_start);
+		o = quote_start;
+	}
+
+	if (single_quotes) {
+		/* Single quoted selection */
+		if (quote_end - quote_start > 0) {
+			memcpy(cp + o, pattern + quote_start + 1,
+			    quote_end - quote_start - 1);
+			o += quote_end - quote_start - 1;
+		}
+	} else {
+		/* Double quoted selection has to handle '\' */
+		for (i = quote_start + 1; i < quote_end; i++) {
+			if ((pattern[i] == '\\') && (i < quote_end - 1)) {
+				i++;
+			}
+			cp[o++] = pattern[i];
+		}
+	}
+
+	/* Remainder of pattern after closing brace */
+	if (tail_len > 0) {
+		memcpy(cp + o, pattern + quote_end + 1, tail_len);
+		o += tail_len;
+	}
+	cp[o] = '\0';
+	if (append(cp, patternsp, npatternsp) != 0) {
+		free(cp);
+		return -1;
+	}
+	return 0;
+}
+
 /*
  * Expand the first encountered brace in pattern, appending the expanded
  * patterns it yielded to the *patternsp array.
@@ -748,21 +832,13 @@ emit_expansion(const char *pattern, int brace_start, int brace_end,
  * pattern was invalid via *invalid.
  */
 static int
-brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
-    int *expanded, int *invalid)
+brace_expand_one(const char *pattern, int brace_start, int brace_end,
+	char ***patternsp, size_t *npatternsp, int *expanded, int *invalid)
 {
 	int i;
-	int in_bracket, brace_start, brace_end, brace_level;
+	int in_bracket, brace_level;
 	int sel_start, sel_end;
 
-	*invalid = *expanded = 0;
-
-	if (find_brace(pattern, &brace_start, &brace_end) != 0) {
-		*invalid = 1;
-		return 0;
-	} else if (brace_start == -1)
-		return 0;
-
 	in_bracket = brace_level = 0;
 	for (i = sel_start = brace_start + 1; i < brace_end; i++) {
 		switch (pattern[i]) {
@@ -811,9 +887,38 @@ brace_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
 	return 0;
 }
 
-/* Expand braces from pattern. Returns 0 on success, -1 on failure */
+
+static int pattern_meta_expand_one(const char *pattern, char ***patternsp, size_t *npatternsp,
+    int *expanded, int *invalid)
+{
+	int meta_start, meta_end;
+
+	*invalid = *expanded = 0;
+
+	if (find_meta_pair(pattern, &meta_start, &meta_end) != 0) {
+		*invalid = 1;
+		return 0;
+	} else if (meta_start == -1)
+		return 0;
+
+	if (pattern[meta_start] == '\'' || pattern[meta_start] == '"')
+	{
+		if (emit_quote_expansion(pattern, meta_start, meta_end, pattern[meta_start] == '\'',
+			 patternsp, npatternsp) != 0) {
+			return -1;
+		}
+		*invalid = 0;
+		*expanded = 1;
+		return 0;
+	}
+
+	return brace_expand_one(pattern, meta_start, meta_end, patternsp, npatternsp,
+							 expanded, invalid);
+}
+
+/* Expand braces and quotes from pattern. Returns 0 on success, -1 on failure */
 static int
-brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
+pattern_meta_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
 {
 	char *cp, *cp2, **active = NULL, **done = NULL;
 	size_t i, nactive = 0, ndone = 0;
@@ -832,13 +937,13 @@ brace_expand(const char *pattern, char ***patternsp, size_t *npatternsp)
 	while (nactive > 0) {
 		cp = active[nactive - 1];
 		nactive--;
-		if (brace_expand_one(cp, &active, &nactive,
+		if (pattern_meta_expand_one(cp, &active, &nactive,
 		    &expanded, &invalid) == -1) {
 			free(cp);
 			goto fail;
 		}
 		if (invalid)
-			fatal("%s: invalid brace pattern \"%s\"", __func__, cp);
+			fatal("%s: invalid filename pattern \"%s\"", __func__, cp);
 		if (expanded) {
 			/*
 			 * Current entry expanded to new entries on the
@@ -1271,7 +1376,7 @@ sink(int argc, char **argv, const char *src)
 		 * Prepare to try to restrict incoming filenames to match
 		 * the requested destination file glob.
 		 */
-		if (brace_expand(src, &patterns, &npatterns) != 0)
+		if (pattern_meta_expand(src, &patterns, &npatterns) != 0)
 			fatal("%s: could not expand pattern", __func__);
 	}
 	for (first = 1;; first = 0) {
-- 
