1c2808d8bSBen Gras /* config_read(), _delete(), _length() - Generic config file routines.
2c2808d8bSBen Gras * Author: Kees J. Bot
3c2808d8bSBen Gras * 5 Jun 1999
4c2808d8bSBen Gras */
5c2808d8bSBen Gras #define nil ((void*)0)
6c2808d8bSBen Gras #if __minix_vmd
7c2808d8bSBen Gras #include <minix/stubs.h>
8c2808d8bSBen Gras #else
9c2808d8bSBen Gras #define fstat _fstat
10c2808d8bSBen Gras #define stat _stat
11c2808d8bSBen Gras #endif
12c2808d8bSBen Gras #include <sys/types.h>
13c2808d8bSBen Gras #include <stdio.h>
14c2808d8bSBen Gras #include <stddef.h>
15c2808d8bSBen Gras #include <stdlib.h>
16c2808d8bSBen Gras #include <errno.h>
17c2808d8bSBen Gras #include <string.h>
18c2808d8bSBen Gras #include <time.h>
19c2808d8bSBen Gras #include <sys/stat.h>
20c2808d8bSBen Gras #if __minix_vmd
21c2808d8bSBen Gras #include <minix/asciictype.h>
22c2808d8bSBen Gras #else
23c2808d8bSBen Gras #include <ctype.h>
24c2808d8bSBen Gras #endif
25c2808d8bSBen Gras #define _c /* not const */
26c2808d8bSBen Gras #include <configfile.h>
27c2808d8bSBen Gras
28c2808d8bSBen Gras typedef struct configfile { /* List of (included) configuration files. */
29c2808d8bSBen Gras struct configfile *next; /* A list indeed. */
30c2808d8bSBen Gras time_t ctime; /* Last changed time, -1 if no file. */
31c2808d8bSBen Gras char name[1]; /* File name. */
32c2808d8bSBen Gras } configfile_t;
33c2808d8bSBen Gras
34c2808d8bSBen Gras /* Size of a configfile_t given a file name of length 'len'. */
35c2808d8bSBen Gras #define configfilesize(len) (offsetof(configfile_t, name) + 1 + (len))
36c2808d8bSBen Gras
37c2808d8bSBen Gras typedef struct firstconfig { /* First file and first word share a slot. */
38c2808d8bSBen Gras configfile_t *filelist;
39c2808d8bSBen Gras char new; /* Set when created. */
40c2808d8bSBen Gras config_t config1;
41c2808d8bSBen Gras } firstconfig_t;
42c2808d8bSBen Gras
43c2808d8bSBen Gras /* Size of a config_t given a word of lenght 'len'. Same for firstconfig_t. */
44c2808d8bSBen Gras #define config0size() (offsetof(config_t, word))
45c2808d8bSBen Gras #define configsize(len) (config0size() + 1 + (len))
46c2808d8bSBen Gras #define firstconfigsize(len) \
47c2808d8bSBen Gras (offsetof(firstconfig_t, config1) + configsize(len))
48c2808d8bSBen Gras
49c2808d8bSBen Gras /* Translate address of first config word to enclosing firstconfig_t and vv. */
50c2808d8bSBen Gras #define cfg2fcfg(p) \
51c2808d8bSBen Gras ((firstconfig_t *) ((char *) (p) - offsetof(firstconfig_t, config1)))
52c2808d8bSBen Gras #define fcfg2cfg(p) (&(p)->config1)
53c2808d8bSBen Gras
54c2808d8bSBen Gras /* Variables used while building data. */
55c2808d8bSBen Gras static configfile_t *c_files; /* List of (included) config files. */
56c2808d8bSBen Gras static int c_flags; /* Flags argument of config_read(). */
57c2808d8bSBen Gras static FILE *c_fp; /* Current open file. */
58c2808d8bSBen Gras static char *c_file; /* Current open file name. */
59c2808d8bSBen Gras static unsigned c_line; /* Current line number. */
60c2808d8bSBen Gras static int c; /* Next character. */
61c2808d8bSBen Gras
allocate(void * mem,size_t size)62c2808d8bSBen Gras static void *allocate(void *mem, size_t size)
63c2808d8bSBen Gras /* Like realloc(), but checked. */
64c2808d8bSBen Gras {
65c2808d8bSBen Gras if ((mem= realloc(mem, size)) == nil) {
66c2808d8bSBen Gras fprintf(stderr, "\"%s\", line %u: Out of memory\n", c_file, c_line);
67c2808d8bSBen Gras exit(1);
68c2808d8bSBen Gras }
69c2808d8bSBen Gras return mem;
70c2808d8bSBen Gras }
71c2808d8bSBen Gras
72c2808d8bSBen Gras #define deallocate(mem) free(mem)
73c2808d8bSBen Gras
delete_filelist(configfile_t * cfgf)74c2808d8bSBen Gras static void delete_filelist(configfile_t *cfgf)
75c2808d8bSBen Gras /* Delete configuration file list. */
76c2808d8bSBen Gras {
77c2808d8bSBen Gras void *junk;
78c2808d8bSBen Gras
79c2808d8bSBen Gras while (cfgf != nil) {
80c2808d8bSBen Gras junk= cfgf;
81c2808d8bSBen Gras cfgf= cfgf->next;
82c2808d8bSBen Gras deallocate(junk);
83c2808d8bSBen Gras }
84c2808d8bSBen Gras }
85c2808d8bSBen Gras
delete_config(config_t * cfg)86c2808d8bSBen Gras static void delete_config(config_t *cfg)
87c2808d8bSBen Gras /* Delete configuration file data. */
88c2808d8bSBen Gras {
89c2808d8bSBen Gras config_t *next, *list, *junk;
90c2808d8bSBen Gras
91c2808d8bSBen Gras next= cfg;
92c2808d8bSBen Gras list= nil;
93c2808d8bSBen Gras for (;;) {
94c2808d8bSBen Gras if (next != nil) {
95c2808d8bSBen Gras /* Push the 'next' chain in reverse on the 'list' chain, putting
96c2808d8bSBen Gras * a leaf cell (next == nil) on top of 'list'.
97c2808d8bSBen Gras */
98c2808d8bSBen Gras junk= next;
99c2808d8bSBen Gras next= next->next;
100c2808d8bSBen Gras junk->next= list;
101c2808d8bSBen Gras list= junk;
102c2808d8bSBen Gras } else
103c2808d8bSBen Gras if (list != nil) {
104c2808d8bSBen Gras /* Delete the leaf cell. If it has a sublist then that becomes
105c2808d8bSBen Gras * the 'next' chain.
106c2808d8bSBen Gras */
107c2808d8bSBen Gras junk= list;
108c2808d8bSBen Gras next= list->list;
109c2808d8bSBen Gras list= list->next;
110c2808d8bSBen Gras deallocate(junk);
111c2808d8bSBen Gras } else {
112c2808d8bSBen Gras /* Both chains are gone. */
113c2808d8bSBen Gras break;
114c2808d8bSBen Gras }
115c2808d8bSBen Gras }
116c2808d8bSBen Gras }
117c2808d8bSBen Gras
config_delete(config_t * cfg1)118c2808d8bSBen Gras void config_delete(config_t *cfg1)
119c2808d8bSBen Gras /* Delete configuration file data, being careful with the odd first one. */
120c2808d8bSBen Gras {
121c2808d8bSBen Gras firstconfig_t *fcfg= cfg2fcfg(cfg1);
122c2808d8bSBen Gras
123c2808d8bSBen Gras delete_filelist(fcfg->filelist);
124c2808d8bSBen Gras delete_config(fcfg->config1.next);
125c2808d8bSBen Gras delete_config(fcfg->config1.list);
126c2808d8bSBen Gras deallocate(fcfg);
127c2808d8bSBen Gras }
128c2808d8bSBen Gras
nextc(void)129c2808d8bSBen Gras static void nextc(void)
130c2808d8bSBen Gras /* Read the next character of the current file into 'c'. */
131c2808d8bSBen Gras {
132c2808d8bSBen Gras if (c == '\n') c_line++;
133c2808d8bSBen Gras c= getc(c_fp);
134c2808d8bSBen Gras if (c == EOF && ferror(c_fp)) {
135c2808d8bSBen Gras fprintf(stderr, "\"%s\", line %u: %s\n",
136c2808d8bSBen Gras c_file, c_line, strerror(errno));
137c2808d8bSBen Gras exit(1);
138c2808d8bSBen Gras }
139c2808d8bSBen Gras }
140c2808d8bSBen Gras
skipwhite(void)141c2808d8bSBen Gras static void skipwhite(void)
142c2808d8bSBen Gras /* Skip whitespace and comments. */
143c2808d8bSBen Gras {
144c2808d8bSBen Gras while (isspace(c)) {
145c2808d8bSBen Gras nextc();
146c2808d8bSBen Gras if (c == '#') {
147c2808d8bSBen Gras do nextc(); while (c != EOF && c != '\n');
148c2808d8bSBen Gras }
149c2808d8bSBen Gras }
150c2808d8bSBen Gras }
151c2808d8bSBen Gras
parse_err(void)1525dd8da10SDavid van Moolenbroek static void __dead parse_err(void)
153c2808d8bSBen Gras /* Tell user that you can't parse past the current character. */
154c2808d8bSBen Gras {
155c2808d8bSBen Gras char sc[2];
156c2808d8bSBen Gras
157c2808d8bSBen Gras sc[0]= c;
158c2808d8bSBen Gras sc[1]= 0;
159c2808d8bSBen Gras fprintf(stderr, "\"%s\", line %u: parse error at '%s'\n",
160c2808d8bSBen Gras c_file, c_line, c == EOF ? "EOF" : sc);
161c2808d8bSBen Gras exit(1);
162c2808d8bSBen Gras }
163c2808d8bSBen Gras
read_word(void)164c2808d8bSBen Gras static config_t *read_word(void)
165c2808d8bSBen Gras /* Read a word or string. */
166c2808d8bSBen Gras {
167c2808d8bSBen Gras config_t *w;
168c2808d8bSBen Gras size_t i, len;
169c2808d8bSBen Gras int q;
170c2808d8bSBen Gras static char SPECIAL[] = "!#$%&*+-./:<=>?[\\]^_|~";
171c2808d8bSBen Gras
172c2808d8bSBen Gras i= 0;
173c2808d8bSBen Gras len= 32;
174c2808d8bSBen Gras w= allocate(nil, configsize(32));
175c2808d8bSBen Gras w->next= nil;
176c2808d8bSBen Gras w->list= nil;
177c2808d8bSBen Gras w->file= c_file;
178c2808d8bSBen Gras w->line= c_line;
179c2808d8bSBen Gras w->flags= 0;
180c2808d8bSBen Gras
181c2808d8bSBen Gras /* Is it a quoted string? */
182c2808d8bSBen Gras if (c == '\'' || c == '"') {
183c2808d8bSBen Gras q= c; /* yes */
184c2808d8bSBen Gras nextc();
185c2808d8bSBen Gras } else {
186c2808d8bSBen Gras q= -1; /* no */
187c2808d8bSBen Gras }
188c2808d8bSBen Gras
189c2808d8bSBen Gras for (;;) {
190c2808d8bSBen Gras if (i == len) {
191c2808d8bSBen Gras len+= 32;
192c2808d8bSBen Gras w= allocate(w, configsize(len));
193c2808d8bSBen Gras }
194c2808d8bSBen Gras
195c2808d8bSBen Gras if (q == -1) {
196c2808d8bSBen Gras /* A word consists of letters, numbers and a few special chars. */
197c2808d8bSBen Gras if (!isalnum(c) && c < 0x80 && strchr(SPECIAL, c) == nil) break;
198c2808d8bSBen Gras } else {
199c2808d8bSBen Gras /* Strings are made up of anything except newlines. */
200c2808d8bSBen Gras if (c == EOF || c == '\n') {
201c2808d8bSBen Gras fprintf(stderr,
202c2808d8bSBen Gras "\"%s\", line %u: string at line %u not closed\n",
203c2808d8bSBen Gras c_file, c_line, w->line);
204c2808d8bSBen Gras exit(1);
205c2808d8bSBen Gras break;
206c2808d8bSBen Gras }
207c2808d8bSBen Gras if (c == q) { /* Closing quote? */
208c2808d8bSBen Gras nextc();
209c2808d8bSBen Gras break;
210c2808d8bSBen Gras }
211c2808d8bSBen Gras }
212c2808d8bSBen Gras
213c2808d8bSBen Gras if (c != '\\') { /* Simply add non-escapes. */
214c2808d8bSBen Gras w->word[i++]= c;
215c2808d8bSBen Gras nextc();
216c2808d8bSBen Gras } else { /* Interpret an escape. */
217c2808d8bSBen Gras nextc();
218c2808d8bSBen Gras if (isspace(c)) {
219c2808d8bSBen Gras skipwhite();
220c2808d8bSBen Gras continue;
221c2808d8bSBen Gras }
222c2808d8bSBen Gras
223c2808d8bSBen Gras if (c_flags & CFG_ESCAPED) {
224c2808d8bSBen Gras w->word[i++]= '\\'; /* Keep the \ for the caller. */
225c2808d8bSBen Gras if (i == len) {
226c2808d8bSBen Gras len+= 32;
227c2808d8bSBen Gras w= allocate(w, configsize(len));
228c2808d8bSBen Gras }
229c2808d8bSBen Gras w->flags |= CFG_ESCAPED;
230c2808d8bSBen Gras }
231c2808d8bSBen Gras
232c2808d8bSBen Gras if (isdigit(c)) { /* Octal escape */
233c2808d8bSBen Gras int n= 3;
234c2808d8bSBen Gras int d= 0;
235c2808d8bSBen Gras
236c2808d8bSBen Gras do {
237c2808d8bSBen Gras d= d * 010 + (c - '0');
238c2808d8bSBen Gras nextc();
239c2808d8bSBen Gras } while (--n > 0 && isdigit(c));
240c2808d8bSBen Gras w->word[i++]= d;
241c2808d8bSBen Gras } else
242c2808d8bSBen Gras if (c == 'x' || c == 'X') { /* Hex escape */
243c2808d8bSBen Gras int n= 2;
244c2808d8bSBen Gras int d= 0;
245c2808d8bSBen Gras
246c2808d8bSBen Gras nextc();
247c2808d8bSBen Gras if (!isxdigit(c)) {
248c2808d8bSBen Gras fprintf(stderr, "\"%s\", line %u: bad hex escape\n",
249c2808d8bSBen Gras c_file, c_line);
250c2808d8bSBen Gras exit(1);
251c2808d8bSBen Gras }
252c2808d8bSBen Gras do {
253c2808d8bSBen Gras d= d * 0x10 + (islower(c) ? (c - 'a' + 0xa) :
254c2808d8bSBen Gras isupper(c) ? (c - 'A' + 0xA) :
255c2808d8bSBen Gras (c - '0'));
256c2808d8bSBen Gras nextc();
257c2808d8bSBen Gras } while (--n > 0 && isxdigit(c));
258c2808d8bSBen Gras w->word[i++]= d;
259c2808d8bSBen Gras } else {
260c2808d8bSBen Gras switch (c) {
261c2808d8bSBen Gras case 'a': c= '\a'; break;
262c2808d8bSBen Gras case 'b': c= '\b'; break;
263c2808d8bSBen Gras case 'e': c= '\033'; break;
264c2808d8bSBen Gras case 'f': c= '\f'; break;
265c2808d8bSBen Gras case 'n': c= '\n'; break;
266c2808d8bSBen Gras case 'r': c= '\r'; break;
267c2808d8bSBen Gras case 's': c= ' '; break;
268c2808d8bSBen Gras case 't': c= '\t'; break;
269c2808d8bSBen Gras case 'v': c= '\v'; break;
270c2808d8bSBen Gras default: /* Anything else is kept as-is. */;
271c2808d8bSBen Gras }
272c2808d8bSBen Gras w->word[i++]= c;
273c2808d8bSBen Gras nextc();
274c2808d8bSBen Gras }
275c2808d8bSBen Gras }
276c2808d8bSBen Gras }
277c2808d8bSBen Gras w->word[i]= 0;
278c2808d8bSBen Gras if (q != -1) {
279c2808d8bSBen Gras w->flags |= CFG_STRING;
280c2808d8bSBen Gras } else {
281c2808d8bSBen Gras int f;
282c2808d8bSBen Gras char *end;
283c2808d8bSBen Gras static char base[]= { 0, 010, 10, 0x10 };
284c2808d8bSBen Gras
285c2808d8bSBen Gras if (i == 0) parse_err();
286c2808d8bSBen Gras
287c2808d8bSBen Gras /* Can the word be used as a number? */
288c2808d8bSBen Gras for (f= 0; f < 4; f++) {
289c2808d8bSBen Gras (void) strtol(w->word, &end, base[f]);
290c2808d8bSBen Gras if (*end == 0) w->flags |= 1 << (f + 0);
291c2808d8bSBen Gras (void) strtoul(w->word, &end, base[f]);
292c2808d8bSBen Gras if (*end == 0) w->flags |= 1 << (f + 4);
293c2808d8bSBen Gras }
294c2808d8bSBen Gras }
295c2808d8bSBen Gras return allocate(w, configsize(i));
296c2808d8bSBen Gras }
297c2808d8bSBen Gras
298c2808d8bSBen Gras static config_t *read_file(const char *file);
299c2808d8bSBen Gras static config_t *read_list(void);
300c2808d8bSBen Gras
read_line(void)301c2808d8bSBen Gras static config_t *read_line(void)
302c2808d8bSBen Gras /* Read and return one line of the config file. */
303c2808d8bSBen Gras {
304c2808d8bSBen Gras config_t *cline, **pcline, *clist;
305c2808d8bSBen Gras
306c2808d8bSBen Gras cline= nil;
307c2808d8bSBen Gras pcline= &cline;
308c2808d8bSBen Gras
309c2808d8bSBen Gras for (;;) {
310c2808d8bSBen Gras skipwhite();
311c2808d8bSBen Gras
312c2808d8bSBen Gras if (c == EOF || c == '}') {
313c2808d8bSBen Gras if(0) if (cline != nil) parse_err();
314c2808d8bSBen Gras break;
315c2808d8bSBen Gras } else
316c2808d8bSBen Gras if (c == ';') {
317c2808d8bSBen Gras nextc();
318c2808d8bSBen Gras if (cline != nil) break;
319c2808d8bSBen Gras } else
320c2808d8bSBen Gras if (cline != nil && c == '{') {
321c2808d8bSBen Gras /* A sublist. */
322c2808d8bSBen Gras nextc();
323c2808d8bSBen Gras clist= allocate(nil, config0size());
324c2808d8bSBen Gras clist->next= nil;
325c2808d8bSBen Gras clist->file= c_file;
326c2808d8bSBen Gras clist->line= c_line;
327c2808d8bSBen Gras clist->list= read_list();
328c2808d8bSBen Gras clist->flags= CFG_SUBLIST;
329c2808d8bSBen Gras *pcline= clist;
330c2808d8bSBen Gras pcline= &clist->next;
331c2808d8bSBen Gras if (c != '}') parse_err();
332c2808d8bSBen Gras nextc();
333c2808d8bSBen Gras } else {
334c2808d8bSBen Gras *pcline= read_word();
335c2808d8bSBen Gras pcline= &(*pcline)->next;
336c2808d8bSBen Gras }
337c2808d8bSBen Gras }
338c2808d8bSBen Gras return cline;
339c2808d8bSBen Gras }
340c2808d8bSBen Gras
read_list(void)341c2808d8bSBen Gras static config_t *read_list(void)
342c2808d8bSBen Gras /* Read and return a list of config file commands. */
343c2808d8bSBen Gras {
344c2808d8bSBen Gras config_t *clist, **pclist, *cline;
345c2808d8bSBen Gras
346c2808d8bSBen Gras clist= nil;
347c2808d8bSBen Gras pclist= &clist;
348c2808d8bSBen Gras
349c2808d8bSBen Gras while ((cline= read_line()) != nil) {
350c2808d8bSBen Gras if (strcmp(cline->word, "include") == 0) {
351c2808d8bSBen Gras config_t *file= cline->next;
352c2808d8bSBen Gras if (file == nil || file->next != nil || !config_isatom(file)) {
353c2808d8bSBen Gras fprintf(stderr,
354c2808d8bSBen Gras "\"%s\", line %u: 'include' command requires an argument\n",
355c2808d8bSBen Gras c_file, cline->line);
356c2808d8bSBen Gras exit(1);
357c2808d8bSBen Gras }
358c2808d8bSBen Gras if (file->flags & CFG_ESCAPED) {
359c2808d8bSBen Gras char *p, *q;
360c2808d8bSBen Gras p= q= file->word;
361c2808d8bSBen Gras for (;;) {
362c2808d8bSBen Gras if ((*q = *p) == '\\') *q = *++p;
363c2808d8bSBen Gras if (*q == 0) break;
364c2808d8bSBen Gras p++;
365c2808d8bSBen Gras q++;
366c2808d8bSBen Gras }
367c2808d8bSBen Gras }
368c2808d8bSBen Gras file= read_file(file->word);
369c2808d8bSBen Gras delete_config(cline);
370c2808d8bSBen Gras *pclist= file;
371c2808d8bSBen Gras while (*pclist != nil) pclist= &(*pclist)->next;
372c2808d8bSBen Gras } else {
373c2808d8bSBen Gras config_t *cfg= allocate(nil, config0size());
374c2808d8bSBen Gras cfg->next= nil;
375c2808d8bSBen Gras cfg->list= cline;
376c2808d8bSBen Gras cfg->file= cline->file;
377c2808d8bSBen Gras cfg->line= cline->line;
378c2808d8bSBen Gras cfg->flags= CFG_SUBLIST;
379c2808d8bSBen Gras *pclist= cfg;
380c2808d8bSBen Gras pclist= &cfg->next;
381c2808d8bSBen Gras }
382c2808d8bSBen Gras }
383c2808d8bSBen Gras return clist;
384c2808d8bSBen Gras }
385c2808d8bSBen Gras
read_file(const char * file)386c2808d8bSBen Gras static config_t *read_file(const char *file)
387c2808d8bSBen Gras /* Read and return a configuration file. */
388c2808d8bSBen Gras {
389c2808d8bSBen Gras configfile_t *cfgf;
390c2808d8bSBen Gras config_t *cfg;
391c2808d8bSBen Gras struct stat st;
392c2808d8bSBen Gras FILE *old_fp; /* old_* variables store current file context. */
393c2808d8bSBen Gras char *old_file;
394c2808d8bSBen Gras unsigned old_line;
395c2808d8bSBen Gras int old_c;
396c2808d8bSBen Gras size_t n;
397c2808d8bSBen Gras char *slash;
398c2808d8bSBen Gras
399c2808d8bSBen Gras old_fp= c_fp;
400c2808d8bSBen Gras old_file= c_file;
401c2808d8bSBen Gras old_line= c_line;
402c2808d8bSBen Gras old_c= c;
403c2808d8bSBen Gras
404c2808d8bSBen Gras n= 0;
405c2808d8bSBen Gras if (file[0] != '/' && old_file != nil
406c2808d8bSBen Gras && (slash= strrchr(old_file, '/')) != nil) {
407c2808d8bSBen Gras n= slash - old_file + 1;
408c2808d8bSBen Gras }
409c2808d8bSBen Gras cfgf= allocate(nil, configfilesize(n + strlen(file)));
410c2808d8bSBen Gras memcpy(cfgf->name, old_file, n);
411c2808d8bSBen Gras strcpy(cfgf->name + n, file);
412c2808d8bSBen Gras cfgf->next= c_files;
413c2808d8bSBen Gras c_files= cfgf;
414c2808d8bSBen Gras
415c2808d8bSBen Gras c_file= cfgf->name;
416c2808d8bSBen Gras c_line= 0;
417c2808d8bSBen Gras
418c2808d8bSBen Gras if ((c_fp= fopen(file, "r")) == nil || fstat(fileno(c_fp), &st) < 0) {
419c2808d8bSBen Gras if (errno != ENOENT) {
420c2808d8bSBen Gras fprintf(stderr, "\"%s\", line 1: %s\n", file, strerror(errno));
421c2808d8bSBen Gras exit(1);
422c2808d8bSBen Gras }
423c2808d8bSBen Gras cfgf->ctime= -1;
424c2808d8bSBen Gras c= EOF;
425c2808d8bSBen Gras } else {
426c2808d8bSBen Gras cfgf->ctime= st.st_ctime;
427c2808d8bSBen Gras c= '\n';
428c2808d8bSBen Gras }
429c2808d8bSBen Gras
430c2808d8bSBen Gras cfg= read_list();
431c2808d8bSBen Gras if (c != EOF) parse_err();
432c2808d8bSBen Gras
433c2808d8bSBen Gras if (c_fp != nil) fclose(c_fp);
434c2808d8bSBen Gras c_fp= old_fp;
435c2808d8bSBen Gras c_file= old_file;
436c2808d8bSBen Gras c_line= old_line;
437c2808d8bSBen Gras c= old_c;
438c2808d8bSBen Gras return cfg;
439c2808d8bSBen Gras }
440c2808d8bSBen Gras
config_read(const char * file,int flags,config_t * cfg)441c2808d8bSBen Gras config_t *config_read(const char *file, int flags, config_t *cfg)
442c2808d8bSBen Gras /* Read and parse a configuration file. */
443c2808d8bSBen Gras {
444c2808d8bSBen Gras if (cfg != nil) {
445c2808d8bSBen Gras /* First check if any of the involved files has changed. */
446c2808d8bSBen Gras firstconfig_t *fcfg;
447c2808d8bSBen Gras configfile_t *cfgf;
448c2808d8bSBen Gras struct stat st;
449c2808d8bSBen Gras
450c2808d8bSBen Gras fcfg= cfg2fcfg(cfg);
451c2808d8bSBen Gras for (cfgf= fcfg->filelist; cfgf != nil; cfgf= cfgf->next) {
452c2808d8bSBen Gras if (stat(cfgf->name, &st) < 0) {
453c2808d8bSBen Gras if (errno != ENOENT) break;
454c2808d8bSBen Gras st.st_ctime= -1;
455c2808d8bSBen Gras }
456c2808d8bSBen Gras if (st.st_ctime != cfgf->ctime) break;
457c2808d8bSBen Gras }
458c2808d8bSBen Gras
459c2808d8bSBen Gras if (cfgf == nil) return cfg; /* Everything as it was. */
460c2808d8bSBen Gras config_delete(cfg); /* Otherwise delete and reread. */
461c2808d8bSBen Gras }
462c2808d8bSBen Gras
463c2808d8bSBen Gras errno= 0;
464c2808d8bSBen Gras c_files= nil;
465c2808d8bSBen Gras c_flags= flags;
466c2808d8bSBen Gras cfg= read_file(file);
467c2808d8bSBen Gras
468c2808d8bSBen Gras if (cfg != nil) {
469c2808d8bSBen Gras /* Change first word to have a hidden pointer to a file list. */
470c2808d8bSBen Gras size_t len= strlen(cfg->word);
471c2808d8bSBen Gras firstconfig_t *fcfg;
472c2808d8bSBen Gras
473c2808d8bSBen Gras fcfg= allocate(cfg, firstconfigsize(len));
474c2808d8bSBen Gras memmove(&fcfg->config1, fcfg, configsize(len));
475c2808d8bSBen Gras fcfg->filelist= c_files;
476c2808d8bSBen Gras fcfg->new= 1;
477c2808d8bSBen Gras return fcfg2cfg(fcfg);
478c2808d8bSBen Gras }
479c2808d8bSBen Gras /* Couldn't read (errno != 0) of nothing read (errno == 0). */
480c2808d8bSBen Gras delete_filelist(c_files);
481c2808d8bSBen Gras delete_config(cfg);
482c2808d8bSBen Gras return nil;
483c2808d8bSBen Gras }
484c2808d8bSBen Gras
config_renewed(config_t * cfg)485c2808d8bSBen Gras int config_renewed(config_t *cfg)
486c2808d8bSBen Gras {
487c2808d8bSBen Gras int new;
488c2808d8bSBen Gras
489c2808d8bSBen Gras if (cfg == nil) {
490c2808d8bSBen Gras new= 1;
491c2808d8bSBen Gras } else {
492c2808d8bSBen Gras new= cfg2fcfg(cfg)->new;
493c2808d8bSBen Gras cfg2fcfg(cfg)->new= 0;
494c2808d8bSBen Gras }
495c2808d8bSBen Gras return new;
496c2808d8bSBen Gras }
497c2808d8bSBen Gras
config_length(config_t * cfg)498c2808d8bSBen Gras size_t config_length(config_t *cfg)
499c2808d8bSBen Gras /* Count the number of items on a list. */
500c2808d8bSBen Gras {
501c2808d8bSBen Gras size_t n= 0;
502c2808d8bSBen Gras
503c2808d8bSBen Gras while (cfg != nil) {
504c2808d8bSBen Gras n++;
505c2808d8bSBen Gras cfg= cfg->next;
506c2808d8bSBen Gras }
507c2808d8bSBen Gras return n;
508c2808d8bSBen Gras }
509c2808d8bSBen Gras
510c2808d8bSBen Gras #if TEST
511c2808d8bSBen Gras #include <unistd.h>
512c2808d8bSBen Gras
513c2808d8bSBen Gras static void print_list(int indent, config_t *cfg);
514c2808d8bSBen Gras
print_words(int indent,config_t * cfg)515c2808d8bSBen Gras static void print_words(int indent, config_t *cfg)
516c2808d8bSBen Gras {
517c2808d8bSBen Gras while (cfg != nil) {
518c2808d8bSBen Gras if (config_isatom(cfg)) {
519c2808d8bSBen Gras if (config_isstring(cfg)) fputc('"', stdout);
520c2808d8bSBen Gras printf("%s", cfg->word);
521c2808d8bSBen Gras if (config_isstring(cfg)) fputc('"', stdout);
522c2808d8bSBen Gras } else {
523c2808d8bSBen Gras printf("{\n");
524c2808d8bSBen Gras print_list(indent+4, cfg->list);
525c2808d8bSBen Gras printf("%*s}", indent, "");
526c2808d8bSBen Gras }
527c2808d8bSBen Gras cfg= cfg->next;
528c2808d8bSBen Gras if (cfg != nil) fputc(' ', stdout);
529c2808d8bSBen Gras }
530c2808d8bSBen Gras printf(";\n");
531c2808d8bSBen Gras }
532c2808d8bSBen Gras
print_list(int indent,config_t * cfg)533c2808d8bSBen Gras static void print_list(int indent, config_t *cfg)
534c2808d8bSBen Gras {
535c2808d8bSBen Gras while (cfg != nil) {
536c2808d8bSBen Gras if (!config_issub(cfg)) {
537*1cd28eb8SJacob Adams fprintf(stderr, "Cell at \"%s\", line %u is not a sublist\n",
538*1cd28eb8SJacob Adams c_file, c_line);
539c2808d8bSBen Gras break;
540c2808d8bSBen Gras }
541c2808d8bSBen Gras printf("%*s", indent, "");
542c2808d8bSBen Gras print_words(indent, cfg->list);
543c2808d8bSBen Gras cfg= cfg->next;
544c2808d8bSBen Gras }
545c2808d8bSBen Gras }
546c2808d8bSBen Gras
print_config(config_t * cfg)547c2808d8bSBen Gras static void print_config(config_t *cfg)
548c2808d8bSBen Gras {
549c2808d8bSBen Gras if (!config_renewed(cfg)) {
550c2808d8bSBen Gras printf("# Config didn't change\n");
551c2808d8bSBen Gras } else {
552c2808d8bSBen Gras print_list(0, cfg);
553c2808d8bSBen Gras }
554c2808d8bSBen Gras }
555c2808d8bSBen Gras
main(int argc,char ** argv)556c2808d8bSBen Gras int main(int argc, char **argv)
557c2808d8bSBen Gras {
558c2808d8bSBen Gras config_t *cfg;
559c2808d8bSBen Gras int c;
560c2808d8bSBen Gras
561c2808d8bSBen Gras if (argc != 2) {
562c2808d8bSBen Gras fprintf(stderr, "One config file name please\n");
563c2808d8bSBen Gras exit(1);
564c2808d8bSBen Gras }
565c2808d8bSBen Gras
566c2808d8bSBen Gras cfg= nil;
567c2808d8bSBen Gras do {
568c2808d8bSBen Gras cfg= config_read(argv[1], CFG_ESCAPED, cfg);
569c2808d8bSBen Gras print_config(cfg);
570c2808d8bSBen Gras if (!isatty(0)) break;
571c2808d8bSBen Gras while ((c= getchar()) != EOF && c != '\n') {}
572c2808d8bSBen Gras } while (c != EOF);
573c2808d8bSBen Gras return 0;
574c2808d8bSBen Gras }
575c2808d8bSBen Gras #endif /* TEST */
576