@@@ 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 -------------------------------------------------*/