/* Note: It is kind of stupid to first call strbuf_free_to_string, * then later free (above this function). But with the current API * of strbuf_free there's no other way! */ char * expand_substitution(const char *repl, MatchState *ms, uint32_t subc, SubmatchSpec *subv) { StrBuf *buf = strbuf_new(); /* XXX: memory management */ bool escaped = false; uint32_t c; for (c = 0; repl[c] != '\0'; c++) { if (!escaped && repl[c] == '$') { uint32_t d; if (repl[c+1] == '{') { for (d = c+2; repl[d] != '}' && repl[d] != '\0'; d++); if (repl[d] != '\0') { if (expand_variable(buf, repl+c+2, d-c-2, ms, subc, subv)) { c = d; continue; } } } else if (isdigit(repl[c+1])) { for (d = c+2; isdigit(repl[d]); d++); if (expand_variable(buf, repl+c+1, d-c-1, ms, subc, subv)) { c = d-1; continue; } } else if (repl[c+1] != '\0' && strchr("`'&", repl[c+1]) != NULL) { expand_variable(buf, repl+c+1, 1, ms, subc, subv); c++; continue; } } escaped = (!escaped && repl[c] == '\\'); if (!escaped) strbuf_append_char(buf, repl[c]); } return strbuf_free_to_string(buf); }
/* * recurse == 1: don't expand '@(command)@' * recurse == 2: don't expand '@<java script>@' */ gchar* expand(const char* s, guint recurse) { enum exp_type etype; char* end_simple_var = "\t^°!\"§$%&/()=?'`'+~*'#-:,;@<>| \\{}[]¹²³¼½"; char* ret = NULL; char* vend = NULL; GError* err = NULL; gchar* cmd_stdout = NULL; gchar* mycmd = NULL; GString* buf = g_string_new(""); GString* js_ret = g_string_new(""); while (s && *s) { switch(*s) { case '\\': g_string_append_c(buf, *++s); s++; break; case '@': etype = get_exp_type(s); s++; switch(etype) { case EXP_SIMPLE_VAR: vend = strpbrk(s, end_simple_var); if(!vend) vend = strchr(s, '\0'); break; case EXP_BRACED_VAR: s++; vend = strchr(s, '}'); if(!vend) vend = strchr(s, '\0'); break; case EXP_EXPR: s++; vend = strstr(s, ")@"); if(!vend) vend = strchr(s, '\0'); break; case EXP_JS: s++; vend = strstr(s, ">@"); if(!vend) vend = strchr(s, '\0'); break; case EXP_ESCAPE: s++; vend = strstr(s, "]@"); if(!vend) vend = strchr(s, '\0'); break; /*@notreached@*/ case EXP_ERR: break; } assert(vend); ret = g_strndup(s, vend-s); if(etype == EXP_SIMPLE_VAR || etype == EXP_BRACED_VAR) { expand_variable(buf, ret); if(etype == EXP_SIMPLE_VAR) s = vend; else s = vend+1; } else if(recurse != 1 && etype == EXP_EXPR) { /* execute program directly */ if(ret[0] == '+') { mycmd = expand(ret+1, 1); g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err); g_free(mycmd); } /* execute program through shell, quote it first */ else { mycmd = expand(ret, 1); gchar *quoted = g_shell_quote(mycmd); gchar *tmp = g_strdup_printf("%s %s", uzbl.behave.shell_cmd?uzbl.behave.shell_cmd:"/bin/sh -c", quoted); g_spawn_command_line_sync(tmp, &cmd_stdout, NULL, NULL, &err); g_free(mycmd); g_free(quoted); g_free(tmp); } if (err) { g_printerr("error on running command: %s\n", err->message); g_error_free (err); } else if (*cmd_stdout) { size_t len = strlen(cmd_stdout); if(len > 0 && cmd_stdout[len-1] == '\n') cmd_stdout[--len] = '\0'; /* strip trailing newline */ g_string_append(buf, cmd_stdout); g_free(cmd_stdout); } s = vend+2; } else if(recurse != 2 && etype == EXP_JS) { /* read JS from file */ if(ret[0] == '+') { GArray *tmp = g_array_new(TRUE, FALSE, sizeof(gchar *)); mycmd = expand(ret+1, 2); g_array_append_val(tmp, mycmd); run_external_js(uzbl.gui.web_view, tmp, js_ret); g_array_free(tmp, TRUE); } /* JS from string */ else { mycmd = expand(ret, 2); eval_js(uzbl.gui.web_view, mycmd, js_ret, "(command)"); g_free(mycmd); } if(js_ret->str) { g_string_append(buf, js_ret->str); g_string_free(js_ret, TRUE); js_ret = g_string_new(""); } s = vend+2; } else if(etype == EXP_ESCAPE) { mycmd = expand(ret, 0); char *escaped = g_markup_escape_text(mycmd, strlen(mycmd)); g_string_append(buf, escaped); g_free(escaped); g_free(mycmd); s = vend+2; } g_free(ret); ret = NULL; break; default: g_string_append_c(buf, *s); s++; break; } } g_string_free(js_ret, TRUE); return g_string_free(buf, FALSE); }
static bool recursive_print(std::ostream& out, const NodePtr root, const RuleTable& rules, BindingsPtr bindings, std::size_t indent, Context& context) { if (root->is_leaf()) { return !!(out << root->get_token().get_literal()); } else { Arity arity(root->size()); Operator op = root->get_op(); BindingsPtr local_bindings; RuleTable::print_iterator it, end; int found = 0; for (it = rules.reversed_find(op, arity, end); it != end; ++it) { ++found; local_bindings = std::make_shared<Bindings>(bindings); if (matches(root, it->second->get_tree_expression(), local_bindings, context)) break; } if (it == end) { // try wildcard rules for (it = rules.reversed_find(op, Arity(), end); it != end; ++it) { ++found; local_bindings = std::make_shared<Bindings>(bindings); if (matches(root, it->second->get_tree_expression(), local_bindings, context)) break; } if (it == end) { std::ostringstream os; if (found > 0) { os << "no matching "; } else { os << "no "; } os << "rule found for '" << op.get_name() << "' with " << root->size() << " parameters"; throw Exception(root->get_location(), os.str()); } } context.descend(root); const NodePtr& node = it->second->get_rhs(); std::size_t add_indent = 0; for (std::size_t pi = 0; pi < node->size(); ++pi) { const NodePtr& subnode = node->get_operand(pi); if (subnode->is_leaf()) { Token t = subnode->get_token(); switch (t.get_tokenval()) { case parser::token::TEXT_LITERAL: { expand_text(out, t, indent); int new_indent = get_indent(t.get_text()); if (new_indent >= 0) add_indent = new_indent; break; } case parser::token::VARIABLE: if (!expand_variable(out, t.get_text(), rules, indent + add_indent, bindings, local_bindings, context)) { std::ostringstream os; os << "undefined variable in replacement text: " << t.get_text(); throw Exception(subnode->get_location(), os.str()); } break; default: assert(false); std::abort(); } } else if (subnode->get_op() == Op::print_expression_listvar) { std::string varname = subnode->get_operand(0)->get_token().get_text(); if (!local_bindings->defined(varname)) { std::ostringstream os; os << "undefined variable in replacement list: " << varname; throw Exception(subnode->get_location(), os.str()); } AttributePtr list = local_bindings->get(varname); if (list->get_type() != Attribute::list) { std::ostringstream os; os << "list expected: " << varname; throw Exception(subnode->get_location(), os.str()); } if (list->size() > 0) { recursive_print(out, list->get_value(0)->get_node(), rules, bindings, indent + add_indent, context); } for (std::size_t i = 1; i < list->size(); ++i) { if (subnode->size() == 2) { Token t = subnode->get_operand(1)->get_token(); expand_text(out, t, indent); int new_indent = get_indent(t.get_text()); if (new_indent >= 0) add_indent = new_indent; } recursive_print(out, list->get_value(i)->get_node(), rules, bindings, indent + add_indent, context); } } else { assert(subnode->get_op() == Op::expression); Expression expr(subnode, local_bindings); if (!recursive_print(out, expr.convert_to_node(), rules, bindings, indent, context)) { return false; } } } } context.ascend(); return true; }
/* This routine will expand the pathname to account for ~ and $ * characters as described above. Returns a pointer to a newly * malloc'd string. If an error occurs, an error message is printed * via error() and NULL is returned. FILE and LINE are the filename * and linenumber to include in the error message. FILE must point * to something; LINE can be zero to indicate the line number is not * known. * * When FORMATSAFE is set, percent signs (`%') in variable contents are doubled * to prevent later expansion by format_cmdline. * * CVSROOT is used to expanding $CVSROOT. */ char * expand_path (const char *name, const char *cvsroot, bool formatsafe, const char *file, int line) { size_t s, d, p; const char *e; char *mybuf = NULL; size_t mybuf_size = 0; char *buf = NULL; size_t buf_size = 0; char inquotes = '\0'; char *result; /* Sorry this routine is so ugly; it is a head-on collision between the `traditional' unix *d++ style and the need to dynamically allocate. It would be much cleaner (and probably faster, not that this is a bottleneck for CVS) with more use of strcpy & friends, but I haven't taken the effort to rewrite it thusly. */ /* First copy from NAME to MYBUF, expanding $<foo> as we go. */ s = d = 0; expand_string (&mybuf, &mybuf_size, d + 1); while ((mybuf[d++] = name[s]) != '\0') { if (name[s] == '\\') { /* The next character is a literal. Leave the \ in the string * since it will be needed again when the string is split into * arguments. */ /* if we have a \ as the last character of the string, just leave * it there - this is where we would set the escape flag to tell * our parent we want another line if we cared. */ if (name[++s]) { expand_string (&mybuf, &mybuf_size, d + 1); mybuf[d++] = name[s++]; } } /* skip $ variable processing for text inside single quotes */ else if (inquotes == '\'') { if (name[s++] == '\'') { inquotes = '\0'; } } else if (name[s] == '\'') { s++; inquotes = '\''; } else if (name[s] == '"') { s++; if (inquotes) inquotes = '\0'; else inquotes = '"'; } else if (name[s++] == '$') { int flag = (name[s] == '{'); p = d; expand_string (&mybuf, &mybuf_size, d + 1); for (; (mybuf[d++] = name[s]); s++) { if (flag ? name[s] =='}' : !isalnum (name[s]) && name[s] != '_') break; expand_string (&mybuf, &mybuf_size, d + 1); } mybuf[--d] = '\0'; e = expand_variable (&mybuf[p+flag], cvsroot, file, line); if (e) { expand_string (&mybuf, &mybuf_size, d + 1); for (d = p - 1; (mybuf[d++] = *e++); ) { expand_string (&mybuf, &mybuf_size, d + 1); if (mybuf[d-1] == '"') { /* escape the double quotes if we're between a matched * pair of double quotes so that this sub will be * passed inside as or as part of a single argument * during the argument split later. */ if (inquotes) { mybuf[d-1] = '\\'; expand_string (&mybuf, &mybuf_size, d + 1); mybuf[d++] = '"'; } } else if (formatsafe && mybuf[d-1] == '%') { /* escape '%' to get past printf style format strings * later (in make_cmdline). */ expand_string (&mybuf, &mybuf_size, d + 1); mybuf[d] = '%'; d++; } } --d; if (flag && name[s]) s++; } else /* expand_variable has already printed an error message. */ goto error_exit; } expand_string (&mybuf, &mybuf_size, d + 1); } expand_string (&mybuf, &mybuf_size, d + 1); mybuf[d] = '\0'; /* Then copy from MYBUF to BUF, expanding ~. */ s = d = 0; /* If you don't want ~username ~/ to be expanded simply remove * This entire if statement including the else portion */ if (mybuf[s] == '~') { p = d; while (mybuf[++s] != '/' && mybuf[s] != '\0') { expand_string (&buf, &buf_size, p + 1); buf[p++] = name[s]; } expand_string (&buf, &buf_size, p + 1); buf[p] = '\0'; if (p == d) e = get_homedir (); else { #ifdef GETPWNAM_MISSING if (line) error (0, 0, "%s:%d:tilde expansion not supported on this system", file, line); else error (0, 0, "%s:tilde expansion not supported on this system", file); goto error_exit; #else struct passwd *ps; ps = getpwnam (buf + d); if (ps == NULL) { if (line) error (0, 0, "%s:%d: no such user %s", file, line, buf + d); else error (0, 0, "%s: no such user %s", file, buf + d); goto error_exit; } e = ps->pw_dir; #endif } if (!e) error (1, 0, "cannot find home directory"); p = strlen (e); expand_string (&buf, &buf_size, d + p); memcpy (buf + d, e, p); d += p; } /* Kill up to here */ p = strlen (mybuf + s) + 1; expand_string (&buf, &buf_size, d + p); memcpy (buf + d, mybuf + s, p); /* OK, buf contains the value we want to return. Clean up and return it. */ free (mybuf); /* Save a little memory with xstrdup; buf will tend to allocate more than it needs to. */ result = xstrdup (buf); free (buf); return result; error_exit: if (mybuf) free (mybuf); if (buf) free (buf); return NULL; }