1 #include <stdio.h>
  2 #include <string.h>
  3 #include <sys/types.h>
  4 #include <sys/stat.h>
  5 #include <fcntl.h>
  6 #include <sys/mman.h>
  7 #include <unistd.h>
  8 #include <ctype.h>
  9 #include "config.h"
 10 #include "rh_string.h"
 11 
 12 static int config_parse (config_t *config, const char *data, size_t ndata);
 13 static int config_get_key (const char *key, size_t nkey);
 14 
 15 
 16 #define DECLARE_KEY(_idx,_name,_min,_max,_type) \
 17         { CONFIG_KEY_ ## _idx, _name, CONST_LEN(_name), _min, _max, _type }
 18 
 19 static const struct config_key_definition config_key_definition[] =
 20 {
 21         DECLARE_KEY(TYPES_CONFIG, "TypesConfig", 1, -1, 0),
 22         DECLARE_KEY(DEFAULT_TYPE, "DefaultType", 1, 1, 0),
 23         DECLARE_KEY(VIRTUAL_DOCUMENT_ROOT, "VirtualDocumentRoot", 1, 1, 0),
 24         DECLARE_KEY(SERVER_NAME, "ServerName", 1, 1, 0),
 25         DECLARE_KEY(LOADMODULE, "LoadModule", 2, 2, 0),
 26         DECLARE_KEY(DIRECTORYINDEX, "DirectoryIndex", 0, -1, 0),
 27         {0,NULL,}
 28 };
 29 
 30 #undef DECLARE_KEY
 31 
 32 config_t * config_alloc (const char *filename)
 33 {
 34         config_t        *config;
 35         size_t          size;
 36 
 37         config = calloc (1, sizeof(*config) );
 38         if (NULL == config)
 39                 goto error_out;
 40         
 41         size = strlen(filename);
 42         
 43         if (0 != rh_buffer_reserve (&config->filename, size + 1))
 44                 goto error_out;
 45 
 46         if (0 != rh_buffer_append (&config->filename, filename, size))
 47                 goto error_out;
 48 
 49         config->filename.data[config->filename.used] = 0;
 50 
 51         config->used = 1;
 52         
 53         return config;
 54 
 55 error_out:
 56         if (config) {
 57                 rh_buffer_destroy(&config->filename);
 58                 free (config);
 59         }
 60         return NULL;
 61 }
 62 
 63 
 64 /*
 65  *
 66  *
 67  * this function free's the whole config thing if it is not used.
 68  *
 69  * example: configuration is reloaded but the old one is currently
 70  *          in use by something.
 71  *
 72  */
 73 void config_free (config_t *config)
 74 {       
 75         if (config->used)
 76                 --config->used;
 77 
 78         if (config->used)
 79                 return;
 80 
 81         config_reset (config);
 82         
 83         rh_buffer_destroy(&config->filename);
 84         
 85         memset (config, 0, sizeof(*config) );
 86         
 87         free (config);
 88 }
 89 
 90 /*
 91  *
 92  * removes ALL config entries without any "used" checks
 93  *
 94  *
 95  */
 96 void config_reset (config_t *config)
 97 {
 98         size_t  i;
 99 
100         for (i = 0; i < CONFIG_KEY_MAX; ++i) {
101                 config_entry_t  *entry;
102                 
103                 entry = config->entries[i];
104                 
105                 if (NULL == entry)
106                         continue;
107 
108                 entry->key = NULL;
109                 
110                 while (!TAILQ_EMPTY(&entry->values)) {
111                         config_value_t  *value;
112 
113                         value = TAILQ_FIRST(&entry->values);
114 
115                         TAILQ_REMOVE(&entry->values, value, list);
116 
117                         rh_buffer_destroy (&value->value);
118                         
119                         free (value);
120                 }
121 
122                 free (entry);
123                 config->entries[i] = NULL;
124         }
125 
126         memset (config->entries, 0, sizeof(config->entries) );
127 }
128 
129 int config_reload (config_t *config)
130 {
131         int             return_code = 0;
132         struct stat     stbuf;
133         int             fd;
134         void            *mapping = MAP_FAILED;
135 
136         if (config->used > 1)
137                 return -1;
138         
139 
140         /*
141          *
142          * open config file
143          *
144          *
145          */
146 
147         fd = open (config->filename.data, O_RDONLY);
148         if (fd == -1)
149                 return -1;
150 
151         if (-1 == fstat (fd, &stbuf)) {
152                 return_code = -1;
153                 goto out;
154         }
155 
156         mapping = mmap (NULL, stbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
157         if (MAP_FAILED == mapping) {
158                 return_code = -1;
159                 goto out;
160         }       
161 
162 
163         /*
164          *
165          * parse config file
166          *
167          *
168          */
169         
170         if (0 != config_parse (config, mapping, stbuf.st_size)) {
171                 return_code = -1;
172                 goto out;
173         }
174                 
175 
176 out:
177         if (fd > -1) {
178                 if (mapping != MAP_FAILED)
179                         munmap (mapping, stbuf.st_size);
180                 close (fd);
181         }
182         
183         if (0 != return_code) {
184                 config_reset (config);
185         }
186 
187         return return_code;
188 }
189 
190 static int config_parse (config_t *config, const char *data, size_t ndata)
191 {
192         config_entry_t  *entry;
193         size_t          lineno;
194 
195         lineno = 0;
196         entry = NULL;
197         
198         while (ndata) {
199                 config_value_t  *value;
200                 config_key_t    key_idx;
201 
202                 const char      *line, *temp;
203                 size_t          nline;
204 
205                 const char      *key;
206                 size_t          nkey;
207                 
208                 const char      *val;
209                 size_t          nval;
210 
211                 size_t          i;
212 
213                 /* find line length */
214                 
215                 line = data;
216                 
217                 temp = memchr (line, '\n', ndata);
218 
219                 if (NULL == temp) {
220                         nline = ndata;
221                 } else {
222                         nline = temp + 1 - line;
223                         ++lineno;
224                 }
225 
226                 temp = NULL;
227 
228                 /* recalc buffer */
229                 data  += nline;
230                 ndata -= nline;
231 
232                 /* trim line */
233                 for (;nline && iscntrl(line[nline-1]); --nline);
234                 CONST_LTRIM(line,nline,isspace);
235         
236                 if (0 == nline)
237                         continue;
238                 
239 #if 0
240                 printf ("LINE [%.*s]\n", nline, line);
241 #endif
242 
243                 /* find key */
244 
245                 key = line;
246                 nkey = 0;
247                 
248                 for (i=0;i<nline;++i) {
249                         char ch = line[i];
250                         
251                         switch (ch) {
252                         case '#':
253                                 /* "# comment" */
254                                 /* "keyname#whatever" ?! */
255                                 i = nline = 0;
256                                 break;
257                         case ' ' :
258                         case '\t':
259                                 break;
260                         case '-':
261                         case '_':
262                                 continue;
263                         default:
264                                 if (!isalnum(ch))
265                                         break;
266                                 continue;
267                         }
268                         break;
269                 }
270 
271                 nkey   = i;
272 
273                 if (0 == nkey)
274                         continue;
275 #if 0
276                 printf (" key [%.*s]\n", nkey, key);
277 #endif
278                 
279                 line  += i;
280                 nline -= i;
281                 
282                 CONST_LTRIM(line,nline,isspace);
283                 
284                 val  = line;
285                 nval = 0;
286 
287                 if (0 == nline)
288                         continue;
289 
290                 /* lookup key */
291                 key_idx = config_get_key (key, nkey);
292                 if (-1 == key_idx) {
293                         printf ("%s(%.*s): line #%3zu unknown key \"%.*s\"\n",
294                                         __FUNCTION__,
295                                         config->filename.used,
296                                         config->filename.data,
297                                         lineno,
298                                         nkey, key );
299                         continue;
300                 }
301 
302 
303                 /* add key or use previously added */
304                 if (config->entries[key_idx]) {
305                         entry = config->entries[key_idx];
306                 } else {
307                         entry = calloc (1, sizeof(*entry));
308                         if (NULL==entry)
309                                 return -1;
310 
311                         entry->key = &config_key_definition[key_idx];
312 
313                         entry->config = config;
314                         
315                         config->entries[key_idx] = entry;
316 
317                         TAILQ_INIT(&entry->values);
318                 }
319         
320                 /* find values */
321 
322                 /*
323                  *
324                  * this parser sucks a bit.
325                  *
326                  * add support for multiple lines:
327                  *      
328                  *      configkey value1 value2 \
329                  *        value3 value4
330                  *
331                  */     
332                 for (;;) {
333                         char ch=0;
334                                 
335                         if (    !nline ||
336                                 /* WARNING: side effect! */
337                                 isspace((ch=line[0])) ||
338                                 '#' == ch)
339                         {
340                                 if (nval) {
341 #if 0
342                                         printf ("   val [%.*s]\n", nval, val);
343 #endif
344                                         
345                                         value = calloc (1, sizeof(*value));
346                                         if (NULL == value)
347                                                 return -1;
348                                         
349                                         if (rh_buffer_reserve(&value->value,
350                                                                 nval+1) )
351                                         {
352                                                 free (value);
353                                                 return -1;
354                                         }
355                 
356                                         if (rh_buffer_append(&value->value,
357                                                                 val, nval) )
358                                         {
359                                                 free (value);
360                                                 return -1;
361                                         }
362 
363                                         value->value.data[value->value.used]=0;
364 
365                                         value->entry = entry;
366                                         ++entry->nvalues;
367 
368                                         TAILQ_INSERT_TAIL(&entry->values,
369                                                         value, list);
370                                 }
371                 
372                                 if (0 == nline || ch == '#')
373                                         break;
374                                 
375                                 val  = "";
376                                 nval = 0;
377 
378                                 ++line;
379                                 --nline;
380 
381                                 val = line;
382                                 continue;
383                         }
384                                 
385                         ++line;
386                         --nline;
387                         ++nval;
388                 }
389         }
390         
391         
392         return 0;
393 }
394 
395 
396 static int config_get_key (const char *key, size_t nkey)
397 {
398         const struct config_key_definition      *def;
399         
400         for (def=config_key_definition; def->name; ++def) {
401 #if 0
402                 printf ("%s() [%.*s] vs. [%.*s]\n", __FUNCTION__,
403                                 nkey, key,
404                                 def->nname, def->name);
405 #endif
406                 if (def->nname != nkey)
407                         continue;
408 
409                 if (0 != rh_memcasecmp(def->name, key, nkey))
410                         continue;
411 
412                 return def->idx;
413         }
414         
415         return -1;
416 }
417 
418 const config_entry_t * config_pop (config_t *config, config_key_t key)
419 {
420         config_entry_t  *entry;
421 
422         entry = config->entries[key];
423         if (NULL==entry)
424                 return NULL;
425 
426         ++entry->used;
427         ++entry->config->used;
428         
429         return entry;
430 }
431 
432 const config_value_t * config_pop_value (config_t *config, config_key_t key)
433 {
434         const config_entry_t    *entry;
435 
436         entry = config_pop (config, key);
437         if (NULL == entry)
438                 return NULL;
439 
440         if (TAILQ_EMPTY(&entry->values)) {
441                 config_push(entry);
442                 return NULL;
443         }
444         
445         return TAILQ_FIRST(&entry->values);
446 }
447 
448 void config_push (const config_entry_t * const_entry)
449 {
450         config_entry_t  *entry = (config_entry_t*)const_entry;
451 
452         if (NULL==entry)
453                 return;
454 
455         if (entry->used)
456                 --entry->used;
457 
458         config_free (entry->config);
459 }
460 
461 void config_push_value (const config_value_t * value)
462 {
463         if (value && value->entry)
464                 config_push (value->entry);
465 }


syntax highlighted by Code2HTML, v. 0.9.1