Skip to content

Instantly share code, notes, and snippets.

@b4n
Created March 29, 2015 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save b4n/4c100d6f1defd4751217 to your computer and use it in GitHub Desktop.
Save b4n/4c100d6f1defd4751217 to your computer and use it in GitHub Desktop.
test for build_replace_placeholder()
/* test for build_replace_placeholder().
* USAGE: [-q | -Q] [basename [dirname [basename-without-ext [line [project]]]]]
*
* example:
* test-build-replace-placeholders -Q "file name" "/dir/name" "file name" 42 < test-build-replace-placeholders.input
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#define SETPTR(a, b) do { gpointer t = (b); g_free(a); a = t; } while(0)
/* Replaces %f, %d, %e, %l 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.
*
* If @quote is FALSE, it only replaces placeholders without adding any quoting. This is
* useful if replacing placeholders in a non-shell string, like working directory.
*
* Returns: an UTF-8 string with placeholders replaced. */
static gchar *build_replace_placeholder(const gchar *f_,
const gchar *d_,
const gchar *e_,
gint l_,
const gchar *p_,
const gchar *src, gboolean quote)
{
GString *stack;
struct {
gchar quote;
gchar terminator;
} nesting[16] = {{ 0, 0 }};
guint level = 0;
/* replacements, %<letter> */
gchar *f = g_strdup(f_); /* %f: basename */
gchar *d = g_strdup(d_); /* %d: dirname */
gchar *e = g_strdup(e_); /* %e: basename without extension */
gint l = l_; /* %l: current 1-based line number */
gchar *p = g_strdup(p_); /* %p: project (absolute) base directory */
/*g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);*/
if (src == NULL)
return NULL;
/*if (doc != NULL && doc->file_name != NULL)
{
gchar *filename = utils_get_utf8_from_locale(doc->file_name);
f = g_path_get_basename(filename);
d = g_path_get_dirname(filename);
e = utils_remove_ext_from_filename(f);
g_free(filename);
}
if (doc != NULL)
l = sci_get_current_line(doc->editor->sci) + 1;
if (app->project)
p = project_get_base_path();*/
if (quote)
{
/* 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++)
{
if (*src == nesting[level].terminator &&
! nesting[level].quote)
{
g_string_append_c(stack, *src);
level--;
continue;
}
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;
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 (quote && 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 == 'l')
g_string_append_printf(stack, "%d", l);
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 (quote && 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 g_string_free(stack, FALSE);
}
#define CONSUME_ARG(c, v) (c--, v++)
int main (int argc, char **argv)
{
const gchar *f = NULL;
const gchar *d = NULL;
const gchar *e = NULL;
gint l = 0;
const gchar *p = NULL;
gboolean quote = FALSE;
gchar input[1024] = {0};
fprintf(stderr, "USAGE: [-q | -Q] [basename [dirname [basename-without-ext [line [project]]]]]\n\n");
if (argc > 1 && !strcmp(argv[1], "-q")) {
quote = TRUE;
CONSUME_ARG(argc, argv);
} else if (argc > 1 && !strcmp(argv[1], "-Q")) {
quote = FALSE;
CONSUME_ARG(argc, argv);
}
if (argc > 1) {
f = argv[1];
CONSUME_ARG(argc, argv);
}
if (argc > 1) {
d = argv[1];
CONSUME_ARG(argc, argv);
}
if (argc > 1) {
e = argv[1];
CONSUME_ARG(argc, argv);
}
if (argc > 1) {
l = atoi(argv[1]);
CONSUME_ARG(argc, argv);
}
if (argc > 1) {
p = argv[1];
CONSUME_ARG(argc, argv);
}
while (fgets(input, sizeof input, stdin))
{
gchar *res;
size_t len = strlen(input);
if (len > 0 && input[len - 1] == '\n')
input[len - 1] = 0;
res = build_replace_placeholder(f, d, e, l, p, input, quote);
fprintf(stdout, "in: %s\nout: %s\n\n", input, res);
g_free(res);
}
return 0;
}
foo bar %f
hello "%f"
some "$(cat "%f")-stuff"
some "`cat "%f"`-stuff"
some '$(cat "%f")-stuff'
some '`cat "%f"`-stuff'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment