Le 16/12/2012 01:47, Colomban Wendling a écrit : > [...] > > OK, to chose appropriate quoting you may argue one needs to understand > sub-command syntax. Expanding the command: > > cmd "$(cmd2 %f)" > > to > > cmd "$(cmd2 "'xxx.c'")" > > is incorrect -- and that's what my patch would do, because it doesn't > understand sub-shells. We may or may not want to make sure to support > sub-shells -- that's not that hard, but may also not be that useful, but > whatever.
Attached an updated version of my patch to deal with `` and $() sub-shell syntax. Cheers, Colomban
diff --git a/src/build.c b/src/build.c index 7a2d84a..db198d7 100644 --- a/src/build.c +++ b/src/build.c @@ -706,60 +706,141 @@ static void parse_build_output(const gchar **output, gint status) #endif -/* Replaces occurences of %e and %p with the appropriate filenames, - * %d and %p replacements should be in UTF8 */ +/* Replaces %f, %d, %e and %p placeholders in a shell-style string. + * + * This functions reads @p src as a shell-style input, understanding quoting (with "" and '') + * as well as nested sub-commands (with `` and $(), up to 16 levels). It makes sure the + * placeholder replacements are properly quoted. + * + * Returns: an UTF-8 string with placeholders replaced. */ static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src) { GString *stack; - gchar *filename = NULL; - gchar *replacement; - gchar *executable = NULL; - gchar *ret_str; /* to be freed when not in use anymore */ + struct { + gchar quote; + gchar terminator; + } nesting[16] = {{ 0, 0 }}; + guint level = 0; + /* replacements, %<letter> */ + gchar *f = NULL; /* %f: basename */ + gchar *d = NULL; /* %d: dirname */ + gchar *e = NULL; /* %e: basename without extension */ + gchar *p = NULL; /* %p: project (absolute) base directory */ + + if (src == NULL) + return NULL; - stack = g_string_new(src); if (doc != NULL && doc->file_name != NULL) { - filename = utils_get_utf8_from_locale(doc->file_name); - - /* replace %f with the filename (including extension) */ - replacement = g_path_get_basename(filename); - utils_string_replace_all(stack, "%f", replacement); - g_free(replacement); + gchar *filename = utils_get_utf8_from_locale(doc->file_name); - /* replace %d with the absolute path of the dir of the current file */ - replacement = g_path_get_dirname(filename); - utils_string_replace_all(stack, "%d", replacement); - g_free(replacement); + f = g_path_get_basename(filename); + d = g_path_get_dirname(filename); + e = utils_remove_ext_from_filename(f); - /* replace %e with the filename (excluding extension) */ - executable = utils_remove_ext_from_filename(filename); - replacement = g_path_get_basename(executable); - utils_string_replace_all(stack, "%e", replacement); - g_free(replacement); + g_free(filename); } - - /* replace %p with the current project's (absolute) base directory */ - replacement = NULL; /* prevent double free if no replacement found */ if (app->project) + p = project_get_base_path(); + + /* quote the replacements */ + if (f) SETPTR(f, g_shell_quote(f)); + if (d) SETPTR(d, g_shell_quote(d)); + if (e) SETPTR(e, g_shell_quote(e)); + if (p) SETPTR(p, g_shell_quote(p)); + + stack = g_string_new(NULL); + for (; *src; src++) { - replacement = project_get_base_path(); - } - else if (strstr(stack->str, "%p")) - { /* fall back to %d */ - ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active")); - if (doc != NULL && filename != NULL) - replacement = g_path_get_dirname(filename); - } + if (*src == nesting[level].terminator) + { + g_string_append_c(stack, *src); + level--; + continue; + } - utils_string_replace_all(stack, "%p", replacement); - g_free(replacement); + switch (*src) + { + case '`': + g_string_append_c(stack, *src); + if (nesting[level].quote != '\'' && + level < G_N_ELEMENTS(nesting)) + { + level ++; + nesting[level].quote = 0; + nesting[level].terminator = *src; + } + break; - ret_str = utils_get_utf8_from_locale(stack->str); - g_free(executable); - g_free(filename); - g_string_free(stack, TRUE); + case '$': + g_string_append_c(stack, *src); + if (nesting[level].quote != '\'' && + level < G_N_ELEMENTS(nesting) && + src[1] == '(') + { + src++; + g_string_append_c(stack, *src); + level ++; + nesting[level].quote = 0; + nesting[level].terminator = ')'; + } + break; + + case '"': + case '\'': + if (! nesting[level].quote) + nesting[level].quote = *src; + else if (nesting[level].quote == *src) + nesting[level].quote = 0; + g_string_append_c(stack, *src); + break; + + case '%': + src++; + if (nesting[level].quote) /* close the quote */ + g_string_append_c(stack, nesting[level].quote); + if (*src == 'f' && f) + g_string_append(stack, f); + else if (*src == 'd' && d) + g_string_append(stack, d); + else if (*src == 'e' && e) + g_string_append(stack, e); + else if (*src == 'p') + { + if (p) + g_string_append(stack, p); + else + { /* fallback to %d */ + ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active")); + if (d) + g_string_append(stack, d); + } + } + else + { /* just leave the placeholder */ + g_string_append_c(stack, '%'); + g_string_append_c(stack, *src); + } + if (nesting[level].quote) /* re-open quote */ + g_string_append_c(stack, nesting[level].quote); + break; + + case '\\': + g_string_append_c(stack, *src); + src++; + /* fallthrough */ + default: + g_string_append_c(stack, *src); + break; + } + } + + g_free (f); + g_free (d); + g_free (e); + g_free (p); - return ret_str; /* don't forget to free src also if needed */ + return g_string_free(stack, FALSE); }
_______________________________________________ Devel mailing list Devel@lists.geany.org https://lists.geany.org/cgi-bin/mailman/listinfo/devel