3 * Common definitions for `runlisp'
5 * (c) 2020 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Runlisp, a tool for invoking Common Lisp scripts.
12 * Runlisp is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * Runlisp is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with Runlisp. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
43 /*----- Miscellany --------------------------------------------------------*/
45 int str_lt(const char *a, size_t an, const char *b, size_t bn)
47 if (an < bn) return (MEMCMP(a, <=, b, an));
48 else return (MEMCMP(a, <, b, bn));
51 /*----- Diagnostic utilities ----------------------------------------------*/
53 const char *progname = "???";
55 void set_progname(const char *prog)
59 p = strrchr(prog, '/');
60 progname = p ? p + 1 : progname;
63 void vmoan(const char *msg, va_list ap)
65 fprintf(stderr, "%s: ", progname);
66 vfprintf(stderr, msg, ap);
70 void moan(const char *msg, ...)
71 { va_list ap; va_start(ap, msg); vmoan(msg, ap); va_end(ap); }
73 void lose(const char *msg, ...)
74 { va_list ap; va_start(ap, msg); vmoan(msg, ap); va_end(ap); exit(127); }
76 /*----- Memory allocation -------------------------------------------------*/
78 void *xmalloc(size_t n)
83 p = malloc(n); if (!p) lose("failed to allocate memory");
87 void *xrealloc(void *p, size_t n)
89 if (!n) { free(p); return (0); }
90 else if (!p) return (xmalloc(n));
91 p = realloc(p, n); if (!p) lose("failed to allocate memory");
95 char *xstrndup(const char *p, size_t n)
97 char *q = xmalloc(n + 1);
99 memcpy(q, p, n); q[n] = 0;
103 char *xstrdup(const char *p) { return (xstrndup(p, strlen(p))); }
105 /*----- Dynamic strings ---------------------------------------------------*/
107 void dstr_init(struct dstr *d) { d->p = 0; d->len = d->sz = 0; }
109 void dstr_reset(struct dstr *d) { d->len = 0; }
111 void dstr_ensure(struct dstr *d, size_t n)
113 size_t need = d->len + n, newsz;
115 if (need <= d->sz) return;
116 newsz = d->sz ? 2*d->sz : 16;
117 while (newsz < need) newsz *= 2;
118 d->p = xrealloc(d->p, newsz); d->sz = newsz;
121 void dstr_release(struct dstr *d) { free(d->p); }
123 void dstr_putm(struct dstr *d, const void *p, size_t n)
124 { dstr_ensure(d, n); memcpy(d->p + d->len, p, n); d->len += n; }
126 void dstr_puts(struct dstr *d, const char *p)
128 size_t n = strlen(p);
130 dstr_ensure(d, n + 1);
131 memcpy(d->p + d->len, p, n + 1);
135 void dstr_putc(struct dstr *d, int ch)
136 { dstr_ensure(d, 1); d->p[d->len++] = ch; }
138 void dstr_putcn(struct dstr *d, int ch, size_t n)
139 { dstr_ensure(d, n); memset(d->p + d->len, ch, n); d->len += n; }
141 void dstr_putz(struct dstr *d)
142 { dstr_ensure(d, 1); d->p[d->len] = 0; }
144 void dstr_vputf(struct dstr *d, const char *p, va_list ap)
152 n = vsnprintf(d->p + d->len, r, p, ap2); assert(n >= 0);
155 dstr_ensure(d, n + 1); r = d->sz - d->len;
156 n = vsnprintf(d->p + d->len, r, p, ap); assert(n >= 0); assert(n < r);
161 PRINTF_LIKE(2, 3) void dstr_putf(struct dstr *d, const char *p, ...)
162 { va_list ap; va_start(ap, p); dstr_vputf(d, p, ap); va_end(ap); }
164 int dstr_readline(struct dstr *d, FILE *fp)
171 if (!fgets(d->p + d->len, d->sz - d->len, fp)) break;
172 n = strlen(d->p + d->len); assert(n > 0); any = 1;
174 if (d->p[d->len - 1] == '\n') { d->p[--d->len] = 0; break; }
177 if (!any) return (-1);
181 /*----- Dynamic vectors of strings ----------------------------------------*/
183 void argv_init(struct argv *av)
184 { av->v = 0; av->o = av->n = av->sz = 0; }
186 void argv_reset(struct argv *av) { av->n = 0; }
188 void argv_ensure(struct argv *av, size_t n)
190 size_t need = av->n + av->o + n, newsz;
192 if (need <= av->sz) return;
193 newsz = av->sz ? 2*av->sz : 8;
194 while (newsz < need) newsz *= 2;
196 (const char **)xrealloc(av->v - av->o, newsz*sizeof(const char *)) +
201 void argv_ensure_offset(struct argv *av, size_t n)
205 /* Stupid version. We won't, in practice, be prepending lots of stuff, so
206 * avoid the extra bookkeeping involved in trying to make a double-ended
207 * extendable array asymptotically efficient.
209 if (av->o >= n) return;
211 while (newoff < n) newoff *= 2;
212 argv_ensure(av, newoff - av->o);
213 memmove(av->v + newoff - av->o, av->v, av->n*sizeof(const char *));
214 av->v += newoff - av->o; av->o = newoff;
217 void argv_release(struct argv *av) { free(av->v - av->o); }
219 void argv_append(struct argv *av, const char *p)
220 { argv_ensure(av, 1); av->v[av->n++] = p; }
222 void argv_appendz(struct argv *av)
223 { argv_ensure(av, 1); av->v[av->n] = 0; }
225 void argv_appendn(struct argv *av, const char *const *v, size_t n)
228 memcpy(av->v + av->n, v, n*sizeof(const char *));
232 void argv_appendav(struct argv *av, const struct argv *bv)
233 { argv_appendn(av, bv->v, bv->n); }
235 void argv_appendv(struct argv *av, va_list ap)
240 { p = va_arg(ap, const char *); if (!p) break; argv_append(av, p); }
243 void argv_appendl(struct argv *av, ...)
244 { va_list ap; va_start(ap, av); argv_appendv(av, ap); va_end(ap); }
246 void argv_prepend(struct argv *av, const char *p)
247 { argv_ensure_offset(av, 1); *--av->v = p; av->o--; av->n++; }
249 void argv_prependn(struct argv *av, const char *const *v, size_t n)
251 argv_ensure_offset(av, n);
252 av->o -= n; av->v -= n; av->n += n;
253 memcpy(av->v, v, n*sizeof(const char *));
256 void argv_prependav(struct argv *av, const struct argv *bv)
257 { argv_prependn(av, bv->v, bv->n); }
259 void argv_prependv(struct argv *av, va_list ap)
265 p = va_arg(ap, const char *); if (!p) break;
266 argv_prepend(av, p); n++;
270 p = v[0]; v[0] = v[n - 1]; v[n - 1] = p;
275 void argv_prependl(struct argv *av, ...)
276 { va_list ap; va_start(ap, av); argv_prependv(av, ap); va_end(ap); }
278 /*----- Treaps ------------------------------------------------------------*/
280 void treap_init(struct treap *t) { t->root = 0; }
282 void *treap_lookup(const struct treap *t, const char *k, size_t kn)
284 struct treap_node *n = t->root, *candidate = 0;
287 if (str_lt(k, kn, n->k, n->kn)) n = n->left;
288 else { candidate = n; n = n->right; }
290 if (!candidate || str_lt(candidate->k, candidate->kn, k, kn)) return (0);
294 void *treap_probe(struct treap *t, const char *k, size_t kn,
295 struct treap_path *p)
297 struct treap_node **nn = &t->root, *candidate = 0;
301 assert(i < TREAP_PATHMAX); p->path[i++] = nn;
303 if (str_lt(k, kn, (*nn)->k, (*nn)->kn)) nn = &(*nn)->left;
304 else { candidate = *nn; nn = &(*nn)->right; }
307 if (!candidate || str_lt(candidate->k, candidate->kn, k, kn)) return (0);
311 void treap_insert(struct treap *t, const struct treap_path *p,
312 struct treap_node *n, const char *k, size_t kn)
314 size_t i = p->nsteps;
315 struct treap_node **nn, **uu, *u;
318 n->k = xstrndup(k, kn); n->kn = kn;
319 n->wt = wt = rand(); n->left = n->right = 0;
320 assert(i); nn = p->path[--i];
322 uu = p->path[i]; u = *uu;
323 if (wt <= u->wt) break;
324 if (nn == &u->left) { u->left = n->right; n->right = u; }
325 else { u->right = n->left; n->left = u; }
331 void *treap_remove(struct treap *t, const char *k, size_t kn)
333 struct treap_node **nn = &t->root, **candidate = 0, *n, *l, *r;
336 if (str_lt(k, kn, (*nn)->k, (*nn)->kn)) nn = &(*nn)->left;
337 else { candidate = nn; nn = &(*nn)->right; }
339 if (!candidate || str_lt((*candidate)->k, (*candidate)->kn, k, kn))
342 n = *candidate; l = n->left; r = n->right;
344 if (l && (!r || l->wt > r->wt)) { nn = &l->right; l = l->right; }
345 else if (r) { nn = &r->left; r = r->left; }
353 void treap_start_iter(struct treap *t, struct treap_iter *i)
355 struct treap_node *n = t->root;
359 assert(sp < TREAP_PATHMAX);
360 i->stack[sp++] = n; n = n->left;
365 void *treap_next(struct treap_iter *i)
367 struct treap_node *n, *o;
374 assert(sp < TREAP_PATHMAX);
375 i->stack[sp++] = o; o = o->left;
381 static void check_node(struct treap_node *n, unsigned maxwt,
382 const char *klo, const char *khi)
384 assert(n->wt <= maxwt);
385 if (klo) assert(STRCMP(n->k, >, klo));
386 if (khi) assert(STRCMP(n->k, <, khi));
387 if (n->left) check_node(n->left, n->wt, klo, n->k);
388 if (n->right) check_node(n->right, n->wt, n->k, khi);
391 void treap_check(struct treap *t)
392 { if (t->root) check_node(t->root, t->root->wt, 0, 0); }
394 static void dump_node(struct treap_node *n, int ind)
396 if (n->left) dump_node(n->left, ind + 1);
397 printf(";;%*s [%10u] `%s'\n", 2*ind, "", n->wt, n->k);
398 if (n->right) dump_node(n->right, ind + 1);
401 void treap_dump(struct treap *t) { if (t->root) dump_node(t->root, 0); }
403 /*----- Configuration file parsing ----------------------------------------*/
406 extern char **environ;
409 void config_init(struct config *conf)
410 { treap_init(&conf->sections); }
412 struct config_section *config_find_section(struct config *conf, unsigned f,
414 { return (config_find_section_n(conf, f, name, strlen(name))); }
416 struct config_section *config_find_section_n(struct config *conf, unsigned f,
417 const char *name, size_t sz)
419 struct config_section *sect;
420 struct treap_path path;
423 sect = treap_lookup(&conf->sections, name, sz);
425 sect = treap_probe(&conf->sections, name, sz, &path);
427 sect = xmalloc(sizeof(*sect));
428 if (!conf->head) conf->tail = &conf->head;
429 sect->next = 0; *conf->tail = sect; conf->tail = §->next;
430 sect->parents = 0; sect->nparents = SIZE_MAX;
431 treap_init(§->vars); treap_init(§->cache);
432 treap_insert(&conf->sections, &path, §->_node, name, sz);
433 config_set_var_n(conf, sect, CF_LITERAL, "@NAME", 5, name, sz);
439 void config_set_fallback(struct config *conf, struct config_section *sect)
441 if (sect->nparents == SIZE_MAX) sect->nparents = 0;
442 conf->fallback = sect;
445 void config_set_parent(struct config_section *sect,
446 struct config_section *parent)
451 sect->parents = xmalloc(sizeof(*sect->parents));
452 sect->parents[0] = parent; sect->nparents = 1;
456 void config_start_section_iter(struct config *conf,
457 struct config_section_iter *i)
458 { i->sect = conf->head; }
460 struct config_section *config_next_section(struct config_section_iter *i)
462 struct config_section *sect;
465 if (sect) i->sect = sect->next;
469 static void set_config_section_parents(struct config *conf,
470 struct config_section *sect)
472 struct config_section *parent;
473 struct config_var *var;
474 struct argv av = ARGV_INIT;
478 if (sect->nparents != SIZE_MAX) return;
480 var = treap_lookup(§->vars, "@PARENTS", 8);
485 sect->parents = xmalloc(sizeof(*sect->parents));
487 sect->parents[0] = conf->fallback;
492 while (ISSPACE(*p)) p++;
494 q = p; while (*q && *q != ',' && !ISSPACE(*q)) q++;
495 argv_append(&av, p); argv_append(&av, q);
496 p = q; if (*p == ',') p++;
498 sect->nparents = av.n/2;
499 sect->parents = xmalloc(sect->nparents*sizeof(sect->parents));
500 for (i = 0; i < av.n; i += 2) {
501 n = av.v[i + 1] - av.v[i];
502 parent = config_find_section_n(conf, 0, av.v[i], n);
504 lose("%s:%u: unknown parent section `%.*s'",
505 var->file, var->line, (int)n, av.v[i]);
506 sect->parents[i/2] = parent;
512 struct config_var *search_recursive(struct config *conf,
513 struct config_section *sect,
514 const char *name, size_t sz)
516 struct config_cache_entry *cache;
517 struct treap_path path;
518 struct config_var *var, *v;
521 cache = treap_probe(§->cache, name, sz, &path);
523 cache = xmalloc(sizeof(*cache)); cache->f = CF_OPEN;
524 treap_insert(§->cache, &path, &cache->_node, name, sz);
525 } else if (cache->f&CF_OPEN)
526 lose("inheritance cycle through section `%s'",
527 CONFIG_SECTION_NAME(sect));
531 set_config_section_parents(conf, sect);
533 var = treap_lookup(§->vars, name, sz);
535 for (i = 0; i < sect->nparents; i++) {
536 v = search_recursive(conf, sect->parents[i], name, sz);
538 else if (!var) { var = v; j = i; }
540 lose("section `%s' inherits variable `%s' ambiguously "
542 CONFIG_SECTION_NAME(sect), CONFIG_VAR_NAME(var),
543 CONFIG_SECTION_NAME(sect->parents[j]),
544 CONFIG_SECTION_NAME(sect->parents[i]));
548 cache->var = var; cache->f &= ~CF_OPEN;
552 struct config_var *config_find_var(struct config *conf,
553 struct config_section *sect,
554 unsigned f, const char *name)
555 { return (config_find_var_n(conf, sect, f, name, strlen(name))); }
557 struct config_var *config_find_var_n(struct config *conf,
558 struct config_section *sect,
559 unsigned f, const char *name, size_t sz)
561 struct config_var *var;
562 struct treap_path path;
565 var = search_recursive(conf, sect, name, sz);
566 else if (!(f&CF_CREAT))
567 var = treap_lookup(§->vars, name, sz);
569 var = treap_probe(§->vars, name, sz, &path);
571 var = xmalloc(sizeof(*var));
572 var->val = 0; var->file = 0; var->f = 0; var->line = 1;
573 treap_insert(§->vars, &path, &var->_node, name, sz);
579 void config_start_var_iter(struct config_section *sect,
580 struct config_var_iter *i)
581 { treap_start_iter(§->vars, &i->i); }
583 struct config_var *config_next_var(struct config_var_iter *i)
584 { return (treap_next(&i->i)); }
586 void config_set_var(struct config *conf, struct config_section *sect,
588 const char *name, const char *value)
590 config_set_var_n(conf, sect, f,
592 value, strlen(value));
595 void config_set_var_n(struct config *conf, struct config_section *sect,
597 const char *name, size_t namelen,
598 const char *value, size_t valuelen)
600 struct config_var *var =
601 config_find_var_n(conf, sect, CF_CREAT, name, namelen);
603 if (var->f&~f&CF_OVERRIDE) return;
604 free(var->val); var->val = xstrndup(value, valuelen); var->n = valuelen;
608 int config_read_file(struct config *conf, const char *file, unsigned f)
610 struct config_section *sect;
611 struct config_var *var;
612 struct dstr d = DSTR_INIT, dd = DSTR_INIT;
617 fp = fopen(file, "r");
619 if ((f&CF_NOENTOK) && errno == ENOENT) return (-1);
620 lose("failed to open configuration file `%s': %s",
621 file, strerror(errno));
624 sect = config_find_section(conf, CF_CREAT, "@CONFIG"); var = 0;
627 dstr_reset(&d); if (dstr_readline(&d, fp)) break;
630 if (d.p[0] && !ISSPACE(d.p[0])) {
632 if (!(var->f&CF_OVERRIDE))
633 { var->val = xstrndup(dd.p, dd.len); var->n = dd.len; }
638 else if (d.p[0] == '[') {
639 p = d.p + 1; q = strchr(p, ']');
640 if (!q) lose("%s:%u: missing `]' in section header", file, line);
641 sect = config_find_section_n(conf, CF_CREAT, p, q - p);
642 q++; while (ISSPACE(*q)) q++;
643 if (*q) lose("%s:%u: trailing junk after `]' in section header",
647 while (*p && !ISSPACE(*p) && *p != '{' && *p != '}' && *p != '=')
649 var = config_find_var_n(conf, sect, CF_CREAT, d.p, p - d.p);
650 while (ISSPACE(*p)) p++;
651 if (*p != '=') lose("%s:%u: missing `=' in assignment", file, line);
652 p++; while (ISSPACE(*p)) p++;
653 if (!(var->f&CF_OVERRIDE)) {
654 free(var->val); var->val = 0; var->f = 0;
655 free(var->file); var->file = xstrdup(file); var->line = line;
657 dstr_reset(&dd); dstr_puts(&dd, p);
660 p = d.p; while (ISSPACE(*p)) p++;
663 lose("%s:%u: continuation line, but no variable", file, line);
664 if (dd.len) dstr_putc(&dd, ' ');
670 if (var && !(var->f&CF_OVERRIDE))
671 { var->val = xstrndup(dd.p, dd.len); var->n = dd.len; }
673 dstr_release(&d); dstr_release(&dd);
675 lose("error reading configuration file `%s': %s", file, strerror(errno));
679 void config_read_env(struct config *conf, struct config_section *sect)
684 for (i = 0; (p = environ[i]) != 0; i++) {
685 v = strchr(p, '='); if (!v) continue;
686 config_set_var_n(conf, sect, CF_LITERAL, p, v - p, v + 1, strlen(v + 1));
690 /*----- Substitution and quoting ------------------------------------------*/
693 struct config *config;
694 struct config_section *home, *fallback;
699 static const char *scan_name(const char *p, const char *l)
702 (ISALNUM(*p) || *p == '-' || *p == '_' || *p == '.' || *p == '/' ||
703 *p == '*' || *p == '+' || *p == '%' || *p == '@'))
708 static void filter_string(const char *p, const char *l, struct subst *sb,
714 dstr_putm(sb->d, p, l - p);
716 r = l - p; n = strcspn(p, "\"\\");
718 dstr_putm(sb->d, p, n);
720 dstr_putcn(sb->d, '\\', qfilt); dstr_putc(sb->d, p[n]);
725 static const char *retrieve_varspec(const char *p, const char *l,
727 struct config_var **var_out)
729 struct config_section *sect = sb->home;
733 if (t < l && *t == ':') {
734 sect = config_find_section_n(sb->config, 0, p, t - p);
735 p = t + 1; t = scan_name(p, l);
738 if (!sect) *var_out = 0;
739 else *var_out = config_find_var_n(sb->config, sect, CF_INHERIT, p, t - p);
743 #define SF_SPLIT 0x0001u
744 #define SF_QUOT 0x0002u
745 #define SF_SUBST 0x0004u
746 #define SF_SUBEXPR 0x0008u
747 #define SF_SPANMASK 0x00ffu
748 #define SF_WORD 0x0100u
749 #define SF_SKIP 0x0200u
750 #define SF_LITERAL 0x0400u
752 static const char *subst(const char *p, const char *l, struct subst *sb,
753 const char *file, unsigned line,
754 unsigned qfilt, unsigned f)
756 struct config_var *var;
757 const char *q0, *q1, *t;
758 unsigned subqfilt, ff;
763 #define WORDSEP " \f\r\n\t\v'\""
767 static const char *const delimtab[] =
773 ESCAPE SUBST WORDSEP,
777 ESCAPE DELIM WORDSEP,
781 ESCAPE DELIM SUBST WORDSEP,
783 ESCAPE DELIM SUBST QUOT };
790 if (!file) file = "<internal>";
793 filter_string(p, l, sb, qfilt);
800 if ((f&(SF_SPLIT | SF_QUOT)) == SF_SPLIT && ISSPACE(*p)) {
803 argv_append(sb->av, xstrndup(sb->d->p, sb->d->len));
808 do p++; while (p < l && ISSPACE(*p));
810 } else if (*p == '\\') {
812 if (p >= l) lose("%s:%u: unfinished `\\' escape", file, line);
814 if (qfilt && (*p == '"' || *p == '\\'))
815 dstr_putcn(sb->d, '\\', qfilt);
816 dstr_putc(sb->d, *p);
820 } else if ((f&SF_SPLIT) && *p == '"') {
821 f ^= SF_QUOT; f |= SF_WORD; p++;
823 } else if ((f&(SF_SPLIT | SF_QUOT)) == SF_SPLIT && *p == '\'') {
824 t = strchr(p, '\''); if (!t) lose("%s:%u: missing `''", file, line);
825 if (!(f&SF_SKIP)) filter_string(p, t, sb, qfilt);
826 p = t + 1; f |= SF_WORD;
828 } else if ((f&SF_SUBEXPR) && (*p == '|' || *p == '}')) {
831 } else if ((f&SF_SUBST) && *p == '$') {
832 p++; if (p >= l) lose("%s:%u: incomplete substitution", file, line);
833 ff = f&~(SF_QUOT | (f&SF_WORD ? SF_SPLIT : 0));
837 p = retrieve_varspec(p + 1, l, sb, &var);
838 if (p > l || *p != '{') lose("%s:%u: expected `{'", file, line);
841 p = subst(p, l, sb, file, line, qfilt,
842 ff | (var ? 0 : SF_SKIP));
843 if (p < l && *p == '|')
844 p = subst(p + 1, l, sb, file, line, qfilt,
845 ff | (var ? SF_SKIP : 0));
846 if (p >= l || *p != '}') lose("%s:%u: missing `}'", file, line);
851 q0 = p + 1; p = retrieve_varspec(q0, l, sb, &var); q1 = p;
854 if (*p != '|') break;
855 p++; t = scan_name(p, l);
856 if (t - p == 1 && *p == 'q') subqfilt = 2*subqfilt + 1;
858 lose("%s:%u: unknown filter `%.*s'",
859 file, line, (int)(t - p), p);
862 if (!(f&SF_SKIP) && var) {
863 if (var->f&CF_EXPAND)
864 lose("%s:%u: recursive expansion of variable `%.*s'",
865 file, line, (int)(q1 - q0), q0);
867 subst(var->val, var->val + var->n, sb,
868 var->file, var->line, subqfilt,
869 ff | (var->f&CF_LITERAL ? SF_LITERAL : 0));
870 var->f &= ~CF_EXPAND;
872 if (p < l && *p == '?')
873 p = subst(p + 1, l, sb, file, line, subqfilt,
874 ff | SF_SUBEXPR | (var ? SF_SKIP : 0));
875 else if (!var && !(f&SF_SKIP))
876 lose("%s:%u: unknown variable `%.*s'",
877 file, line, (int)(q1 - q0), q0);
878 if (p >= l || *p != '}') lose("%s:%u: missing `}'", file, line);
883 lose("%s:%u: unexpected substitution `%c'", file, line, *p);
885 if (p < l && !(~f&~(SF_WORD | SF_SPLIT)) && !ISSPACE(*p) &&
886 !((f&SF_SUBEXPR) && (*p == '|' || *p == '}')))
887 lose("%s:%u: surprising word boundary "
888 "after splicing substitution",
893 n = strcspn(p, delimtab[f&SF_SPANMASK]);
894 if (n > l - p) n = l - p;
895 if (!(f&SF_SKIP)) filter_string(p, p + n, sb, qfilt);
896 p += n; f |= SF_WORD;
901 if (f&SF_QUOT) lose("%s:%u: missing `\"'", file, line);
902 if ((f&(SF_WORD | SF_SPLIT | SF_SKIP)) == (SF_SPLIT | SF_WORD)) {
903 argv_append(sb->av, xstrndup(sb->d->p, sb->d->len));
910 void config_subst_string(struct config *config, struct config_section *home,
911 const char *what, const char *p, struct dstr *d)
915 sb.config = config; sb.home = home; sb.d = d;
916 subst(p, p + strlen(p), &sb, what, 0, 0, SF_SUBST);
920 char *config_subst_string_alloc(struct config *config,
921 struct config_section *home,
922 const char *what, const char *p)
924 struct dstr d = DSTR_INIT;
927 config_subst_string(config, home, what, p, &d);
928 q = xstrndup(d.p, d.len); dstr_release(&d); return (q);
931 void config_subst_var(struct config *config, struct config_section *home,
932 struct config_var *var, struct dstr *d)
936 sb.config = config; sb.home = home; sb.d = d;
938 subst(var->val, var->val + var->n, &sb, var->file, var->line, 0,
939 SF_SUBST | (var->f&CF_LITERAL ? SF_LITERAL : 0));
940 var->f &= ~CF_EXPAND;
944 char *config_subst_var_alloc(struct config *config,
945 struct config_section *home,
946 struct config_var *var)
948 struct dstr d = DSTR_INIT;
951 config_subst_var(config, home, var, &d);
952 q = xstrndup(d.p, d.len); dstr_release(&d); return (q);
955 void config_subst_split_var(struct config *config,
956 struct config_section *home,
957 struct config_var *var, struct argv *av)
959 struct dstr d = DSTR_INIT;
962 sb.config = config; sb.home = home; sb.av = av; sb.d = &d;
964 subst(var->val, var->val + var->n, &sb, var->file, var->line, 0,
965 SF_SUBST | SF_SPLIT | (var->f&CF_LITERAL ? SF_LITERAL : 0));
966 var->f &= ~CF_EXPAND;
970 /*----- That's all, folks -------------------------------------------------*/