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