↑ 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