Commit | Line | Data |
---|---|---|
7b8ff279 MW |
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 | #ifndef LIB_H | |
27 | #define LIB_H | |
28 | ||
29 | #ifdef __cplusplus | |
30 | extern "C" { | |
31 | #endif | |
32 | ||
33 | /*----- Header files ------------------------------------------------------*/ | |
34 | ||
35 | #include <limits.h> | |
36 | #include <stdarg.h> | |
37 | #include <stddef.h> | |
38 | #include <stdio.h> | |
39 | ||
40 | /*----- Handy macros ------------------------------------------------------*/ | |
41 | ||
42 | #define N(v) (sizeof(v)/sizeof((v)[0])) | |
8996f767 | 43 | /* The number of elements in the array V. */ |
7b8ff279 | 44 | |
8996f767 | 45 | /* Figure out the compiler version to see whether fancy tricks will work. */ |
7b8ff279 MW |
46 | #if defined(__GNUC__) |
47 | # define GCC_VERSION_P(maj, min) \ | |
48 | (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min))) | |
49 | #else | |
50 | # define GCC_VERSION_P(maj, min) 0 | |
51 | #endif | |
52 | ||
53 | #ifdef __clang__ | |
54 | # define CLANG_VERSION_P(maj, min) \ | |
55 | (__clang_major__ > (maj) || (__clang_major__ == (maj) && \ | |
56 | __clang_minor__ >= (min))) | |
57 | #else | |
58 | # define CLANG_VERSION_P(maj, min) 0 | |
59 | #endif | |
60 | ||
61 | #if GCC_VERSION_P(2, 5) || CLANG_VERSION_P(3, 3) | |
8996f767 | 62 | |
7b8ff279 | 63 | # define NORETURN __attribute__((__noreturn__)) |
8996f767 MW |
64 | /* Mark a function as not returning. */ |
65 | ||
7b8ff279 | 66 | # define PRINTF_LIKE(fix, aix) __attribute__((__format__(printf, fix, aix))) |
8996f767 MW |
67 | /* Mark a function as accepting a printf(3)-like format string as |
68 | * argument FIX, with arguments to be substituted starting at AIX. | |
69 | */ | |
7b8ff279 MW |
70 | #endif |
71 | ||
72 | #if GCC_VERSION_P(4, 0) || CLANG_VERSION_P(3, 3) | |
8996f767 | 73 | |
7b8ff279 | 74 | # define EXECL_LIKE(ntrail) __attribute__((__sentinel__(ntrail))) |
8996f767 MW |
75 | /* Mark a function as expecting a variable number of arguments |
76 | * terminated by a null pointer, followed by NTRAIL further | |
77 | * arguments. | |
78 | */ | |
79 | ||
80 | #endif | |
81 | ||
82 | /* Couldn't detect fancy compiler features. We'll have to make do | |
83 | * without. | |
84 | */ | |
85 | #ifndef NORETURN | |
86 | # define NORETURN | |
87 | #endif | |
88 | #ifndef PRINTF_LIKE | |
89 | # define PRINTF_LIKE(fix, aix) | |
90 | #endif | |
91 | #ifndef EXECL_LIKE | |
92 | # define EXECL_LIKE(ntrail) | |
7b8ff279 MW |
93 | #endif |
94 | ||
8996f767 MW |
95 | #define DISCARD(x) do if (x); while (0) |
96 | /* Discard the result of evaluating expression X, without upsetting | |
97 | * the compiler. | |
98 | */ | |
99 | ||
100 | #define END ((const char *)0) | |
101 | /* A null pointer to terminate the argument tail to an `EXECL_LIKE' | |
102 | * function. (Note that `NULL' is /not/ adequate for this purpose, | |
103 | * since it might expand simply to `0', which is an integer, not a | |
104 | * pointer, and might well be the wrong size and/or value.) | |
105 | */ | |
106 | ||
107 | /* Wrap up <ctype.h> macros with explicit conversions to `unsigned char'. */ | |
7b8ff279 MW |
108 | #define CTYPE_HACK(func, ch) (func((unsigned char)(ch))) |
109 | #define ISSPACE(ch) CTYPE_HACK(isspace, ch) | |
110 | #define ISALNUM(ch) CTYPE_HACK(isalnum, ch) | |
6c39ec6d | 111 | #define ISXDIGIT(ch) CTYPE_HACK(isxdigit, ch) |
8996f767 MW |
112 | #define TOLOWER(ch) CTYPE_HACK(tolower, ch) |
113 | #define TOUPPER(ch) CTYPE_HACK(toupper, ch) | |
7b8ff279 | 114 | |
8996f767 MW |
115 | /* Wrap up comparison functions to take an ordering relation as part of their |
116 | * syntax. This makes it much harder to screw up. | |
117 | */ | |
7b8ff279 MW |
118 | #define MEMCMP(x, op, y, n) (memcmp((x), (y), (n)) op 0) |
119 | #define STRCMP(x, op, y) (strcmp((x), (y)) op 0) | |
120 | #define STRNCMP(x, op, y, n) (strncmp((x), (y), (n)) op 0) | |
121 | ||
7b8ff279 MW |
122 | #ifndef SIZE_MAX |
123 | # define SIZE_MAX (-(size_t)1) | |
124 | #endif | |
8996f767 MW |
125 | /* The largest value that can be stored in an object of type |
126 | * `size_t'. A proper <limits.h> setting would be a preprocessor- | |
127 | * time constant, but we don't actually need that. | |
128 | */ | |
7b8ff279 MW |
129 | |
130 | /*----- Diagnostic utilities ----------------------------------------------*/ | |
131 | ||
132 | extern const char *progname; | |
8996f767 | 133 | /* Our program name, for use in error messages. */ |
7b8ff279 MW |
134 | |
135 | extern void set_progname(const char */*prog*/); | |
8996f767 MW |
136 | /* Set `progname' from the pathname in PROG (typically from |
137 | * `argv[0]'). | |
138 | */ | |
139 | ||
7b8ff279 | 140 | extern void vmoan(const char */*msg*/, va_list /*ap*/); |
8996f767 MW |
141 | /* Report an error or warning in Unix style, given a captured |
142 | * argument cursor. | |
143 | */ | |
144 | ||
7b8ff279 | 145 | extern PRINTF_LIKE(1, 2) void moan(const char */*msg*/, ...); |
8996f767 MW |
146 | /* Issue a warning message. */ |
147 | ||
7b8ff279 | 148 | extern NORETURN PRINTF_LIKE(1, 2) void lose(const char */*msg*/, ...); |
8996f767 | 149 | /* Issue a fatal error message and exit unsuccessfully. */ |
7b8ff279 MW |
150 | |
151 | /*----- Memory allocation -------------------------------------------------*/ | |
152 | ||
153 | extern void *xmalloc(size_t /*n*/); | |
8996f767 MW |
154 | /* Allocate and return a pointer to N bytes, or report a fatal error. |
155 | * | |
156 | * Release the pointer using `free' as usual. If N is zero, returns | |
157 | * null (but you are not expected to check for this). | |
158 | */ | |
159 | ||
7b8ff279 | 160 | extern void *xrealloc(void */*p*/, size_t /*n*/); |
8996f767 MW |
161 | /* Resize the block at P (from `malloc' or `xmalloc') to be N bytes |
162 | * long. | |
163 | * | |
164 | * The block might (and probably will) move, so it returns the new | |
165 | * address. If N is zero, then the block is freed (if necessary) and | |
166 | * a null pointer returned; otherwise, if P is null then a fresh | |
167 | * block is allocated. If allocation fails, then a fatal error is | |
168 | * reported. | |
169 | */ | |
170 | ||
7b8ff279 | 171 | extern char *xstrndup(const char */*p*/, size_t /*n*/); |
8996f767 MW |
172 | /* Allocate and return a copy of the N-byte string starting at P. |
173 | * | |
174 | * The new string is null-terminated, though P need not be. If | |
175 | * allocation fails, then a fatal error is reported. | |
176 | */ | |
177 | ||
7b8ff279 | 178 | extern char *xstrdup(const char */*p*/); |
8996f767 MW |
179 | /* Allocate and return a copy of the null-terminated string starting |
180 | * at P. | |
181 | * | |
182 | * If allocation fails, then a fatal error is reported. | |
183 | */ | |
7b8ff279 MW |
184 | |
185 | /*----- Dynamic strings ---------------------------------------------------*/ | |
186 | ||
8996f767 MW |
187 | /* A dynamic string. |
188 | * | |
189 | * Note that the string might not be null-terminated. | |
190 | */ | |
7b8ff279 | 191 | struct dstr { |
8996f767 MW |
192 | char *p; /* string base address */ |
193 | size_t len; /* current string length */ | |
194 | size_t sz; /* allocated size of buffer */ | |
7b8ff279 MW |
195 | }; |
196 | #define DSTR_INIT { 0, 0, 0 } | |
197 | ||
198 | extern void dstr_init(struct dstr */*d*/); | |
8996f767 MW |
199 | /* Initialize the string D. |
200 | * | |
201 | * Usually you'd use the static initializer `DSTR_INIT'. | |
202 | */ | |
203 | ||
7b8ff279 | 204 | extern void dstr_reset(struct dstr */*d*/); |
8996f767 MW |
205 | /* Reset string D so it's empty again. */ |
206 | ||
7b8ff279 | 207 | extern void dstr_ensure(struct dstr */*d*/, size_t /*n*/); |
8996f767 MW |
208 | /* Ensure that D has at least N unused bytes available. */ |
209 | ||
7b8ff279 | 210 | extern void dstr_release(struct dstr */*d*/); |
8996f767 MW |
211 | /* Release the memory held by D. |
212 | * | |
213 | * It must be reinitialized (e.g., by `dstr_init') before it can be | |
214 | * used again. | |
215 | */ | |
216 | ||
7b8ff279 | 217 | extern void dstr_putm(struct dstr */*d*/, const void */*p*/, size_t /*n*/); |
8996f767 MW |
218 | /* Append the N-byte string at P to D. |
219 | * | |
220 | * P need not be null-terminated. D will not be null-terminated | |
221 | * afterwards. | |
222 | */ | |
223 | ||
7b8ff279 | 224 | extern void dstr_puts(struct dstr */*d*/, const char */*p*/); |
8996f767 MW |
225 | /* Append the null-terminated string P to D. |
226 | * | |
227 | * D /is/ guaranteed to be null-terminated after this. | |
228 | */ | |
229 | ||
7b8ff279 | 230 | extern void dstr_putc(struct dstr */*d*/, int /*ch*/); |
8996f767 MW |
231 | /* Append the single character CH to D. |
232 | * | |
233 | * D will not be null-terminated afterwards. | |
234 | */ | |
235 | ||
236 | extern void dstr_putcn(struct dstr */*d*/, int /*ch*/, size_t /*n*/); | |
237 | /* Append N copies of the character CH to D. | |
238 | * | |
239 | * D will not be null-terminated afterwards. | |
240 | */ | |
241 | ||
7b8ff279 | 242 | extern void dstr_putz(struct dstr */*d*/); |
8996f767 MW |
243 | /* Null-terminate the string D. |
244 | * | |
245 | * This doesn't change the length of D. If further stuff is appended | |
246 | * then the null terminator will be overwritten. | |
247 | */ | |
248 | ||
7b8ff279 MW |
249 | extern void dstr_vputf(struct dstr */*d*/, |
250 | const char */*p*/, va_list /*ap*/); | |
8996f767 MW |
251 | /* Append stuff to D, determined by printf(3) format string P and |
252 | * argument tail AP. | |
253 | * | |
254 | * D will not be null-terminated afterwards. | |
255 | */ | |
256 | ||
7b8ff279 MW |
257 | extern PRINTF_LIKE(2, 3) |
258 | void dstr_putf(struct dstr */*d*/, const char */*p*/, ...); | |
8996f767 MW |
259 | /* Append stuff to D, determined by printf(3) format string P and |
260 | * arguments. | |
261 | * | |
262 | * D will not be null-terminated afterwards. | |
263 | */ | |
264 | ||
7b8ff279 | 265 | extern int dstr_readline(struct dstr */*d*/, FILE */*fp*/); |
8996f767 MW |
266 | /* Append the next input line from FP to D. |
267 | * | |
268 | * Return 0 on success, or -1 if reading immediately fails or | |
269 | * encounters end-of-file (call ferror(3) to distinguish). Any | |
270 | * trailing newline is discarded: it is not possible to determine | |
271 | * whether the last line was ended with a newline. D is guaranteed | |
272 | * to be null-terminated afterwards. | |
273 | */ | |
7b8ff279 MW |
274 | |
275 | /*----- Dynamic vectors of strings ----------------------------------------*/ | |
276 | ||
8996f767 MW |
277 | /* A dynamic vector of strings. |
278 | * | |
279 | * This machinery only actually tracks character pointers. It assumes that | |
280 | * the caller will manage the underlying storage for the strings. | |
281 | * | |
282 | * Note that `v' always points to the first element in the vector. The | |
283 | * underlying storage starts `o' slots before this. | |
284 | */ | |
7b8ff279 | 285 | struct argv { |
8996f767 MW |
286 | char **v; /* pointer the first element */ |
287 | size_t n; /* length of the vector */ | |
288 | size_t o; /* number of spare slots at start */ | |
289 | size_t sz; /* allocated size (in slots) */ | |
7b8ff279 MW |
290 | }; |
291 | #define ARGV_INIT { 0, 0, 0, 0 } | |
292 | ||
293 | extern void argv_init(struct argv */*a*/v); | |
8996f767 MW |
294 | /* Initialize the vector AV. |
295 | * | |
296 | * Usually you'd use the static initializer `ARGV_INIT'. | |
297 | */ | |
298 | ||
7b8ff279 | 299 | extern void argv_reset(struct argv */*av*/); |
8996f767 MW |
300 | /* Reset the vector AV so that it's empty again. */ |
301 | ||
7b8ff279 | 302 | extern void argv_ensure(struct argv */*av*/, size_t /*n*/); |
8996f767 MW |
303 | /* Ensure that AV has at least N unused slots at the end. */ |
304 | ||
7b8ff279 | 305 | extern void argv_ensure_offset(struct argv */*av*/, size_t /*n*/); |
8996f767 MW |
306 | /* Ensure that AV has at least N unused slots at the /start/. */ |
307 | ||
7b8ff279 | 308 | extern void argv_release(struct argv */*av*/); |
8996f767 MW |
309 | /* Release the memory held by AV. |
310 | * | |
311 | * It must be reinitialized (e.g., by `argv_init') before it can be | |
312 | * used again. | |
313 | */ | |
314 | ||
315 | extern void argv_append(struct argv */*av*/, char */*p*/); | |
316 | /* Append the pointer P to AV. */ | |
317 | ||
7b8ff279 | 318 | extern void argv_appendz(struct argv */*av*/); |
8996f767 MW |
319 | /* Append a null pointer to AV, without extending the vactor length. |
320 | * | |
321 | * The null pointer will be overwritten when the next string is | |
322 | * appended. | |
323 | */ | |
324 | ||
7b8ff279 | 325 | extern void argv_appendn(struct argv */*av*/, |
8996f767 MW |
326 | char *const */*v*/, size_t /*n*/); |
327 | /* Append a N-element vector V of pointers to AV. */ | |
328 | ||
7b8ff279 | 329 | extern void argv_appendav(struct argv */*av*/, const struct argv */*bv*/); |
8996f767 MW |
330 | /* Append the variable-length vector BV to AV. */ |
331 | ||
7b8ff279 | 332 | extern void argv_appendv(struct argv */*av*/, va_list /*ap*/); |
8996f767 MW |
333 | /* Append the pointers from a variable-length argument list AP to AV. |
334 | * | |
335 | * The list is terminated by a null pointer. | |
336 | */ | |
337 | ||
7b8ff279 | 338 | extern EXECL_LIKE(0) void argv_appendl(struct argv */*av*/, ...); |
8996f767 MW |
339 | /* Append the argument pointers, terminated by a null pointer, to |
340 | * AV. | |
341 | */ | |
342 | ||
343 | extern void argv_prepend(struct argv */*av*/, char */*p*/); | |
344 | /* Prepend the pointer P to AV. */ | |
345 | ||
7b8ff279 | 346 | extern void argv_prependn(struct argv */*av*/, |
8996f767 MW |
347 | char *const */*v*/, size_t /*n*/); |
348 | /* Prepend a N-element vector V of pointers to AV. */ | |
349 | ||
7b8ff279 | 350 | extern void argv_prependav(struct argv */*av*/, const struct argv */*bv*/); |
8996f767 MW |
351 | /* Prepend the variable-length vector BV to AV. */ |
352 | ||
7b8ff279 | 353 | extern void argv_prependv(struct argv */*av*/, va_list /*ap*/); |
8996f767 MW |
354 | /* Prepend the pointers from a variable-length argument list AP to |
355 | * AV. | |
356 | * | |
357 | * The list is terminated by a null pointer. | |
358 | */ | |
359 | ||
7b8ff279 | 360 | extern EXECL_LIKE(0) void argv_prependl(struct argv */*av*/, ...); |
8996f767 MW |
361 | /* Prepend the argument pointers, terminated by a null pointer, to |
362 | * AV. | |
363 | */ | |
7b8ff279 MW |
364 | |
365 | /*----- Treaps ------------------------------------------------------------*/ | |
366 | ||
8996f767 MW |
367 | /* A `treap' is a data structure for associating values with keys. This |
368 | * implementation assumes that keys are simply text strings. | |
369 | */ | |
7b8ff279 MW |
370 | struct treap { |
371 | struct treap_node *root; | |
372 | }; | |
373 | #define TREAP_INIT { 0 } | |
374 | ||
8996f767 MW |
375 | /* A treap is a combination of a binary search tree and a binary heap. The |
376 | * nodes are ordered according to the search keys, in the usual way, so that | |
377 | * all the keys in a node's left subtree precede that node's key, and all of | |
378 | * the keys in its right subtree follow the node's key. The trick is that | |
379 | * the tree must /also/ satisfy the heap condition regarding randomly | |
380 | * assigned `weights' attached to each node: so a node's weight must not be | |
381 | * less than their weight of either of its children. | |
382 | * | |
383 | * This combination uniquely determines the structure of the tree, except for | |
384 | * nodes whose weights exactly match one (or both) of their children. (The | |
385 | * root must be the heaviest node in the tree. The root's key splits the | |
386 | * remaining nodes into left and right subtrees, whose structure is then | |
387 | * uniquely determined by induction.) | |
388 | * | |
389 | * This is an /intrusive/ data structure. A caller is expected to include a | |
390 | * `struct treap_node' as (probably) the initial part of a larger structure. | |
391 | */ | |
7b8ff279 | 392 | struct treap_node { |
8996f767 MW |
393 | unsigned wt; /* weight (randomly assigned) */ |
394 | struct treap_node *left, *right; /* left and right subtrees */ | |
395 | char *k; size_t kn; /* key pointer and length */ | |
7b8ff279 MW |
396 | }; |
397 | #define TREAP_NODE_KEY(n) (((const struct treap_node *)(n))->k + 0) | |
398 | #define TREAP_NODE_KEYLEN(n) (((const struct treap_node *)(n))->kn + 0) | |
399 | ||
8996f767 MW |
400 | /* We can't allocate nodes ourselves, because only the caller knows how. |
401 | * Instead, insertion is split into two operations: `treap_probe' looks to | |
402 | * see whether a matching node is already in the treap, and returns it if so; | |
403 | * otherwise, it flls in this `treap_path' structure, which is passed back to | |
404 | * `treap_insert' to help it add the fresh node into the treap. (See the | |
405 | * commentary in `treap_probe' and `treap_insert' for the details.) | |
406 | */ | |
7b8ff279 MW |
407 | #define TREAP_PATHMAX 64 |
408 | struct treap_path { | |
409 | struct treap_node **path[TREAP_PATHMAX]; | |
410 | unsigned nsteps; | |
411 | }; | |
412 | ||
8996f767 MW |
413 | /* An external iterator for a treap. (See the commentary for |
414 | * `treap_start_iter' and `treap_next' for the details.) | |
415 | */ | |
7b8ff279 MW |
416 | struct treap_iter { |
417 | struct treap_node *stack[TREAP_PATHMAX]; | |
418 | unsigned sp; | |
419 | }; | |
420 | ||
421 | extern void treap_init(struct treap */*t*/); | |
8996f767 MW |
422 | /* Initialize the treap T. |
423 | * | |
424 | * Usually you'd use the static initializer `TREAP_INIT'. | |
425 | */ | |
426 | ||
7b8ff279 MW |
427 | extern void *treap_lookup(const struct treap */*t*/, |
428 | const char */*k*/, size_t /*kn*/); | |
8996f767 MW |
429 | /* Look up the KN-byte key K in the treap T. |
430 | * | |
431 | * Return a pointer to the matching node if one was found, or null | |
432 | * otherwise. | |
433 | */ | |
434 | ||
7b8ff279 MW |
435 | extern void *treap_probe(struct treap */*t*/, |
436 | const char */*k*/, size_t /*kn*/, | |
437 | struct treap_path */*p*/); | |
8996f767 MW |
438 | /* Look up the KN-byte K in the treap T, recording a path in P. |
439 | * | |
440 | * This is similar to `treap_lookup', in that it returns the | |
441 | * requested node if it already exists, or null otherwise, but it | |
442 | * also records in P information to be used by `treap_insert' to | |
10427eb2 | 443 | * insert a new node with the given key if it's not there already. |
8996f767 MW |
444 | */ |
445 | ||
7b8ff279 MW |
446 | extern void treap_insert(struct treap */*t*/, const struct treap_path */*p*/, |
447 | struct treap_node */*n*/, | |
448 | const char */*k*/, size_t /*kn*/); | |
8996f767 MW |
449 | /* Insert a new node N into T, associating it with the KN-byte key K. |
450 | * | |
451 | * Use the path data P, from `treap_probe', to help with insertion. | |
452 | */ | |
453 | ||
7b8ff279 MW |
454 | extern void *treap_remove(struct treap */*t*/, |
455 | const char */*k*/, size_t /*kn*/); | |
8996f767 MW |
456 | /* Remove the node with the KN-byte K from T. |
457 | * | |
458 | * Return the address of the node we removed, or null if it couldn't | |
459 | * be found. | |
460 | */ | |
461 | ||
7b8ff279 | 462 | extern void treap_start_iter(struct treap */*t*/, struct treap_iter */*i*/); |
8996f767 MW |
463 | /* Initialize an iterator I over T's nodes. */ |
464 | ||
7b8ff279 | 465 | extern void *treap_next(struct treap_iter */*i*/); |
8996f767 MW |
466 | /* Return the next node from I, in ascending order by key. |
467 | * | |
468 | * If there are no more nodes, then return null. | |
469 | */ | |
470 | ||
7b8ff279 | 471 | extern void treap_check(struct treap */*t*/); |
8996f767 MW |
472 | /* Check the treap structure rules for T. */ |
473 | ||
7b8ff279 | 474 | extern void treap_dump(struct treap */*t*/); |
8996f767 | 475 | /* Dump the treap T to standard output, for debugging purposes. */ |
7b8ff279 MW |
476 | |
477 | /*----- Configuration file parsing ----------------------------------------*/ | |
478 | ||
8996f767 | 479 | /* A configuration file. */ |
7b8ff279 | 480 | struct config { |
8996f767 MW |
481 | struct treap sections; /* treap of sections */ |
482 | struct config_section *head, **tail; /* section list, in creation order */ | |
483 | struct config_section *fallback; /* default parent section */ | |
7b8ff279 MW |
484 | }; |
485 | #define CONFIG_INIT { TREAP_INIT, 0, 0 } | |
486 | ||
8996f767 | 487 | /* A configuration section. */ |
7b8ff279 | 488 | struct config_section { |
8996f767 MW |
489 | struct treap_node _node; /* treap intrustion */ |
490 | struct config_section *next; /* next section in creation order */ | |
491 | struct config_section **parents; size_t nparents; /* vector of parents */ | |
492 | struct treap vars; /* treap of variables */ | |
493 | struct treap cache; /* inheritance cache */ | |
7b8ff279 MW |
494 | }; |
495 | #define CONFIG_SECTION_NAME(sect) TREAP_NODE_KEY(sect) | |
496 | #define CONFIG_SECTION_NAMELEN(sect) TREAP_NODE_KEYLEN(sect) | |
497 | ||
8996f767 MW |
498 | /* An entry in a section's inheritance cache: see `search_recursive' for |
499 | * details. | |
500 | */ | |
7b8ff279 | 501 | struct config_cache_entry { |
8996f767 MW |
502 | struct treap_node _node; /* treap intrusion */ |
503 | unsigned f; /* flags */ | |
504 | #define CF_OPEN 1u /* traps inheritance cycles */ | |
505 | struct config_var *var; /* pointer to inherited variable */ | |
7b8ff279 MW |
506 | }; |
507 | ||
8996f767 | 508 | /* A configuration variable. */ |
7b8ff279 | 509 | struct config_var { |
8996f767 MW |
510 | struct treap_node _node; /* treap intrusion */ |
511 | char *file; unsigned line; /* source location, or null/0 */ | |
512 | char *val; size_t n; /* value pointer and length */ | |
513 | unsigned f; /* flags */ | |
514 | #define CF_LITERAL 1u /* value should not be expanded */ | |
515 | #define CF_EXPAND 2u /* traps expansion cycles */ | |
516 | #define CF_OVERRIDE 4u /* override settings from files */ | |
7b8ff279 MW |
517 | }; |
518 | #define CONFIG_VAR_NAME(var) TREAP_NODE_KEY(var) | |
519 | #define CONFIG_VAR_NAMELEN(var) TREAP_NODE_KEYLEN(var) | |
7b8ff279 | 520 | |
8996f767 MW |
521 | /* A section iterator. |
522 | * | |
523 | * (Sections are visited in the order in which they were created.) | |
524 | */ | |
7b8ff279 | 525 | struct config_section_iter { |
8996f767 | 526 | struct config_section *sect; /* next section to return */ |
7b8ff279 MW |
527 | }; |
528 | ||
8996f767 MW |
529 | /* A variable iterator. |
530 | * | |
531 | * (Variables are visited in lexicographical order.) | |
532 | */ | |
7b8ff279 MW |
533 | struct config_var_iter { |
534 | struct treap_iter i; | |
535 | }; | |
536 | ||
8996f767 MW |
537 | /* Common flags. */ |
538 | #define CF_CREAT 1u /* create section or variable */ | |
539 | #define CF_INHERIT 2u /* look up variable in parents */ | |
540 | ||
7b8ff279 | 541 | extern void config_init(struct config */*conf*/); |
8996f767 MW |
542 | /* Initialize the configuration state CONF. |
543 | * | |
544 | * Usually you'd use the static initializer `CONFIG_INIT'. | |
545 | */ | |
7b8ff279 MW |
546 | |
547 | extern struct config_section *config_find_section(struct config */*conf*/, | |
548 | unsigned /*f*/, | |
549 | const char */*name*/); | |
8996f767 MW |
550 | /* Find and return the section with null-terminated NAME in CONF. |
551 | * | |
552 | * If no section is found, the behaviour depends on whether | |
553 | * `CF_CREAT' is set in F: if so, an empty section is created and | |
554 | * returned; otherwise, a null pointer is returned. | |
555 | */ | |
556 | ||
7b8ff279 MW |
557 | extern struct config_section *config_find_section_n(struct config */*conf*/, |
558 | unsigned /*f*/, | |
559 | const char */*name*/, | |
560 | size_t /*sz*/); | |
8996f767 MW |
561 | /* Find and return the section with the given SZ-byte NAME in CONF. |
562 | * | |
563 | * This works like `config_find_section', but with an explicit length | |
564 | * for the NAME rather than null-termination. | |
565 | */ | |
7b8ff279 MW |
566 | |
567 | extern void config_set_fallback(struct config */*conf*/, | |
568 | struct config_section */*sect*/); | |
8996f767 MW |
569 | /* Set the fallback section for CONF to be SECT. |
570 | * | |
571 | * That is, if a section has no explicit parents, then by default it | |
572 | * will have a single parent which is SECT. If SECT is null then | |
573 | * there is no fallback section, and sections which don't have | |
574 | * explicitly specified parents have no parents at all. (This is the | |
575 | * default situation.) | |
576 | */ | |
577 | ||
7b8ff279 MW |
578 | extern void config_set_parent(struct config_section */*sect*/, |
579 | struct config_section */*parent*/); | |
8996f767 MW |
580 | /* Arrange that SECT has PARENT as its single parent section. |
581 | * | |
582 | * If PARENT is null, then arrange that SECT has no parents at all. | |
583 | * In either case, any `@parents' setting will be ignored. | |
584 | */ | |
7b8ff279 MW |
585 | |
586 | extern void config_start_section_iter(struct config */*conf*/, | |
587 | struct config_section_iter */*i*/); | |
8996f767 MW |
588 | /* Initialize I to iterate over the sections defined in CONF. */ |
589 | ||
7b8ff279 MW |
590 | extern struct config_section *config_next_section |
591 | (struct config_section_iter */*i*/); | |
8996f767 MW |
592 | /* Return the next section from I, in order of creation. |
593 | * | |
594 | * If there are no more sections, then return null. | |
595 | */ | |
7b8ff279 MW |
596 | |
597 | extern struct config_var *config_find_var(struct config */*conf*/, | |
598 | struct config_section */*sect*/, | |
599 | unsigned /*f*/, | |
600 | const char */*name*/); | |
8996f767 MW |
601 | /* Find and return the variable with null-terminated NAME in SECT. |
602 | * | |
603 | * If `CF_INHERIT' is set in F, then the function searches the | |
604 | * section's parents recursively; otherwise, it only checks to see | |
605 | * whether the variable is set directly in SECT. | |
606 | * | |
607 | * If no variable is found, the behaviour depends on whether | |
608 | * `CF_CREAT' is set in F: if so, an empty variable is created and | |
609 | * returned; otherwise, a null pointer is returned. | |
610 | * | |
611 | * Setting both `CF_INHERIT' and `CF_CREAT' is not useful. | |
612 | */ | |
613 | ||
7b8ff279 MW |
614 | extern struct config_var *config_find_var_n(struct config */*conf*/, |
615 | struct config_section */*sect*/, | |
616 | unsigned /*f*/, | |
617 | const char */*name*/, | |
618 | size_t /*sz*/); | |
8996f767 MW |
619 | /* Find and return the variable with the given SZ-byte NAME in SECT. |
620 | * | |
621 | * This works like `config_find_var', but with an explicit length for | |
622 | * the NAME rather than null-termination. | |
623 | */ | |
7b8ff279 | 624 | |
6c39ec6d MW |
625 | extern struct config_var *config_set_var(struct config */*conf*/, |
626 | struct config_section */*sect*/, | |
627 | unsigned /*f*/, | |
628 | const char */*name*/, | |
629 | const char */*value*/); | |
8996f767 MW |
630 | /* Set variable NAME to VALUE in SECT, with associated flags F. |
631 | * | |
632 | * The names are null-terminated. The flags are variable flags: see | |
6c39ec6d | 633 | * `struct config_var' for details. Returns the variable. |
8996f767 MW |
634 | * |
635 | * If the variable is already set and has the `CF_OVERRIDE' flag, | |
636 | * then this function does nothing unless `CF_OVERRIDE' is /also/ set | |
637 | * in F. | |
638 | */ | |
639 | ||
6c39ec6d MW |
640 | extern struct config_var *config_set_var_n(struct config */*conf*/, |
641 | struct config_section */*sect*/, | |
642 | unsigned /*f*/, | |
643 | const char */*name*/, | |
644 | size_t /*namelen*/, | |
645 | const char */*value*/, | |
646 | size_t /*valuelen*/); | |
8996f767 MW |
647 | /* As `config_set_var', except that the variable NAME and VALUE have |
648 | * explicit lengths (NAMELEN and VALUELEN, respectively) rather than | |
f07b028e | 649 | * being null-terminated. |
8996f767 MW |
650 | */ |
651 | ||
652 | extern void config_start_var_iter(struct config */*conf*/, | |
653 | struct config_section */*sect*/, | |
7b8ff279 | 654 | struct config_var_iter */*i*/); |
8996f767 MW |
655 | /* Initialize I to iterate over the variables directly defined in |
656 | * SECT. | |
657 | */ | |
658 | ||
7b8ff279 | 659 | extern struct config_var *config_next_var(struct config_var_iter */*i*/); |
8996f767 MW |
660 | /* Return next variable from I, in ascending lexicographical order. |
661 | * | |
662 | * If there are no more variables, then return null. | |
663 | */ | |
7b8ff279 MW |
664 | |
665 | extern int config_read_file(struct config */*conf*/, const char */*file*/, | |
666 | unsigned /*f*/); | |
8996f767 MW |
667 | #define CF_NOENTOK 1u |
668 | /* Read and parse configuration FILE, applying its settings to CONF. | |
669 | * | |
670 | * If all goes well, the function returns 0. If the file is not | |
671 | * found, then the behaviour depends on whether `CF_NOENTOK' is set | |
672 | * in F: if so, then the function simply returns -1. Otherwise, a | |
673 | * fatal error is reported. Note that this /only/ applies if the | |
674 | * file does not exist (specifically, opening it fails with `ENOENT') | |
675 | * -- any other problems are reported as fatal errors regardless of | |
676 | * the flag setting. | |
677 | */ | |
678 | ||
7b8ff279 MW |
679 | extern void config_read_env(struct config */*conf*/, |
680 | struct config_section */*sect*/); | |
8996f767 MW |
681 | /* Populate SECT with environment variables. |
682 | * | |
683 | * Environment variables are always set with `CF_LITERAL'. | |
684 | */ | |
7b8ff279 MW |
685 | |
686 | extern void config_subst_string(struct config */*config*/, | |
687 | struct config_section */*home*/, | |
688 | const char */*what*/, | |
689 | const char */*p*/, struct dstr */*d*/); | |
8996f767 MW |
690 | /* Expand substitutions in a string. |
691 | * | |
692 | * Expand the null-terminated string P relative to the HOME section, | |
693 | * using configuration CONFIG, and appending the result to dynamic | |
694 | * string D. Blame WHAT in any error messages. | |
695 | */ | |
696 | ||
7b8ff279 MW |
697 | extern char *config_subst_string_alloc(struct config */*config*/, |
698 | struct config_section */*home*/, | |
699 | const char */*what*/, | |
700 | const char */*p*/); | |
8996f767 MW |
701 | /* Expand substitutions in a string. |
702 | * | |
703 | * Expand the null-terminated string P relative to the HOME section, | |
704 | * using configuration CONFIG, returning the result as a freshly | |
705 | * malloc(3)ed string. Blame WHAT in any error messages. | |
706 | */ | |
707 | ||
7b8ff279 MW |
708 | extern void config_subst_var(struct config */*config*/, |
709 | struct config_section */*home*/, | |
710 | struct config_var */*var*/, | |
711 | struct dstr */*d*/); | |
8996f767 MW |
712 | /* Expand substitutions in a variable. |
713 | * | |
714 | * Expand the value of the variable VAR relative to the HOME section, | |
715 | * using configuration CONFIG, appending the result to dynamic string | |
716 | * D. | |
717 | */ | |
718 | ||
7b8ff279 MW |
719 | extern char *config_subst_var_alloc(struct config */*config*/, |
720 | struct config_section */*home*/, | |
721 | struct config_var */*var*/); | |
8996f767 MW |
722 | /* Expand substitutions in a variable. |
723 | * | |
724 | * Expand the value of the variable VAR relative to the HOME section, | |
725 | * using configuration CONFIG, returning the result as a freshly | |
726 | * malloc(3)ed string. | |
727 | */ | |
728 | ||
7b8ff279 MW |
729 | extern void config_subst_split_var(struct config */*config*/, |
730 | struct config_section */*home*/, | |
731 | struct config_var */*var*/, | |
732 | struct argv */*av*/); | |
8996f767 MW |
733 | /* Expand substitutions in a variable and split into words. |
734 | * | |
735 | * Expand and word-split the value of the variable VAR relative to | |
736 | * the HOME section, using configuration CONFIG, appending the | |
737 | * resulting words into the vector AV. | |
738 | */ | |
7b8ff279 MW |
739 | |
740 | /*----- That's all, folks -------------------------------------------------*/ | |
741 | ||
742 | #ifdef __cplusplus | |
743 | } | |
744 | #endif | |
745 | ||
746 | #endif |