xref: /minix3/minix/lib/libc/gen/configfile.c (revision 1cd28eb8e4f0c6af09546c7b699680f2f74377eb)
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