Mercurial > hg > nnchat
annotate th_config.c @ 378:afbc3bfd3e03
Sync th-libs.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 03 Oct 2011 00:31:46 +0300 |
parents | e694c02d6982 |
children |
rev | line source |
---|---|
133 | 1 /* |
2 * Very simple configuration handling functions | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
4 * (C) Copyright 2004-2010 Tecnic Software productions (TNSP) |
133 | 5 * |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #ifdef HAVE_CONFIG_H | |
9 #include "config.h" | |
10 #endif | |
11 #include <stdio.h> | |
12 #include <stdarg.h> | |
13 #include "th_config.h" | |
14 #include "th_util.h" | |
15 #include "th_string.h" | |
16 | |
17 #define SET_MAX_BUF (8192) | |
18 | |
19 | |
20 /* Free a given configuration (the values are not free'd) | |
21 */ | |
22 void th_cfg_free(cfgitem_t *cfg) | |
23 { | |
24 cfgitem_t *curr = cfg; | |
25 | |
378 | 26 while (curr != NULL) |
27 { | |
133 | 28 cfgitem_t *next = curr->next; |
378 | 29 |
133 | 30 if (curr->type == ITEM_SECTION) |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
31 th_cfg_free((cfgitem_t *) curr->v.data); |
378 | 32 |
133 | 33 th_free(curr->name); |
34 th_free(curr); | |
35 curr = next; | |
36 } | |
37 } | |
38 | |
39 | |
40 /* Allocate and add new item to configuration | |
41 */ | |
378 | 42 static cfgitem_t *th_cfg_add(cfgitem_t **cfg, const char *name, |
43 const int type, void *data) | |
133 | 44 { |
45 cfgitem_t *node; | |
46 | |
47 if (cfg == NULL) | |
48 return NULL; | |
49 | |
50 /* Allocate new item */ | |
51 node = (cfgitem_t *) th_calloc(1, sizeof(cfgitem_t)); | |
52 if (node == NULL) | |
53 return NULL; | |
54 | |
55 /* Set values */ | |
56 node->type = type; | |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
57 node->v.data = data; |
133 | 58 node->name = th_strdup(name); |
378 | 59 |
133 | 60 /* Insert into linked list */ |
378 | 61 if (*cfg != NULL) |
62 { | |
133 | 63 node->prev = (*cfg)->prev; |
64 (*cfg)->prev->next = node; | |
65 (*cfg)->prev = node; | |
378 | 66 } |
67 else | |
68 { | |
133 | 69 *cfg = node; |
70 node->prev = node; | |
71 } | |
72 node->next = NULL; | |
73 | |
74 return node; | |
75 } | |
76 | |
77 | |
78 /* Add integer type setting into give configuration | |
79 */ | |
378 | 80 int th_cfg_add_int(cfgitem_t **cfg, char *name, int *itemData, int itemDef) |
133 | 81 { |
82 cfgitem_t *node; | |
83 | |
84 node = th_cfg_add(cfg, name, ITEM_INT, (void *) itemData); | |
85 if (node == NULL) | |
86 return -1; | |
87 | |
88 *itemData = itemDef; | |
89 | |
90 return 0; | |
91 } | |
92 | |
93 | |
378 | 94 int th_cfg_add_hexvalue(cfgitem_t **cfg, char *name, |
95 int *itemData, int itemDef) | |
133 | 96 { |
97 cfgitem_t *node; | |
98 | |
99 node = th_cfg_add(cfg, name, ITEM_HEX_TRIPLET, (void *) itemData); | |
100 if (node == NULL) | |
101 return -1; | |
102 | |
103 *itemData = itemDef; | |
104 | |
105 return 0; | |
106 } | |
107 | |
108 | |
109 /* Add unsigned integer type setting into give configuration | |
110 */ | |
378 | 111 int th_cfg_add_uint(cfgitem_t **cfg, char *name, |
112 unsigned int *itemData, unsigned int itemDef) | |
133 | 113 { |
114 cfgitem_t *node; | |
115 | |
116 node = th_cfg_add(cfg, name, ITEM_UINT, (void *) itemData); | |
117 if (node == NULL) | |
118 return -1; | |
119 | |
120 *itemData = itemDef; | |
121 | |
122 return 0; | |
123 } | |
124 | |
125 | |
126 /* Add strint type setting into given configuration | |
127 */ | |
378 | 128 int th_cfg_add_string(cfgitem_t **cfg, char *name, |
129 char **itemData, char *itemDef) | |
133 | 130 { |
131 cfgitem_t *node; | |
132 | |
133 node = th_cfg_add(cfg, name, ITEM_STRING, (void *) itemData); | |
134 if (node == NULL) | |
135 return -1; | |
136 | |
137 *itemData = th_strdup(itemDef); | |
138 | |
139 return 0; | |
140 } | |
141 | |
142 | |
143 /* Add boolean type setting into given configuration | |
144 */ | |
378 | 145 int th_cfg_add_bool(cfgitem_t **cfg, char *name, |
146 BOOL *itemData, BOOL itemDef) | |
133 | 147 { |
148 cfgitem_t *node; | |
149 | |
150 node = th_cfg_add(cfg, name, ITEM_BOOL, (void *) itemData); | |
151 if (node == NULL) | |
152 return -1; | |
153 | |
154 *itemData = itemDef; | |
155 | |
156 return 0; | |
157 } | |
158 | |
159 | |
160 /* Add implicit comment | |
161 */ | |
378 | 162 int th_cfg_add_comment(cfgitem_t **cfg, char *comment) |
133 | 163 { |
164 cfgitem_t *node; | |
165 | |
166 node = th_cfg_add(cfg, comment, ITEM_COMMENT, NULL); | |
167 if (node == NULL) | |
168 return -1; | |
169 | |
170 return 0; | |
171 } | |
172 | |
173 | |
174 /* Add new section | |
175 */ | |
378 | 176 int th_cfg_add_section(cfgitem_t **cfg, char *name, cfgitem_t *data) |
133 | 177 { |
178 cfgitem_t *node; | |
378 | 179 |
133 | 180 node = th_cfg_add(cfg, name, ITEM_SECTION, (void *) data); |
181 if (node == NULL) | |
182 return -1; | |
183 | |
184 return 0; | |
185 } | |
186 | |
187 | |
378 | 188 int th_cfg_add_string_list(cfgitem_t **cfg, char *name, qlist_t **data) |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
189 { |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
190 cfgitem_t *node; |
378 | 191 |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
192 if (data == NULL) |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
193 return -5; |
378 | 194 |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
195 node = th_cfg_add(cfg, name, ITEM_STRING_LIST, (void *) data); |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
196 if (node == NULL) |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
197 return -1; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
198 |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
199 return 0; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
200 } |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
201 |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
202 |
133 | 203 /* Read a given file into configuration structure and variables |
204 */ | |
378 | 205 enum |
206 { | |
133 | 207 PM_EOF, |
208 PM_ERROR, | |
209 PM_NORMAL, | |
210 PM_COMMENT, | |
211 PM_NEXT, | |
212 PM_KEYNAME, | |
213 PM_KEYSET, | |
214 PM_STRING, | |
215 PM_INT, | |
216 PM_BOOL, | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
217 PM_SECTION, |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
218 PM_ARRAY |
133 | 219 }; |
220 | |
221 #define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; } | |
222 #define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c) || ch == '#') | |
223 | |
378 | 224 typedef struct |
225 { | |
133 | 226 FILE *file; |
227 char *filename; | |
228 size_t line; | |
229 } conffile_t; | |
230 | |
231 | |
232 static void th_cfg_error(conffile_t *f, const char *fmt, ...) | |
233 { | |
234 va_list ap; | |
235 va_start(ap, fmt); | |
378 | 236 fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, f->filename, |
237 (unsigned int) f->line); | |
133 | 238 vfprintf(stderr, fmt, ap); |
239 va_end(ap); | |
240 } | |
241 | |
242 | |
378 | 243 static int th_cfg_read_sect(conffile_t *f, cfgitem_t *cfg, int nesting) |
133 | 244 { |
245 cfgitem_t *item = NULL; | |
246 char tmpStr[SET_MAX_BUF + 1]; | |
247 size_t strPos; | |
248 int c, parseMode, prevMode, nextMode, tmpCh; | |
249 BOOL isFound, isStart, isError, validError; | |
250 | |
251 /* Initialize values */ | |
252 tmpCh = 0; | |
253 strPos = 0; | |
254 c = -1; | |
255 isFound = isStart = isError = validError = FALSE; | |
256 nextMode = prevMode = parseMode = PM_NORMAL; | |
257 | |
258 /* Parse the configuration */ | |
378 | 259 while (parseMode != PM_EOF && parseMode != PM_ERROR) |
260 { | |
261 if (c == -1) | |
262 { | |
133 | 263 /* Get next character */ |
378 | 264 switch (c = fgetc(f->file)) |
265 { | |
133 | 266 case EOF: |
378 | 267 if (parseMode != PM_NORMAL) |
268 { | |
269 th_cfg_error(f, "Unexpected end of file.\n"); | |
133 | 270 parseMode = PM_ERROR; |
378 | 271 } |
272 else | |
133 | 273 parseMode = PM_EOF; |
274 break; | |
275 | |
276 case '\n': | |
277 f->line++; | |
278 } | |
279 } | |
280 | |
378 | 281 switch (parseMode) |
282 { | |
133 | 283 case PM_COMMENT: |
284 /* Comment parsing mode */ | |
378 | 285 if (c == '\n') |
286 { | |
133 | 287 /* End of line, end of comment */ |
288 parseMode = prevMode; | |
289 prevMode = PM_COMMENT; | |
290 } | |
291 c = -1; | |
292 break; | |
293 | |
294 case PM_NORMAL: | |
295 /* Normal parsing mode */ | |
378 | 296 if (c == '#') |
297 { | |
133 | 298 prevMode = parseMode; |
299 parseMode = PM_COMMENT; | |
300 c = -1; | |
378 | 301 } |
302 else if (VISEND(c)) | |
303 { | |
133 | 304 c = -1; |
378 | 305 } |
306 else if (c == '}') | |
307 { | |
308 if (nesting > 0) | |
309 { | |
133 | 310 /* Check for validation errors */ |
311 return (validError) ? 1 : 0; | |
378 | 312 } |
313 else | |
314 { | |
315 th_cfg_error(f, | |
316 "Invalid nesting sequence encountered.\n"); | |
133 | 317 parseMode = PM_ERROR; |
318 } | |
378 | 319 } |
320 else if (th_isalpha(c)) | |
321 { | |
133 | 322 /* Start of key name found */ |
323 prevMode = parseMode; | |
324 parseMode = PM_KEYNAME; | |
325 strPos = 0; | |
378 | 326 } |
327 else | |
328 { | |
133 | 329 /* Error! Invalid character found */ |
378 | 330 th_cfg_error(f, "Unexpected character '%c'.\n", c); |
133 | 331 parseMode = PM_ERROR; |
332 } | |
333 break; | |
334 | |
335 case PM_KEYNAME: | |
336 /* Configuration KEY name parsing mode */ | |
378 | 337 if (c == '#') |
338 { | |
133 | 339 /* Start of comment */ |
340 prevMode = parseMode; | |
341 parseMode = PM_COMMENT; | |
342 c = -1; | |
378 | 343 } |
344 else if (th_iscrlf(c) || th_isspace(c) || c == '=') | |
345 { | |
133 | 346 /* End of key name */ |
347 prevMode = parseMode; | |
348 parseMode = PM_NEXT; | |
349 nextMode = PM_KEYSET; | |
378 | 350 } |
351 else if (th_isalnum(c) || c == '_') | |
352 { | |
133 | 353 /* Add to key name string */ |
354 VADDCH(c) | |
355 else | |
356 { | |
357 /* Error! Key name string too long! */ | |
378 | 358 th_cfg_error(f, "Config key name too long!"); |
133 | 359 parseMode = PM_ERROR; |
360 } | |
361 c = -1; | |
378 | 362 } |
363 else | |
364 { | |
133 | 365 /* Error! Invalid character found */ |
366 tmpStr[strPos] = 0; | |
367 th_cfg_error(f, | |
378 | 368 "Unexpected character '%c' in key name '%s'.\n", |
369 c, tmpStr); | |
133 | 370 parseMode = PM_ERROR; |
371 } | |
372 break; | |
373 | |
374 case PM_KEYSET: | |
378 | 375 if (c == '=') |
376 { | |
133 | 377 /* Find key from configuration */ |
378 tmpStr[strPos] = 0; | |
379 isFound = FALSE; | |
380 item = cfg; | |
378 | 381 while (item != NULL && !isFound) |
382 { | |
133 | 383 if (item->name != NULL && strcmp(item->name, tmpStr) == 0) |
384 isFound = TRUE; | |
385 else | |
386 item = item->next; | |
387 } | |
388 | |
389 /* Check if key was found */ | |
378 | 390 if (isFound) |
391 { | |
133 | 392 /* Okay, set next mode */ |
378 | 393 switch (item->type) |
394 { | |
133 | 395 case ITEM_HEX_TRIPLET: |
396 case ITEM_STRING: | |
397 nextMode = PM_STRING; | |
398 break; | |
399 | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
400 case ITEM_STRING_LIST: |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
401 nextMode = PM_ARRAY; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
402 break; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
403 |
133 | 404 case ITEM_INT: |
405 case ITEM_UINT: | |
406 nextMode = PM_INT; | |
407 break; | |
408 | |
409 case ITEM_BOOL: | |
410 nextMode = PM_BOOL; | |
411 break; | |
378 | 412 |
133 | 413 case ITEM_SECTION: |
414 nextMode = PM_SECTION; | |
415 break; | |
416 } | |
417 | |
418 prevMode = parseMode; | |
419 parseMode = PM_NEXT; | |
420 isStart = TRUE; | |
421 strPos = 0; | |
378 | 422 } |
423 else | |
424 { | |
133 | 425 /* Error! No configuration key by this name found */ |
426 th_cfg_error(f, | |
378 | 427 "No such configuration setting ('%s')\n", |
428 tmpStr); | |
133 | 429 parseMode = PM_ERROR; |
430 } | |
431 | |
432 c = -1; | |
378 | 433 } |
434 else | |
435 { | |
133 | 436 /* Error! '=' expected! */ |
437 th_cfg_error(f, | |
378 | 438 "Unexpected character '%c', assignation '=' was expected.\n", |
439 c); | |
133 | 440 parseMode = PM_ERROR; |
441 } | |
442 break; | |
443 | |
444 case PM_NEXT: | |
445 /* Search next item parsing mode */ | |
378 | 446 if (c == '#') |
447 { | |
133 | 448 /* Start of comment */ |
449 prevMode = parseMode; | |
450 parseMode = PM_COMMENT; | |
378 | 451 } |
452 else if (th_isspace(c) || th_iscrlf(c)) | |
453 { | |
133 | 454 /* Ignore whitespaces and linechanges */ |
455 c = -1; | |
378 | 456 } |
457 else | |
458 { | |
133 | 459 /* Next item found */ |
460 prevMode = parseMode; | |
461 parseMode = nextMode; | |
462 } | |
463 break; | |
464 | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
465 case PM_ARRAY: |
378 | 466 if (isStart) |
467 { | |
468 switch (item->type) | |
469 { | |
470 case ITEM_STRING_LIST: | |
471 prevMode = parseMode; | |
472 parseMode = PM_STRING; | |
473 break; | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
474 } |
378 | 475 } |
476 else if (c == ',') | |
477 { | |
478 switch (item->type) | |
479 { | |
480 case ITEM_STRING_LIST: | |
481 c = -1; | |
482 isStart = TRUE; | |
483 prevMode = parseMode; | |
484 parseMode = PM_NEXT; | |
485 nextMode = PM_STRING; | |
486 break; | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
487 } |
378 | 488 } |
489 else | |
490 { | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
491 prevMode = parseMode; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
492 parseMode = PM_NORMAL; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
493 } |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
494 break; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
495 |
133 | 496 case PM_SECTION: |
497 /* Section parsing mode */ | |
378 | 498 if (c != '{') |
499 { | |
133 | 500 /* Error! Section start '{' expected! */ |
501 th_cfg_error(f, | |
378 | 502 "Unexpected character '%c', section start '{' was expected.\n", |
503 c); | |
133 | 504 parseMode = PM_ERROR; |
378 | 505 } |
506 else | |
507 { | |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
508 int res = th_cfg_read_sect(f, item->v.section, nesting + 1); |
133 | 509 c = -1; |
510 if (res > 0) | |
511 validError = TRUE; | |
512 else if (res < 0) | |
513 parseMode = PM_ERROR; | |
378 | 514 else |
515 { | |
133 | 516 prevMode = parseMode; |
517 parseMode = PM_NORMAL; | |
518 } | |
519 } | |
520 break; | |
378 | 521 |
133 | 522 case PM_STRING: |
523 /* String parsing mode */ | |
378 | 524 if (isStart) |
525 { | |
133 | 526 /* Start of string, get delimiter */ |
527 tmpCh = c; | |
528 isStart = FALSE; | |
529 strPos = 0; | |
378 | 530 } |
531 else if (c == tmpCh) | |
532 { | |
133 | 533 /* End of string, set the value */ |
534 tmpStr[strPos] = 0; | |
378 | 535 |
536 switch (item->type) | |
537 { | |
538 case ITEM_HEX_TRIPLET: | |
539 *(item->v.val_int) = th_get_hex_triplet(tmpStr); | |
540 prevMode = parseMode; | |
541 parseMode = PM_NORMAL; | |
542 break; | |
543 case ITEM_STRING: | |
544 th_pstrcpy(item->v.val_str, tmpStr); | |
545 prevMode = parseMode; | |
546 parseMode = PM_NORMAL; | |
547 break; | |
548 case ITEM_STRING_LIST: | |
549 th_llist_append(item->v.list, th_strdup(tmpStr)); | |
550 prevMode = parseMode; | |
551 parseMode = PM_NEXT; | |
552 nextMode = PM_ARRAY; | |
553 break; | |
133 | 554 } |
378 | 555 |
556 } | |
557 else | |
558 { | |
133 | 559 /* Add character to string */ |
560 VADDCH(c) | |
561 else | |
562 { | |
563 /* Error! String too long! */ | |
564 th_cfg_error(f, | |
378 | 565 "String too long! Maximum is %d characters.", |
566 SET_MAX_BUF); | |
133 | 567 parseMode = PM_ERROR; |
568 } | |
569 } | |
570 | |
571 c = -1; | |
572 break; | |
573 | |
574 case PM_INT: | |
575 /* Integer parsing mode */ | |
378 | 576 if (isStart && item->type == ITEM_UINT && c == '-') |
577 { | |
133 | 578 /* Error! Negative values not allowed for unsigned ints */ |
579 th_cfg_error(f, | |
378 | 580 "Negative value specified for %s, unsigned value expected.", |
581 item->name); | |
133 | 582 parseMode = PM_ERROR; |
378 | 583 } |
584 else if (isStart && (c == '-' || c == '+')) | |
585 { | |
133 | 586 VADDCH(c) |
587 else | |
588 isError = TRUE; | |
378 | 589 } |
590 else if (th_isdigit(c)) | |
591 { | |
133 | 592 VADDCH(c) |
593 else | |
594 isError = TRUE; | |
378 | 595 } |
596 else if (VISEND(c)) | |
597 { | |
133 | 598 /* End of integer parsing mode */ |
599 tmpStr[strPos] = 0; | |
378 | 600 switch (item->type) |
601 { | |
133 | 602 case ITEM_INT: |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
603 *(item->v.val_int) = atoi(tmpStr); |
133 | 604 break; |
605 | |
606 case ITEM_UINT: | |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
607 *(item->v.val_uint) = atol(tmpStr); |
133 | 608 break; |
609 } | |
610 | |
611 prevMode = parseMode; | |
612 parseMode = PM_NORMAL; | |
378 | 613 } |
614 else | |
615 { | |
133 | 616 /* Error! Unexpected character. */ |
617 th_cfg_error(f, | |
378 | 618 "Unexpected character '%c' for integer setting '%s'.", |
619 c, item->name); | |
133 | 620 parseMode = PM_ERROR; |
621 } | |
622 | |
378 | 623 if (isError) |
624 { | |
133 | 625 /* Error! String too long! */ |
626 th_cfg_error(f, "String too long! Maximum is %d characters.", | |
378 | 627 SET_MAX_BUF); |
133 | 628 parseMode = PM_ERROR; |
629 } | |
630 | |
631 isStart = FALSE; | |
632 c = -1; | |
633 break; | |
634 | |
635 case PM_BOOL: | |
636 /* Boolean parsing mode */ | |
378 | 637 if (isStart) |
638 { | |
133 | 639 tmpCh = c; |
640 isStart = FALSE; | |
378 | 641 } |
642 else if (VISEND(c)) | |
643 { | |
201
f7b571debd81
Silence a warning when compiling with older versions of GCC.
Matti Hamalainen <ccr@tnsp.org>
parents:
146
diff
changeset
|
644 BOOL tmpBool = FALSE; |
378 | 645 |
133 | 646 /* End of boolean parsing */ |
378 | 647 switch (tmpCh) |
648 { | |
649 case 'Y': | |
650 case 'y': | |
651 case 'T': | |
652 case 't': | |
133 | 653 case '1': |
654 tmpBool = TRUE; | |
655 break; | |
378 | 656 |
657 case 'N': | |
658 case 'n': | |
659 case 'F': | |
660 case 'f': | |
133 | 661 case '0': |
662 tmpBool = FALSE; | |
663 break; | |
378 | 664 |
133 | 665 default: |
666 isError = TRUE; | |
667 } | |
378 | 668 |
669 if (isError) | |
670 { | |
671 th_cfg_error(f, "Invalid boolean value for '%s'.\n", | |
672 item->name); | |
133 | 673 parseMode = PM_ERROR; |
378 | 674 } |
675 else | |
676 { | |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
677 *(item->v.val_bool) = tmpBool; |
133 | 678 |
679 prevMode = parseMode; | |
680 parseMode = PM_NORMAL; | |
681 } | |
682 } | |
683 c = -1; | |
684 break; | |
685 } | |
686 } | |
687 | |
688 /* Check for validation errors */ | |
689 if (validError) | |
690 return 1; | |
691 | |
692 /* Return result */ | |
693 if (parseMode == PM_ERROR) | |
694 return -2; | |
695 else | |
696 return 0; | |
697 } | |
698 | |
699 | |
378 | 700 int th_cfg_read(FILE *inFile, char *filename, cfgitem_t *cfg) |
133 | 701 { |
702 conffile_t f; | |
378 | 703 |
133 | 704 f.file = inFile; |
705 f.filename = filename; | |
706 f.line = 1; | |
378 | 707 |
133 | 708 return th_cfg_read_sect(&f, cfg, 0); |
709 } | |
710 | |
711 | |
712 /* Write a configuration into file | |
713 */ | |
714 static void th_print_indent(conffile_t *f, int nesting) | |
715 { | |
716 int i; | |
717 for (i = 0; i < nesting * 2; i++) | |
718 fputc(' ', f->file); | |
719 } | |
720 | |
721 | |
722 static int th_cfg_write_sect(conffile_t *f, cfgitem_t *item, int nesting) | |
723 { | |
378 | 724 while (item != NULL) |
725 { | |
726 if (item->type == ITEM_COMMENT) | |
727 { | |
133 | 728 th_print_indent(f, nesting); |
378 | 729 if (fprintf |
730 (f->file, "# %s\n", | |
731 (item->name != NULL) ? item->name : "") < 0) | |
133 | 732 return -1; |
378 | 733 } |
734 else if (item->name != NULL) | |
735 { | |
133 | 736 th_print_indent(f, nesting); |
378 | 737 |
738 switch (item->type) | |
739 { | |
133 | 740 case ITEM_STRING: |
378 | 741 if (*(item->v.val_str) == NULL) |
742 { | |
133 | 743 if (fprintf(f->file, "#%s = \"\"\n", item->name) < 0) |
744 return -3; | |
378 | 745 } |
746 else | |
747 { | |
133 | 748 if (fprintf(f->file, "%s = \"%s\"\n", |
378 | 749 item->name, *(item->v.val_str)) < 0) |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
750 return -3; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
751 } |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
752 break; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
753 |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
754 case ITEM_STRING_LIST: |
378 | 755 if (*(item->v.list) == NULL) |
756 { | |
757 if (fprintf(f->file, "#%s = \"\", \"\"\n", item->name) < | |
758 0) | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
759 return -3; |
378 | 760 } |
761 else | |
762 { | |
261
0db02b8d2d11
ISO C99 compatibility fixes.
Matti Hamalainen <ccr@tnsp.org>
parents:
215
diff
changeset
|
763 qlist_t *node = *(item->v.list); |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
764 size_t n = th_llist_length(node); |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
765 if (fprintf(f->file, "%s = ", item->name) < 0) |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
766 return -3; |
378 | 767 |
768 while (node != NULL) | |
769 { | |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
770 if (node->data != NULL) |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
771 fprintf(f->file, "\"%s\"", (char *) node->data); |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
772 |
378 | 773 if (--n > 0) |
774 { | |
356
e694c02d6982
Make saved configuration files a bit more readable.
Matti Hamalainen <ccr@tnsp.org>
parents:
261
diff
changeset
|
775 fprintf(f->file, ",\n"); |
e694c02d6982
Make saved configuration files a bit more readable.
Matti Hamalainen <ccr@tnsp.org>
parents:
261
diff
changeset
|
776 th_print_indent(f, nesting); |
e694c02d6982
Make saved configuration files a bit more readable.
Matti Hamalainen <ccr@tnsp.org>
parents:
261
diff
changeset
|
777 } |
138
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
778 node = node->next; |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
779 } |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
780 |
3e221c16b087
Improvements in configuration file handing.
Matti Hamalainen <ccr@tnsp.org>
parents:
135
diff
changeset
|
781 if (fprintf(f->file, "\n") < 0) |
133 | 782 return -3; |
783 } | |
784 break; | |
785 | |
786 case ITEM_INT: | |
787 if (fprintf(f->file, "%s = %i\n", | |
378 | 788 item->name, *(item->v.val_int)) < 0) |
133 | 789 return -4; |
790 break; | |
791 | |
792 case ITEM_UINT: | |
793 if (fprintf(f->file, "%s = %d\n", | |
378 | 794 item->name, *(item->v.val_uint)) < 0) |
133 | 795 return -5; |
796 break; | |
797 | |
798 case ITEM_BOOL: | |
799 if (fprintf(f->file, "%s = %s\n", | |
378 | 800 item->name, |
801 *(item->v.val_bool) ? "yes" : "no") < 0) | |
133 | 802 return -6; |
803 break; | |
378 | 804 |
133 | 805 case ITEM_SECTION: |
806 { | |
378 | 807 int res; |
808 if (fprintf(f->file, "%s = {\n", item->name) < 0) | |
809 return -7; | |
810 res = th_cfg_write_sect(f, item->v.section, nesting + 1); | |
811 if (res != 0) | |
812 return res; | |
813 if (fprintf(f->file, "}\n\n") < 0) | |
814 return -8; | |
133 | 815 } |
816 break; | |
378 | 817 |
133 | 818 case ITEM_HEX_TRIPLET: |
819 if (fprintf(f->file, "%s = \"%06x\"\n", | |
378 | 820 item->name, *(item->v.val_int)) < 0) |
133 | 821 return -6; |
822 break; | |
823 } | |
824 } | |
825 item = item->next; | |
826 } | |
378 | 827 |
133 | 828 return 0; |
829 } | |
830 | |
831 | |
832 int th_cfg_write(FILE *outFile, char *filename, cfgitem_t *cfg) | |
833 { | |
834 conffile_t f; | |
378 | 835 |
133 | 836 if (cfg == NULL) |
837 return -1; | |
838 | |
839 f.file = outFile; | |
840 f.filename = filename; | |
841 f.line = 1; | |
378 | 842 |
843 fprintf(outFile, "# Configuration written by %s %s\n\n", | |
844 th_prog_fullname, th_prog_version); | |
845 | |
133 | 846 return th_cfg_write_sect(&f, cfg, 0); |
847 } |