summaryrefslogtreecommitdiffstats
path: root/fish/fish.c
diff options
context:
space:
mode:
Diffstat (limited to 'fish/fish.c')
-rw-r--r--fish/fish.c88
1 files changed, 84 insertions, 4 deletions
diff --git a/fish/fish.c b/fish/fish.c
index a57472f8..4e45ceaf 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -61,6 +61,7 @@ static void shell_script (void);
static void script (int prompt);
static void cmdline (char *argv[], int optind, int argc);
static struct parsed_command parse_command_line (char *buf, int *exit_on_error_rtn);
+static int parse_quoted_string (char *p);
static int execute_and_inline (const char *cmd, int exit_on_error);
static void initialize_readline (void);
static void cleanup_readline (void);
@@ -758,9 +759,8 @@ parse_command_line (char *buf, int *exit_on_error_rtn)
*/
if (*p == '"') {
p++;
- len = strcspn (p, "\"");
- if (p[len] == '\0') {
- fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
+ len = parse_quoted_string (p);
+ if (len == -1) {
pcmd.status = -1;
return pcmd;
}
@@ -771,7 +771,6 @@ parse_command_line (char *buf, int *exit_on_error_rtn)
pcmd.status = -1;
return pcmd;
}
- p[len] = '\0';
pend = p[len+1] ? &p[len+2] : &p[len+1];
} else if (*p == '\'') {
p++;
@@ -835,6 +834,87 @@ parse_command_line (char *buf, int *exit_on_error_rtn)
return pcmd;
}
+static int
+hexdigit (char d)
+{
+ switch (d) {
+ case '0'...'9': return d - '0';
+ case 'a'...'f': return d - 'a' + 10;
+ case 'A'...'F': return d - 'A' + 10;
+ default: return -1;
+ }
+}
+
+/* Parse double-quoted strings, replacing backslash escape sequences
+ * with the true character. Since the string is returned in place,
+ * the escapes must make the string shorter.
+ */
+static int
+parse_quoted_string (char *p)
+{
+ char *start = p;
+
+ for (; *p && *p != '"'; p++) {
+ if (*p == '\\') {
+ int m = 1, c;
+
+ switch (p[1]) {
+ case '\\': break;
+ case 'a': *p = '\a'; break;
+ case 'b': *p = '\b'; break;
+ case 'f': *p = '\f'; break;
+ case 'n': *p = '\n'; break;
+ case 'r': *p = '\r'; break;
+ case 't': *p = '\t'; break;
+ case 'v': *p = '\v'; break;
+ case '"': *p = '"'; break;
+ case '\'': *p = '\''; break;
+ case '?': *p = '?'; break;
+
+ case '0'...'7': /* octal escape - always 3 digits */
+ m = 3;
+ if (p[2] >= '0' && p[2] <= '7' &&
+ p[3] >= '0' && p[3] <= '7') {
+ c = (p[1] - '0') * 0100 + (p[2] - '0') * 010 + (p[3] - '0');
+ if (c < 1 || c > 255)
+ goto error;
+ *p = c;
+ }
+ else
+ goto error;
+ break;
+
+ case 'x': /* hex escape - always 2 digits */
+ m = 3;
+ if (c_isxdigit (p[2]) && c_isxdigit (p[3])) {
+ c = hexdigit (p[2]) * 0x10 + hexdigit (p[3]);
+ if (c < 1 || c > 255)
+ goto error;
+ *p = c;
+ }
+ else
+ goto error;
+ break;
+
+ default:
+ error:
+ fprintf (stderr, _("%s: invalid escape sequence in string (starting at offset %d)\n"),
+ program_name, (int) (p - start));
+ return -1;
+ }
+ memmove (p+1, p+1+m, strlen (p+1+m) + 1);
+ }
+ }
+
+ if (!*p) {
+ fprintf (stderr, _("%s: unterminated double quote\n"), program_name);
+ return -1;
+ }
+
+ *p = '\0';
+ return p - start;
+}
+
/* Used to handle "<!" (execute command and inline result). */
static int
execute_and_inline (const char *cmd, int global_exit_on_error)