- if((n = table_find(expansions,
- offsetof(struct cgi_expansion, name),
- sizeof (struct cgi_expansion),
- nexpansions,
- v.vec[0])) < 0)
- fatal(0, "%s:%d: unknown expansion '%s'", name, line, v.vec[0]);
- if(v.nvec - 1 < expansions[n].minargs)
- fatal(0, "%s:%d: insufficient arguments to @%s@ (min %d, got %d)",
- name, line, v.vec[0], expansions[n].minargs, v.nvec - 1);
- if(v.nvec - 1 > expansions[n].maxargs)
- fatal(0, "%s:%d: too many arguments to @%s@ (max %d, got %d)",
- name, line, v.vec[0], expansions[n].maxargs, v.nvec - 1);
- /* for ordinary expansions, recursively expand the arguments */
- if(!(expansions[n].flags & EXP_MAGIC)) {
- for(m = 1; m < v.nvec; ++m) {
- dynstr_init(&d);
- byte_xasprintf(&argname, "<%s:%d arg #%d>", name, sline, m);
- parameter_output.quote = 0;
- parameter_output.sink = sink_dynstr(&d);
- cgi_expand_string(argname, v.vec[m],
- expansions, nexpansions,
- ¶meter_output, u);
- dynstr_terminate(&d);
- v.vec[m] = d.vec;
+ e = xmalloc(sizeof *e);
+ e->next = 0;
+ e->line = sline;
+ e->type = ELEMENT_EXPANSION;
+ e->name = v.vec[0];
+ e->nargs = v.nvec - 1;
+ e->args = &v.vec[1];
+ *tailp = e;
+ tailp = &e->next;
+ }
+ return head;
+}
+
+void cgi_expand_string(const char *name,
+ const char *template,
+ const struct cgi_expansion *expansions,
+ size_t nexpansions,
+ cgi_sink *output,
+ void *u) {
+ cgi_expand_parsed(name, cgi_parse_string(name, template),
+ expansions, nexpansions, output, u);
+}
+
+/** @brief Expand a list of arguments in place */
+static void cgi_expand_all_args(const char *name,
+ int line,
+ const struct cgi_expansion *expansions,
+ size_t nexpansions,
+ void *u,
+ char **args,
+ int nargs) {
+ int n;
+ struct dynstr d;
+ char *argname;
+ cgi_sink parameter_output;
+
+ for(n = 0; n < nargs; ++n) {
+ dynstr_init(&d);
+ byte_xasprintf(&argname, "<%s:%d arg #%d>", name, line,n);
+ parameter_output.quote = 0;
+ parameter_output.sink = sink_dynstr(&d);
+ cgi_expand_string(argname, args[n],
+ expansions, nexpansions,
+ ¶meter_output, u);
+ dynstr_terminate(&d);
+ args[n] = d.vec;
+ }
+}
+
+
+/** @brief Substitute macro arguments in place */
+static void cgi_substitute_args(const char *name,
+ struct cgi_element *head,
+ const struct cgi_macro *macro,
+ char **values) {
+ struct cgi_element *e;
+ int n;
+
+ for(e = head; e; e = e->next) {
+ if(e->type != ELEMENT_EXPANSION)
+ continue;
+ /* See if this is an argument name */
+ for(n = 0; n < macro->nargs; ++n)
+ if(!strcmp(e->name, macro->args[n]))
+ break;
+ if(n < macro->nargs) {
+ /* It is! */
+ if(e->nargs != 0)
+ fatal(0, "%s:%d: macro argument (%s) cannot take parameters",
+ name, e->line, e->name);
+ /* Replace it with the argument text */
+ e->type = ELEMENT_TEXT;
+ e->text = values[n];
+ continue;
+ }
+ /* It's not a macro argument. We must recurse into its arguments to
+ * substitute the macro arguments. */
+ /* TODO */
+ /* In order to do this we must parse it and our callers must expect the
+ * parsed form. We're not ready for this yet... */
+ }
+}
+
+static void cgi_expand_parsed(const char *name,
+ struct cgi_element *head,
+ const struct cgi_expansion *expansions,
+ size_t nexpansions,
+ cgi_sink *output,
+ void *u) {
+ int n;
+ const struct cgi_macro *macro;
+ struct cgi_element *e, *macro_head;
+
+ for(e = head; e; e = e->next) {
+ switch(e->type) {
+ case ELEMENT_TEXT:
+ output->sink->write(output->sink, e->text, strlen(e->text));
+ break;
+ case ELEMENT_EXPANSION:
+ if((n = table_find(expansions,
+ offsetof(struct cgi_expansion, name),
+ sizeof (struct cgi_expansion),
+ nexpansions,
+ e->name)) >= 0) {
+ /* We found a built-in */
+ if(e->nargs < expansions[n].minargs)
+ fatal(0, "%s:%d: insufficient arguments to @%s@ (min %d, got %d)",
+ name, e->line, e->name, expansions[n].minargs, e->nargs);
+ if(e->nargs > expansions[n].maxargs)
+ fatal(0, "%s:%d: too many arguments to @%s@ (max %d, got %d)",
+ name, e->line, e->name, expansions[n].maxargs, e->nargs);
+ /* for ordinary expansions, recursively expand the arguments */
+ if(!(expansions[n].flags & EXP_MAGIC))
+ cgi_expand_all_args(name, e->line, expansions, nexpansions, u,
+ e->args, e->nargs);
+ expansions[n].handler(e->nargs, e->args, output, u);
+ } else if(cgi_macros && (macro = hash_find(cgi_macros, e->name))) {
+ /* We found a macro */
+ if(e->nargs != macro->nargs)
+ fatal(0, "%s:%d: wrong number of arguments to @%s@ (need %d, got %d)",
+ name, e->line, e->name, macro->nargs, e->nargs);
+ /* Expand arguments */
+ cgi_expand_all_args(name, e->line, expansions, nexpansions, u,
+ e->args, e->nargs);
+ /* Parse the macro value. Doing this every time isn't very efficient,
+ * but NB that we mess with the result of the parse, so for the time
+ * being we do need to do it. */
+ macro_head = cgi_parse_string(e->name, macro->value);
+ /* Substitute in argument values */
+ cgi_substitute_args(name, macro_head, macro, e->args);
+ /* Expand the result */
+ cgi_expand_parsed(e->name,
+ macro_head,
+ expansions,
+ nexpansions,
+ output,
+ u);
+ } else {
+ /* Totally undefined */
+ fatal(0, "%s:%d: unknown expansion '%s'", name, e->line, e->name);