Add to git.
[rws.git] / cfg.c
1 /* Configuration file parsing.
2  * - by Richard W.M. Jones <rich@annexia.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * $Id: cfg.c,v 1.11 2002/10/15 21:28:32 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_SYS_TYPES_H
27 #include <sys/types.h>
28 #endif
29
30 #ifdef HAVE_DIRENT_H
31 #include <dirent.h>
32 #endif
33
34 #ifdef HAVE_REGEX_H
35 #include <regex.h>
36 #endif
37
38 #ifdef HAVE_STRING_H
39 #include <string.h>
40 #endif
41
42 #include <pool.h>
43 #include <pstring.h>
44 #include <hash.h>
45 #include <vector.h>
46 #include <pre.h>
47
48 #include "re.h"
49 #include "cfg.h"
50
51 struct config_data
52 {
53   sash data;
54
55   /* Aliases -- these are only present in the host-specific configuration
56    * files, not in CFG_MAIN.
57    */
58   shash aliases;                /* Hash of string -> struct alias_data * */
59 };
60
61 struct alias_data
62 {
63   sash data;
64 };
65
66 static pool cfg_pool = 0;
67 static shash cfg_hosts;         /* Hash of string -> struct config_data * */
68 static struct config_data *cfg_main;
69
70 static struct config_data *read_config (FILE *fp, int is_main, const char *filename);
71 static void config_err (const char *filename, const char *line, const char *msg);
72
73 /* The PATH argument will point to the base for configuration
74  * files, eg. "/etc/rws". We append "/rws.conf" to get the main
75  * configuration file and "/hosts/" to get the virtual hosts
76  * directory.
77  */
78 void
79 cfg_reread_config (const char *path)
80 {
81   const char *config_file;
82   const char *hosts_dir;
83   FILE *fp;
84   DIR *dir;
85   struct dirent *d;
86   pool tmp;
87
88   /* Show the message about reloading the configuration file, but only
89    * the second and subsequent times this function is run.
90    */
91   if (cfg_pool)
92     fprintf (stderr, "reloading configuration file ...\n");
93
94   /* Remove any configuration data from previous configuration run. */
95   if (cfg_pool) delete_pool (cfg_pool);
96
97   /* Create new data structures. */
98   cfg_pool = new_subpool (global_pool);
99   tmp = new_subpool (cfg_pool);
100   cfg_hosts = new_shash (cfg_pool, struct config_data *);
101
102   config_file = psprintf (tmp, "%s/rws.conf", path);
103   hosts_dir = psprintf (tmp, "%s/hosts/", path);
104
105   /* Read in main configuration file. */
106   fp = fopen (config_file, "r");
107   if (fp == 0) { perror (config_file); exit (1); }
108   cfg_main = read_config (fp, 1, config_file);
109   fclose (fp);
110
111   /* Read in each virtual host configuration file. */
112   dir = opendir (hosts_dir);
113   if (dir)
114     {
115       while ((d = readdir (dir)) != 0)
116         {
117           struct config_data *c;
118
119           if (d->d_name[0] != '.') /* Ignore ".", ".." and dotfiles. */
120             {
121               const char *p;
122
123               p = psprintf (tmp, "%s/%s", hosts_dir, d->d_name);
124
125               fp = fopen (p, "r");
126               if (fp == 0) { perror (p); exit (1); }
127               c = read_config (fp, 0, p);
128               fclose (fp);
129
130               shash_insert (cfg_hosts, d->d_name, c);
131             }
132         }
133
134       closedir (dir);
135     }
136
137   delete_pool (tmp);
138 }
139
140 /* Read in a config file from FP. */
141 static struct config_data *
142 read_config (FILE *fp, int is_main, const char *filename)
143 {
144   pool tmp = new_subpool (cfg_pool);
145   char *line = 0;
146   struct config_data *c;
147   struct alias_data *a = 0;
148
149   c = pmalloc (cfg_pool, sizeof *c);
150   c->data = new_sash (cfg_pool);
151   if (!is_main) c->aliases = new_shash (cfg_pool, struct alias_data *);
152   else c->aliases = 0;
153
154   while ((line = pgetlinec (tmp, fp, line)))
155     {
156       vector v;
157
158       if (!is_main && (v = prematch (tmp, line, re_alias_start, 0)))
159         {
160           const char *aliasname;
161
162           if (a) config_err (filename, line, "nested alias");
163
164           vector_get (v, 1, aliasname);
165
166           a = pmalloc (cfg_pool, sizeof *a);
167           a->data = new_sash (cfg_pool);
168
169           if (shash_insert (c->aliases, aliasname, a))
170             config_err (filename, line, "duplicate alias");
171         }
172       else if (!is_main && prematch (tmp, line, re_alias_end, 0))
173         {
174           if (!a)
175             config_err (filename, line,
176                        "end alias found, but not inside an alias definition");
177
178           a = 0;
179         }
180       else if ((v = prematch (tmp, line, re_begin, 0)))
181         {
182           const char *key;
183           char *value, *end_line;
184           sash s;
185
186           vector_get (v, 1, key);
187
188           /* Read the data lines until we get to 'end key' line. */
189           value = pstrdup (tmp, "");
190           end_line = psprintf (tmp, "end %s", key);
191           while ((line = pgetlinec (tmp, fp, line)))
192             {
193               if (strcmp (line, end_line) == 0)
194                 break;
195
196               value = pstrcat (tmp, value, line);
197               value = pstrcat (tmp, value, "\n");
198             }
199
200           if (!line)
201             config_err (filename, "EOF", "missing end <key> line");
202
203           if (a) s = a->data;
204           else s = c->data;
205
206           if (sash_insert (s, key, value))
207             config_err (filename, line, "duplicate definition");
208         }
209       else if ((v = prematch (tmp, line, re_conf_line, 0)))
210         {
211           const char *key, *value;
212           sash s;
213
214           vector_get (v, 1, key);
215           vector_get (v, 2, value);
216
217           /* 'key:' means define key as the empty string (as opposed to
218            * commenting it out which leaves 'key' undefined).
219            */
220           if (value == 0) value = "";
221
222           if (a) s = a->data;
223           else s = c->data;
224
225           if (sash_insert (s, key, value))
226             config_err (filename, line, "duplicate definition");
227         }
228       else
229         config_err (filename, line, "unexpected line");
230     }
231
232   delete_pool (tmp);
233
234   return c;
235 }
236
237 static void
238 config_err (const char *filename, const char *line, const char *msg)
239 {
240   fprintf (stderr,
241            "rws: %s: %s\n"
242            "rws: near ``%s''\n",
243            filename, msg,
244            line);
245   exit (1);
246 }
247
248 void *
249 cfg_get_host (const char *host)
250 {
251   struct config_data *c = 0;
252
253   shash_get (cfg_hosts, host, c);
254   return c;
255 }
256
257 void *
258 cfg_get_alias (void *host_ptr, const char *path)
259 {
260   struct config_data *c = (struct config_data *) host_ptr;
261   struct alias_data *a = 0;
262
263   shash_get (c->aliases, path, a);
264   return a;
265 }
266
267 const char *
268 cfg_get_string (void *host_ptr, void *alias_ptr,
269                 const char *key, const char *default_value)
270 {
271   struct config_data *c = (struct config_data *) host_ptr;
272   struct alias_data *a = (struct alias_data *) alias_ptr;
273   const char *value;
274
275   if (a && sash_get (a->data, key, value))
276     return value;
277   if (c && sash_get (c->data, key, value))
278     return value;
279   if (sash_get (cfg_main->data, key, value))
280     return value;
281
282   return default_value;
283 }
284
285 int
286 cfg_get_int (void *host_ptr, void *alias_ptr, const char *key, int default_value)
287 {
288   const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
289   int r;
290
291   if (!value) return default_value;
292
293   if (sscanf (value, "%d", &r) != 1) return default_value;
294
295   return r;
296 }
297
298 int
299 cfg_get_bool (void *host_ptr, void *alias_ptr, const char *key, int default_value)
300 {
301   const char *value = cfg_get_string (host_ptr, alias_ptr, key, 0);
302
303   if (!value) return default_value;
304
305   if (value[0] == '0' ||
306       value[0] == 'f' || value[0] == 'F' ||
307       value[0] == 'n' || value[0] == 'N' ||
308       (value[0] == 'o' && value[1] == 'f') ||
309       (value[0] == 'O' && value[1] == 'F'))
310     return 0;
311   else if (value[0] == '1' ||
312       value[0] == 't' || value[0] == 'T' ||
313       value[0] == 'y' || value[0] == 'Y' ||
314       (value[0] == 'o' && value[1] == 'n') ||
315       (value[0] == 'O' && value[1] == 'N'))
316     return 1;
317   else
318     return default_value;
319 }