chiark / gitweb /
@@@ work in progress
[runlisp] / lib.c
1 /* -*-c-*-
2  *
3  * Common definitions for `runlisp'
4  *
5  * (c) 2020 Mark Wooding
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Runlisp, a tool for invoking Common Lisp scripts.
11  *
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.
16  *
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
20  * for more details.
21  *
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/>.
24  */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "config.h"
29
30 #include <assert.h>
31
32 #include <ctype.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include <unistd.h>
40
41 #include "lib.h"
42
43 /*----- Miscellany --------------------------------------------------------*/
44
45 int str_lt(const char *a, size_t an, const char *b, size_t bn)
46 {
47   if (an < bn) return (MEMCMP(a, <=, b, an));
48   else return (MEMCMP(a, <, b, bn));
49 }
50
51 /*----- Diagnostic utilities ----------------------------------------------*/
52
53 const char *progname = "???";
54
55 void set_progname(const char *prog)
56 {
57   const char *p;
58
59   p = strrchr(prog, '/');
60   progname = p ? p + 1 : progname;
61 }
62
63 void vmoan(const char *msg, va_list ap)
64 {
65   fprintf(stderr, "%s: ", progname);
66   vfprintf(stderr, msg, ap);
67   fputc('\n', stderr);
68 }
69
70 void moan(const char *msg, ...)
71   { va_list ap; va_start(ap, msg); vmoan(msg, ap); va_end(ap); }
72
73 void lose(const char *msg, ...)
74   { va_list ap; va_start(ap, msg); vmoan(msg, ap); va_end(ap); exit(127); }
75
76 /*----- Memory allocation -------------------------------------------------*/
77
78 void *xmalloc(size_t n)
79 {
80   void *p;
81
82   if (!n) return (0);
83   p = malloc(n); if (!p) lose("failed to allocate memory");
84   return (p);
85 }
86
87 void *xrealloc(void *p, size_t n)
88 {
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");
92   return (p);
93 }
94
95 char *xstrndup(const char *p, size_t n)
96 {
97   char *q = xmalloc(n + 1);
98
99   memcpy(q, p, n); q[n] = 0;
100   return (q);
101 }
102
103 char *xstrdup(const char *p) { return (xstrndup(p, strlen(p))); }
104
105 /*----- Dynamic strings ---------------------------------------------------*/
106
107 void dstr_init(struct dstr *d) { d->p = 0; d->len = d->sz = 0; }
108
109 void dstr_reset(struct dstr *d) { d->len = 0; }
110
111 void dstr_ensure(struct dstr *d, size_t n)
112 {
113   size_t need = d->len + n, newsz;
114
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;
119 }
120
121 void dstr_release(struct dstr *d) { free(d->p); }
122
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; }
125
126 void dstr_puts(struct dstr *d, const char *p)
127 {
128   size_t n = strlen(p);
129
130   dstr_ensure(d, n + 1);
131   memcpy(d->p + d->len, p, n + 1);
132   d->len += n;
133 }
134
135 void dstr_putc(struct dstr *d, int ch)
136   { dstr_ensure(d, 1); d->p[d->len++] = ch; }
137
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; }
140
141 void dstr_putz(struct dstr *d)
142   { dstr_ensure(d, 1); d->p[d->len] = 0; }
143
144 void dstr_vputf(struct dstr *d, const char *p, va_list ap)
145 {
146   va_list ap2;
147   size_t r;
148   int n;
149
150   r = d->sz - d->len;
151   va_copy(ap2, ap);
152   n = vsnprintf(d->p + d->len, r, p, ap2); assert(n >= 0);
153   va_end(ap2);
154   if (n >= r) {
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);
157   }
158   d->len += n;
159 }
160
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); }
163
164 int dstr_readline(struct dstr *d, FILE *fp)
165 {
166   size_t n;
167   int any = 0;
168
169   for (;;) {
170     dstr_ensure(d, 2);
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;
173     d->len += n;
174     if (d->p[d->len - 1] == '\n') { d->p[--d->len] = 0; break; }
175   }
176
177   if (!any) return (-1);
178   else return (0);
179 }
180
181 /*----- Dynamic vectors of strings ----------------------------------------*/
182
183 void argv_init(struct argv *av)
184   { av->v = 0; av->o = av->n = av->sz = 0; }
185
186 void argv_reset(struct argv *av) { av->n = 0; }
187
188 void argv_ensure(struct argv *av, size_t n)
189 {
190   size_t need = av->n + av->o + n, newsz;
191
192   if (need <= av->sz) return;
193   newsz = av->sz ? 2*av->sz : 8;
194   while (newsz < need) newsz *= 2;
195   av->v =
196     (const char **)xrealloc(av->v - av->o, newsz*sizeof(const char *)) +
197     av->o;
198   av->sz = newsz;
199 }
200
201 void argv_ensure_offset(struct argv *av, size_t n)
202 {
203   size_t newoff;
204
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.
208    */
209   if (av->o >= n) return;
210   newoff = 16;
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;
215 }
216
217 void argv_release(struct argv *av) { free(av->v - av->o); }
218
219 void argv_append(struct argv *av, const char *p)
220   { argv_ensure(av, 1); av->v[av->n++] = p; }
221
222 void argv_appendz(struct argv *av)
223   { argv_ensure(av, 1); av->v[av->n] = 0; }
224
225 void argv_appendn(struct argv *av, const char *const *v, size_t n)
226 {
227   argv_ensure(av, n);
228   memcpy(av->v + av->n, v, n*sizeof(const char *));
229   av->n += n;
230 }
231
232 void argv_appendav(struct argv *av, const struct argv *bv)
233   { argv_appendn(av, bv->v, bv->n); }
234
235 void argv_appendv(struct argv *av, va_list ap)
236 {
237   const char *p;
238
239   for (;;)
240     { p = va_arg(ap, const char *); if (!p) break; argv_append(av, p); }
241 }
242
243 void argv_appendl(struct argv *av, ...)
244   { va_list ap; va_start(ap, av); argv_appendv(av, ap); va_end(ap); }
245
246 void argv_prepend(struct argv *av, const char *p)
247   { argv_ensure_offset(av, 1); *--av->v = p; av->o--; av->n++; }
248
249 void argv_prependn(struct argv *av, const char *const *v, size_t n)
250 {
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 *));
254 }
255
256 void argv_prependav(struct argv *av, const struct argv *bv)
257   { argv_prependn(av, bv->v, bv->n); }
258
259 void argv_prependv(struct argv *av, va_list ap)
260 {
261   const char *p, **v;
262   size_t n = 0;
263
264   for (;;) {
265     p = va_arg(ap, const char *); if (!p) break;
266     argv_prepend(av, p); n++;
267   }
268   v = av->v;
269   while (n >= 2) {
270     p = v[0]; v[0] = v[n - 1]; v[n - 1] = p;
271     v++; n -= 2;
272   }
273 }
274
275 void argv_prependl(struct argv *av, ...)
276   { va_list ap; va_start(ap, av); argv_prependv(av, ap); va_end(ap); }
277
278 /*----- Treaps ------------------------------------------------------------*/
279
280 void treap_init(struct treap *t) { t->root = 0; }
281
282 void *treap_lookup(const struct treap *t, const char *k, size_t kn)
283 {
284   struct treap_node *n = t->root, *candidate = 0;
285
286   while (n) {
287     if (str_lt(k, kn, n->k, n->kn)) n = n->left;
288     else { candidate = n; n = n->right; }
289   }
290   if (!candidate || str_lt(candidate->k, candidate->kn, k, kn)) return (0);
291   return (candidate);
292 }
293
294 void *treap_probe(struct treap *t, const char *k, size_t kn,
295                   struct treap_path *p)
296 {
297   struct treap_node **nn = &t->root, *candidate = 0;
298   unsigned i = 0;
299
300   for (;;) {
301     assert(i < TREAP_PATHMAX); p->path[i++] = nn;
302     if (!*nn) break;
303     if (str_lt(k, kn, (*nn)->k, (*nn)->kn)) nn = &(*nn)->left;
304     else { candidate = *nn; nn = &(*nn)->right; }
305   }
306   p->nsteps = i;
307   if (!candidate || str_lt(candidate->k, candidate->kn, k, kn)) return (0);
308   return (candidate);
309 }
310
311 void treap_insert(struct treap *t, const struct treap_path *p,
312                   struct treap_node *n, const char *k, size_t kn)
313 {
314   size_t i = p->nsteps;
315   struct treap_node **nn, **uu, *u;
316   unsigned wt;
317
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];
321   while (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; }
326     nn = uu;
327   }
328   *nn = n;
329 }
330
331 void *treap_remove(struct treap *t, const char *k, size_t kn)
332 {
333   struct treap_node **nn = &t->root, **candidate = 0, *n, *l, *r;
334
335   while (*nn) {
336     if (str_lt(k, kn, (*nn)->k, (*nn)->kn)) nn = &(*nn)->left;
337     else { candidate = nn; nn = &(*nn)->right; }
338   }
339   if (!candidate || str_lt((*candidate)->k, (*candidate)->kn, k, kn))
340     return (0);
341
342   n = *candidate; l = n->left; r = n->right;
343   for (;;) {
344     if (l && (!r || l->wt > r->wt)) { nn = &l->right; l = l->right; }
345     else if (r) { nn = &r->left; r = r->left; }
346     else break;
347   }
348   *nn = 0;
349   free(n->k);
350   return (n);
351 }
352
353 void treap_start_iter(struct treap *t, struct treap_iter *i)
354 {
355   struct treap_node *n = t->root;
356   unsigned sp = 0;
357
358   while (n) {
359     assert(sp < TREAP_PATHMAX);
360     i->stack[sp++] = n; n = n->left;
361   }
362   i->sp = sp;
363 }
364
365 void *treap_next(struct treap_iter *i)
366 {
367   struct treap_node *n, *o;
368   unsigned sp = i->sp;
369
370   if (!sp) return (0);
371   n = i->stack[--sp];
372   o = n->right;
373   while (o) {
374     assert(sp < TREAP_PATHMAX);
375     i->stack[sp++] = o; o = o->left;
376   }
377   i->sp = sp;
378   return (n);
379 }
380
381 static void check_node(struct treap_node *n, unsigned maxwt,
382                        const char *klo, const char *khi)
383 {
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);
389 }
390
391 void treap_check(struct treap *t)
392   { if (t->root) check_node(t->root, t->root->wt, 0, 0); }
393
394 static void dump_node(struct treap_node *n, int ind)
395 {
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);
399 }
400
401 void treap_dump(struct treap *t) { if (t->root) dump_node(t->root, 0); }
402
403 /*----- Configuration file parsing ----------------------------------------*/
404
405 #ifndef DECL_ENVIRON
406   extern char **environ;
407 #endif
408
409 void config_init(struct config *conf)
410   { treap_init(&conf->sections); }
411
412 struct config_section *config_find_section(struct config *conf, unsigned f,
413                                            const char *name)
414   { return (config_find_section_n(conf, f, name, strlen(name))); }
415
416 struct config_section *config_find_section_n(struct config *conf, unsigned f,
417                                              const char *name, size_t sz)
418 {
419   struct config_section *sect;
420   struct treap_path path;
421
422   if (!(f&CF_CREAT))
423     sect = treap_lookup(&conf->sections, name, sz);
424   else {
425     sect = treap_probe(&conf->sections, name, sz, &path);
426     if (!sect) {
427       sect = xmalloc(sizeof(*sect));
428       if (!conf->head) conf->tail = &conf->head;
429       sect->next = 0; *conf->tail = sect; conf->tail = &sect->next;
430       sect->parents = 0; sect->nparents = SIZE_MAX;
431       treap_init(&sect->vars); treap_init(&sect->cache);
432       treap_insert(&conf->sections, &path, &sect->_node, name, sz);
433       config_set_var_n(conf, sect, CF_LITERAL, "@NAME", 5, name, sz);
434     }
435   }
436   return (sect);
437 }
438
439 void config_set_fallback(struct config *conf, struct config_section *sect)
440 {
441   if (sect->nparents == SIZE_MAX) sect->nparents = 0;
442   conf->fallback = sect;
443 }
444
445 void config_set_parent(struct config_section *sect,
446                        struct config_section *parent)
447 {
448   if (!parent)
449     sect->nparents = 0;
450   else {
451     sect->parents = xmalloc(sizeof(*sect->parents));
452     sect->parents[0] = parent; sect->nparents = 1;
453   }
454 }
455
456 void config_start_section_iter(struct config *conf,
457                                struct config_section_iter *i)
458   { i->sect = conf->head; }
459
460 struct config_section *config_next_section(struct config_section_iter *i)
461 {
462   struct config_section *sect;
463
464   sect = i->sect;
465   if (sect) i->sect = sect->next;
466   return (sect);
467 }
468
469 static void set_config_section_parents(struct config *conf,
470                                        struct config_section *sect)
471 {
472   struct config_section *parent;
473   struct config_var *var;
474   struct argv av = ARGV_INIT;
475   size_t i, n;
476   char *p, *q;
477
478   if (sect->nparents != SIZE_MAX) return;
479
480   var = treap_lookup(&sect->vars, "@PARENTS", 8);
481   if (!var) {
482     if (!conf->fallback)
483       sect->nparents = 0;
484     else {
485       sect->parents = xmalloc(sizeof(*sect->parents));
486       sect->nparents = 1;
487       sect->parents[0] = conf->fallback;
488     }
489   } else {
490     p = var->val;
491     for (;;) {
492       while (ISSPACE(*p)) p++;
493       if (!*p) break;
494       q = p; while (*q && *q != ',' && !ISSPACE(*q)) q++;
495       argv_append(&av, p); argv_append(&av, q);
496       p = q; if (*p == ',') p++;
497     }
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);
503       if (!parent)
504         lose("%s:%u: unknown parent section `%.*s'",
505              var->file, var->line, (int)n, av.v[i]);
506       sect->parents[i/2] = parent;
507     }
508     argv_release(&av);
509   }
510 }
511
512 struct config_var *search_recursive(struct config *conf,
513                                     struct config_section *sect,
514                                     const char *name, size_t sz)
515 {
516   struct config_cache_entry *cache;
517   struct treap_path path;
518   struct config_var *var, *v;
519   size_t i, j = j;
520
521   cache = treap_probe(&sect->cache, name, sz, &path);
522   if (!cache) {
523     cache = xmalloc(sizeof(*cache)); cache->f = CF_OPEN;
524     treap_insert(&sect->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));
528   else
529     return (cache->var);
530
531   set_config_section_parents(conf, sect);
532
533   var = treap_lookup(&sect->vars, name, sz);
534   if (!var) {
535     for (i = 0; i < sect->nparents; i++) {
536       v = search_recursive(conf, sect->parents[i], name, sz);
537       if (!v);
538       else if (!var) { var = v; j = i; }
539       else if (var != v)
540         lose("section `%s' inherits variable `%s' ambiguously "
541              "via `%s' and `%s'",
542              CONFIG_SECTION_NAME(sect), CONFIG_VAR_NAME(var),
543              CONFIG_SECTION_NAME(sect->parents[j]),
544              CONFIG_SECTION_NAME(sect->parents[i]));
545     }
546   }
547
548   cache->var = var; cache->f &= ~CF_OPEN;
549   return (var);
550 }
551
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))); }
556
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)
560 {
561   struct config_var *var;
562   struct treap_path path;
563
564   if (f&CF_INHERIT)
565     var = search_recursive(conf, sect, name, sz);
566   else if (!(f&CF_CREAT))
567     var = treap_lookup(&sect->vars, name, sz);
568   else {
569     var = treap_probe(&sect->vars, name, sz, &path);
570     if (!var) {
571       var = xmalloc(sizeof(*var));
572       var->val = 0; var->file = 0; var->f = 0; var->line = 1;
573       treap_insert(&sect->vars, &path, &var->_node, name, sz);
574     }
575   }
576   return (var);
577 }
578
579 void config_start_var_iter(struct config_section *sect,
580                            struct config_var_iter *i)
581   { treap_start_iter(&sect->vars, &i->i); }
582
583 struct config_var *config_next_var(struct config_var_iter *i)
584   { return (treap_next(&i->i)); }
585
586 void config_set_var(struct config *conf, struct config_section *sect,
587                     unsigned f,
588                     const char *name, const char *value)
589 {
590   config_set_var_n(conf, sect, f,
591                    name, strlen(name),
592                    value, strlen(value));
593 }
594
595 void config_set_var_n(struct config *conf, struct config_section *sect,
596                       unsigned f,
597                       const char *name, size_t namelen,
598                       const char *value, size_t valuelen)
599 {
600   struct config_var *var =
601     config_find_var_n(conf, sect, CF_CREAT, name, namelen);
602
603   if (var->f&~f&CF_OVERRIDE) return;
604   free(var->val); var->val = xstrndup(value, valuelen); var->n = valuelen;
605   var->f = f;
606 }
607
608 int config_read_file(struct config *conf, const char *file, unsigned f)
609 {
610   struct config_section *sect;
611   struct config_var *var;
612   struct dstr d = DSTR_INIT, dd = DSTR_INIT;
613   unsigned line = 0;
614   char *p, *q;
615   FILE *fp;
616
617   fp = fopen(file, "r");
618   if (!fp) {
619     if ((f&CF_NOENTOK) && errno == ENOENT) return (-1);
620     lose("failed to open configuration file `%s': %s",
621          file, strerror(errno));
622   }
623
624   sect = config_find_section(conf, CF_CREAT, "@CONFIG"); var = 0;
625
626   for (;;) {
627     dstr_reset(&d); if (dstr_readline(&d, fp)) break;
628     line++;
629
630     if (d.p[0] && !ISSPACE(d.p[0])) {
631       if (var) {
632         if (!(var->f&CF_OVERRIDE))
633           { var->val = xstrndup(dd.p, dd.len); var->n = dd.len; }
634         var = 0;
635       }
636       if (d.p[0] == ';')
637         ;
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",
644                      file, line);
645       } else {
646         p = d.p;
647         while (*p && !ISSPACE(*p) && *p != '{' && *p != '}' && *p != '=')
648           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;
656         }
657         dstr_reset(&dd); dstr_puts(&dd, p);
658       }
659     } else {
660       p = d.p; while (ISSPACE(*p)) p++;
661       if (*p) {
662         if (!var)
663           lose("%s:%u: continuation line, but no variable", file, line);
664         if (dd.len) dstr_putc(&dd, ' ');
665         dstr_puts(&dd, p);
666       }
667     }
668   }
669
670   if (var && !(var->f&CF_OVERRIDE))
671     { var->val = xstrndup(dd.p, dd.len); var->n = dd.len; }
672
673   dstr_release(&d); dstr_release(&dd);
674   if (fclose(fp))
675     lose("error reading configuration file `%s': %s", file, strerror(errno));
676   return (0);
677 }
678
679 void config_read_env(struct config *conf, struct config_section *sect)
680 {
681   const char *p, *v;
682   size_t i;
683
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));
687   }
688 }
689
690 /*----- Substitution and quoting ------------------------------------------*/
691
692 struct subst {
693   struct config *config;
694   struct config_section *home, *fallback;
695   struct argv *av;
696   struct dstr *d;
697 };
698
699 static const char *scan_name(const char *p, const char *l)
700 {
701   while (p < l &&
702          (ISALNUM(*p) || *p == '-' || *p == '_' || *p == '.' || *p == '/' ||
703                          *p == '*' || *p == '+' || *p == '%' || *p == '@'))
704     p++;
705   return (p);
706 }
707
708 static void filter_string(const char *p, const char *l, struct subst *sb,
709                           unsigned qfilt)
710 {
711   size_t r, n;
712
713   if (!qfilt)
714     dstr_putm(sb->d, p, l - p);
715   else for (;;) {
716     r = l - p; n = strcspn(p, "\"\\");
717     if (n > r) n = r;
718     dstr_putm(sb->d, p, n);
719     if (n >= r) break;
720     dstr_putcn(sb->d, '\\', qfilt); dstr_putc(sb->d, p[n]);
721     p += n + 1;
722   }
723 }
724
725 static const char *retrieve_varspec(const char *p, const char *l,
726                                     struct subst *sb,
727                                     struct config_var **var_out)
728 {
729   struct config_section *sect = sb->home;
730   const char *t;
731
732   t = scan_name(p, l);
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);
736   }
737
738   if (!sect) *var_out = 0;
739   else *var_out = config_find_var_n(sb->config, sect, CF_INHERIT, p, t - p);
740   return (t);
741 }
742
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
751
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)
755 {
756   struct config_var *var;
757   const char *q0, *q1, *t;
758   unsigned subqfilt, ff;
759   size_t n;
760
761 #define ESCAPE "\\"
762 #define SUBST "$"
763 #define WORDSEP " \f\r\n\t\v'\""
764 #define QUOT "\""
765 #define DELIM "|}"
766
767   static const char *const delimtab[] =
768     { ESCAPE,
769       ESCAPE             WORDSEP,
770       0,
771       ESCAPE             QUOT,
772       ESCAPE       SUBST,
773       ESCAPE       SUBST WORDSEP,
774       0,
775       ESCAPE       SUBST QUOT,
776       ESCAPE DELIM,
777       ESCAPE DELIM       WORDSEP,
778       0,
779       ESCAPE DELIM       QUOT,
780       ESCAPE DELIM SUBST,
781       ESCAPE DELIM SUBST WORDSEP,
782       0,
783       ESCAPE DELIM SUBST QUOT };
784
785 #undef COMMON
786 #undef WORDSEP
787 #undef SQUOT
788 #undef DELIM
789
790   if (!file) file = "<internal>";
791
792   if (f&SF_LITERAL) {
793     filter_string(p, l, sb, qfilt);
794     f |= SF_WORD;
795     goto done;
796   }
797
798   while (p < l) {
799
800     if ((f&(SF_SPLIT | SF_QUOT)) == SF_SPLIT && ISSPACE(*p)) {
801       if (f&SF_WORD) {
802         if (!(f&SF_SKIP)) {
803           argv_append(sb->av, xstrndup(sb->d->p, sb->d->len));
804           dstr_reset(sb->d);
805         }
806         f &= ~SF_WORD;
807       }
808       do p++; while (p < l && ISSPACE(*p));
809
810     } else if (*p == '\\') {
811       p++;
812       if (p >= l) lose("%s:%u: unfinished `\\' escape", file, line);
813       if (!(f&SF_SKIP)) {
814         if (qfilt && (*p == '"' || *p == '\\'))
815           dstr_putcn(sb->d, '\\', qfilt);
816         dstr_putc(sb->d, *p);
817       }
818       p++;
819
820     } else if ((f&SF_SPLIT) && *p == '"') {
821       f ^= SF_QUOT; f |= SF_WORD; p++;
822
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;
827
828     } else if ((f&SF_SUBEXPR) && (*p == '|' || *p == '}')) {
829       break;
830
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));
834       switch (*p) {
835
836         case '?':
837           p = retrieve_varspec(p + 1, l, sb, &var);
838           if (p > l || *p != '{') lose("%s:%u: expected `{'", file, line);
839           p++;
840           ff |= SF_SUBEXPR;
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);
847           p++;
848           break;
849
850         case '{':
851           q0 = p + 1; p = retrieve_varspec(q0, l, sb, &var); q1 = p;
852           subqfilt = qfilt;
853           while (p < l) {
854             if (*p != '|') break;
855             p++; t = scan_name(p, l);
856             if (t - p == 1 && *p == 'q') subqfilt = 2*subqfilt + 1;
857             else
858               lose("%s:%u: unknown filter `%.*s'",
859                    file, line, (int)(t - p), p);
860             p = t;
861           }
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);
866             var->f |= CF_EXPAND;
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;
871           }
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);
879           p++;
880           break;
881
882         default:
883           lose("%s:%u: unexpected substitution `%c'", file, line, *p);
884       }
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",
889              file, line);
890     }
891
892     else {
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;
897     }
898   }
899
900 done:
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));
904     dstr_reset(sb->d);
905   }
906
907   return (p);
908 }
909
910 void config_subst_string(struct config *config, struct config_section *home,
911                          const char *what, const char *p, struct dstr *d)
912 {
913   struct subst sb;
914
915   sb.config = config; sb.home = home; sb.d = d;
916   subst(p, p + strlen(p), &sb, what, 0, 0, SF_SUBST);
917   dstr_putz(d);
918 }
919
920 char *config_subst_string_alloc(struct config *config,
921                                 struct config_section *home,
922                                 const char *what, const char *p)
923 {
924   struct dstr d = DSTR_INIT;
925   char *q;
926
927   config_subst_string(config, home, what, p, &d);
928   q = xstrndup(d.p, d.len); dstr_release(&d); return (q);
929 }
930
931 void config_subst_var(struct config *config, struct config_section *home,
932                       struct config_var *var, struct dstr *d)
933 {
934   struct subst sb;
935
936   sb.config = config; sb.home = home; sb.d = d;
937   var->f |= CF_EXPAND;
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;
941   dstr_putz(d);
942 }
943
944 char *config_subst_var_alloc(struct config *config,
945                              struct config_section *home,
946                              struct config_var *var)
947 {
948   struct dstr d = DSTR_INIT;
949   char *q;
950
951   config_subst_var(config, home, var, &d);
952   q = xstrndup(d.p, d.len); dstr_release(&d); return (q);
953 }
954
955 void config_subst_split_var(struct config *config,
956                             struct config_section *home,
957                             struct config_var *var, struct argv *av)
958 {
959   struct dstr d = DSTR_INIT;
960   struct subst sb;
961
962   sb.config = config; sb.home = home; sb.av = av; sb.d = &d;
963   var->f |= CF_EXPAND;
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;
967   dstr_release(&d);
968 }
969
970 /*----- That's all, folks -------------------------------------------------*/