qLibc
qaconf.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2015 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qaconf.c Apache-style configuration file parser.
31 *
32 * Apache-style Configuration is a configuration file syntax and format
33 * originally introduced by Apache HTTPd project. This format is powerful,
34 * flexible and human friendly. Even though this code gets distributed
35 * as a part of qLibc project, the code is written not to have any external
36 * dependencies to make this single file stands alone for better portability.
37 * It is purely written from the ground up and dedicated to the public
38 * by Seungyoung Kim.
39 *
40 * Sample Apache-style Configuration Syntax:
41 * @code
42 * # Lines that begin with the hash character "#" are considered comments.
43 * Listen 53
44 * Protocols UDP TCP
45 * IPSEC On
46 *
47 * <Domain "qdecoder.org">
48 * TTL 86400
49 * MX 10 mail.qdecoder.org
50 *
51 * <Host mail>
52 * IPv4 192.168.10.1
53 * TXT "US Rack-13D-18 \"San Jose's\""
54 * </Host>
55 *
56 * <Host www>
57 * IPv4 192.168.10.2
58 * TXT 'KR Rack-48H-31 "Seoul\'s"'
59 * TTL 3600
60 * </Host>
61 * </Domain>
62 *
63 * <Domain "ringfs.org">
64 * <Host www>
65 * CNAME www.qdecoder.org
66 * </Host>
67 * </Domain>
68 * @endcode
69 *
70 * @code
71 * // THIS EXAMPLE CODE CAN BE FOUND IN EXAMPLES DIRECTORY.
72 *
73 * // Define scope.
74 * // QAC_SCOPE_ALL and QAC_SCOPE_ROOT are predefined.
75 * // Custum scope should be defined from 2(1 << 1).
76 * // Note) These values are ORed(bit operation), so the number should be
77 * // 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
78 * enum {
79 * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
80 * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
81 * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
82 * OPT_SECTION_HOST = (1 << 2), // user-defined section
83 * };
84 *
85 * // Define callback proto-types.
86 * static QAC_CB(confcb_debug);
87 *
88 * // Define options and callbacks.
89 * static qaconf_option_t options[] = {
90 * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
91 * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
92 * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
93 * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
94 * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
95 * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
96 * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
97 * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
98 * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
99 * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
100 * QAC_OPTION_END
101 * };
102 *
103 * int user_main(void)
104 * {
105 * // Create a userdata structure.
106 * struct MyConf myconf;
107 *
108 * // Initialize and create a qaconf object.
109 * qaconf_t *conf = qaconf();
110 * if (conf == NULL) {
111 * printf("Failed to open '" CONF_PATH "'.\n");
112 * return -1;
113 * }
114 *
115 * // Register options.
116 * conf->addoptions(conf, options);
117 *
118 * // Set callback userdata
119 * // This is a userdata which will be provided on callback
120 * conf->setuserdata(conf, &myconf);
121 *
122 * // Run parser.
123 * int count = conf->parse(conf, CONF_PATH, QAC_CASEINSENSITIVE);
124 * if (count < 0) {
125 * printf("Error: %s\n", conf->errmsg(conf));
126 * } else {
127 * printf("Successfully loaded.\n");
128 * }
129 *
130 * // Verify userdata structure.
131 * if (conf->errmsg(conf) == NULL) { // another way to check parsing error.
132 * // codes here
133 * }
134 *
135 * // Release resources.
136 * conf->free(conf);
137 * }
138 *
139 * static QAC_CB(confcb_debug)
140 * {
141 * int i;
142 * for (i = 0; i < data->level; i++) {
143 * printf (" ");
144 * }
145 *
146 * // Print option name
147 * if (data->otype == QAC_OTYPE_SECTIONOPEN) {
148 * printf("<%s>", data->argv[0]);
149 * } else if (data->otype == QAC_OTYPE_SECTIONCLOSE) {
150 * printf("</%s>", data->argv[0]);
151 * } else { // This is QAC_OTYPE_OPTION type.
152 * printf("%s", data->argv[0]);
153 * }
154 *
155 * // Print parent names
156 * qaconf_cbdata_t *parent;
157 * for (parent = data->parent; parent != NULL; parent = parent->parent) {
158 * printf(" ::%s(%s)", parent->argv[0], parent->argv[1]);
159 * }
160 *
161 * // Print option arguments
162 * for (i = 1; i < data->argc; i++) {
163 * printf(" [%d:%s]", i, data->argv[i]);
164 * }
165 * printf("\n");
166 *
167 * // Return OK
168 * return NULL;
169 * }
170 * @endcode
171 *
172 * @code
173 * [Output]
174 * Listen [1:53]
175 * Protocols [1:UDP] [2:TCP]
176 * IPSEC [1:1]
177 * <Domain> [1:qdecoder.org]
178 * TTL ::Domain(qdecoder.org) [1:86400]
179 * MX ::Domain(qdecoder.org) [1:10] [2:mail.qdecoder.org]
180 * <Host> ::Domain(qdecoder.org) [1:mail]
181 * IPv4 ::Host(mail) ::Domain(qdecoder.org) [1:192.168.10.1]
182 * TXT ::Host(mail) ::Domain(qdecoder.org) [1:US Rack-13D-18 "San Jose's"]
183 * </Host> ::Domain(qdecoder.org) [1:mail]
184 * <Host> ::Domain(qdecoder.org) [1:www]
185 * IPv4 ::Host(www) ::Domain(qdecoder.org) [1:192.168.10.2]
186 * TXT ::Host(www) ::Domain(qdecoder.org) [1:KR Rack-48H-31 "Seoul's"]
187 * TTL ::Host(www) ::Domain(qdecoder.org) [1:3600]
188 * </Host> ::Domain(qdecoder.org) [1:www]
189 * </Domain> [1:qdecoder.org]
190 * <Domain> [1:ringfs.org]
191 * <Host> ::Domain(ringfs.org) [1:www]
192 * CNAME ::Host(www) ::Domain(ringfs.org) [1:www.qdecoder.org]
193 * </Host> ::Domain(ringfs.org) [1:www]
194 * </Domain> [1:ringfs.org]
195 * Successfully loaded.
196 * @endcode
197 */
198
199#ifndef DISABLE_QACONF
200
201#include <stdio.h>
202#include <stdlib.h>
203#include <stdbool.h>
204#include <stdarg.h>
205#include <string.h>
206#include <assert.h>
207#include <errno.h>
208#include "qinternal.h"
209#include "utilities/qstring.h"
210#include "extensions/qaconf.h"
211
212#ifndef _DOXYGEN_SKIP
213#define MAX_LINESIZE (1024*4)
214
215/* internal functions */
216static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options);
217static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback);
218static void setuserdata(qaconf_t *qaconf, const void *userdata);
219static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags);
220static const char *errmsg(qaconf_t *qaconf);
221static void reseterror(qaconf_t *qaconf);
222static void free_(qaconf_t *qaconf);
223
224static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
225 enum qaconf_section sectionid,
226 qaconf_cbdata_t *cbdata_parent);
227static void _seterrmsg(qaconf_t *qaconf, const char *format, ...);
228static void _free_cbdata(qaconf_cbdata_t *cbdata);
229static int _is_str_number(const char *s);
230static int _is_str_bool(const char *s);
231#endif
232
233/**
234 * Create a new configuration object.
235 *
236 * @return a pointer of new qaconf_t object.
237 *
238 * @code
239 * qaconf_t *conf = qaconf();
240 * if (conf == NULL) {
241 * // Insufficient memory.
242 * }
243 * @endcode
244 */
245qaconf_t *qaconf(void) {
246 // Malloc qaconf_t structure
247 qaconf_t *qaconf = (qaconf_t *) malloc(sizeof(qaconf_t));
248 if (qaconf == NULL)
249 return NULL;
250
251 // Initialize the structure
252 memset((void *) (qaconf), '\0', sizeof(qaconf_t));
253
254 // member methods
255 qaconf->addoptions = addoptions;
256 qaconf->setdefhandler = setdefhandler;
257 qaconf->setuserdata = setuserdata;
258 qaconf->parse = parse;
259 qaconf->errmsg = errmsg;
260 qaconf->reseterror = reseterror;
261 qaconf->free = free_;
262
263 return qaconf;
264}
265
266/**
267 * qaconf_t->addoptions(): Register option directives.
268 *
269 * @param qaconf qaconf_t object.
270 * @param options array pointer of qaconf_option_t.
271 *
272 * @return a number of options registered(added).
273 *
274 * @code
275 * qaconf_option_t options[] = {
276 * {"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
277 * {"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
278 * {"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
279 * {"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
280 * { "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
281 * { "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
282 * { "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
283 * { "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
284 * { "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
285 * { "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
286 * QAC_OPTION_END
287 * };
288 *
289 * // Register options.
290 * qaconf_t *conf = qaconf();
291 * conf->addoptions(conf, options);
292 * (...codes goes...)
293 * @endcode
294 *
295 * It takes an array of options as provided in the sample codes.
296 * Each option consists of 5 parameters as below
297 *
298 * @code
299 * 1st) Option Name : A option directive name.
300 * 2nd) Arguments : A number of arguments this option takes and their types.
301 * 3rd) Callback : A function pointer for callback.
302 * 4th) Section ID : Section ID if this option is a section like <Option>.
303 * Otherwise 0 for regular option.
304 * 5th) Sections : ORed section IDs where this option can be specified.
305 * @endcode
306 *
307 * Example:
308 *
309 * @code
310 * {"TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST}
311 * 1st) Option name is "TTL"
312 * 2nd) It takes 1 argument and its type must be integer.
313 * 3rd) Callback function, confcb_debug, will be called.
314 * 4th) This is a regular option and does not have section id.
315 * 5th) This option can be specified in OPT_SECTION_DOMAIN and OPT_SECTION_HOST.
316 * @endcode
317 *
318 * OPTION NAME field:
319 *
320 * Option name is a unique string. Even an option is section type like <option>
321 * only name part without bracket needs to be specifed.
322 *
323 * ARGUMENT field:
324 *
325 * This field is for providing argument checking in parser level. So in user's
326 * callback routine can go simple. This provides checking of number of arguments
327 * this option can take and those argument type.
328 *
329 * In terms of argument types. There are 4 argument types as below.
330 * And first 5 arguments can be checked individually with different types.
331 *
332 * @code
333 * STR type : any type
334 * INT type : integer type. ex) 23, -12, 0
335 * FLOAT type : integer + floating point type. ex) 1.32, -32.5, 23, -12, 0
336 * BOOL type : bool type ex) 1/0, true/false, on/off, yes/no
337 * @endcode
338 *
339 * When a BOOL type is specified, the argument passed to callback will be
340 * replaced to "1" or "0" for convenience use. For example, if "On" is specified
341 * as a argument and if BOOL type checking is specified, then actual argument
342 * which will be passed to callback will have "1". So we can simply determine it
343 * like "bool enabled = atoi(data->argv[1])".
344 *
345 * If original input argument needs to be passed to callback, specify STR type.
346 *
347 * Here is some examples of how to specify "Arguments" field.
348 *
349 * @code
350 * An option takes 1 argument.
351 * QAC_TAKE_STR <= String(any) type
352 * QAC_TAKE_INT <= Integer type
353 * QAC_TAKE_FLOAT <= Float type
354 * QAC_TAKE_BOOL <= Bool type
355 *
356 * QAC_TAKE1 <= Equavalent to QAC_TAKE_STR
357 * QAC_TAKE1 | QAC_A1_BOOL <= Equavalent to QAC_TAKE_BOOL
358 *
359 * An option takes 2 arguments, bool and float.
360 * QAC_TAKE2 | QAC_A1_BOOL | QAC_A2_FLOAT
361 *
362 * An option takes any number of arguments in any type.
363 * QAC_TAKEALL
364 *
365 * An option takes any number of arguments but 1st one must be bool and
366 * 2nd one must be integer and rest of them must be float.
367 * QAC_TAKEALL | QAC_A1_BOOL | QAC_A2_INT | QAC_AA_FLOAT
368 * @endcode
369 *
370 * CALLBACK field:
371 *
372 * User defined callback function. We provide a macro, QAC_CB, for function
373 * proto type. Always use QAC_CB macro.
374 *
375 * @code
376 * QAC_CB(sample_cb) {
377 * (...codes...)
378 * }
379 *
380 * is equavalent to
381 *
382 * char *sample_cb(qaconf_cbdata_t *data, void *userdata) {
383 * (...codes...)
384 * }
385 * @endcode
386 *
387 * Callback function will be called with 2 arguments. One is callback data and
388 * the other one is userdata. Userdata is the data pointer set by setuserdata().
389 *
390 * Here is data structure. Arguments belong to the option can be accessed via
391 * argv variables like data->argv[1]. argv[0] is for the option name.
392 *
393 * @code
394 * struct qaconf_cbdata_s {
395 * enum qaconf_otype otype; // option type
396 * uint64_t section; // current section where this option is located
397 * uint64_t sections; // ORed all parent's sectionid(s) including current sections
398 * uint8_t level; // number of parents(level), root level is 0
399 * qaconf_cbdata_t *parent; // upper parent link
400 *
401 * int argc; // number arguments. always equal or greater than 1.
402 * char **argv; // argument pointers. argv[0] is option name.
403 * }
404 * @endcode
405 *
406 * SECTION ID field:
407 *
408 * If an option is an section like <Option>, section id can be assigned.
409 * This section id can be used to limit some other option directives to be
410 * located only inside of that section. So this is your choice. If it doesn't
411 * require to check directory scope, we can just specify 0 here.
412 *
413 * There are 2 pre-defined section id, QAC_SECTION_ALL and QAC_SECTION_ROOT.
414 * When we define user section, it has to be defined from 2(1 << 1)as below.
415 *
416 * @code
417 * enum {
418 * OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
419 * OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
420 * OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
421 * OPT_SECTION_HOST = (1 << 2), // user-defined section
422 * };
423 * @endcode
424 *
425 * Please note that this section IDs are ORed. So the section id should be
426 * assigned in bit operation manner as 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
427 *
428 * SECTION IDS field:
429 *
430 * This field is to limit the scope where an option is allowed to be specified.
431 * Multiple section IDs can be ORed.
432 *
433 * QAC_SECTION_ALL means an option can be appeared in anywhere.
434 *
435 * QAC_SECTION_ROOT means an option can be appeared only in top level and not
436 * inside of any sections.
437 */
438static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options) {
439 if (qaconf == NULL || options == NULL) {
440 _seterrmsg(qaconf, "Invalid parameters.");
441 return -1;
442 }
443
444 // Count a number of options
445 uint32_t numopts;
446 for (numopts = 0; options[numopts].name != NULL; numopts++)
447 ;
448 if (numopts == 0)
449 return 0;
450
451 // Realloc
452 size_t newsize = sizeof(qaconf_option_t) * (qaconf->numoptions + numopts);
453 qaconf->options = (qaconf_option_t *) realloc(qaconf->options, newsize);
454 memcpy(&qaconf->options[qaconf->numoptions], options,
455 sizeof(qaconf_option_t) * numopts);
456 qaconf->numoptions += numopts;
457
458 return numopts;
459}
460
461/**
462 * Set default callback function.
463 *
464 * Default callback function will be called for unregistered option directives.
465 * QAC_IGNOREUNKNOWN flag will be ignored when default callback has set.
466 *
467 * @param qaconf qaconf_t object.
468 * @param callback callback function pointer
469 */
470static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback) {
471 qaconf->defcb = callback;
472}
473
474/**
475 * qaconf_t->setuserdata(): Set userdata which will be provided on callback.
476 *
477 * @param qaconf qaconf_t object.
478 * @param userdata a pointer of userdata.
479 *
480 * @code
481 * // Define an example userdata
482 * struct MyConf {
483 * int sample;
484 * };
485 *
486 * int user_main(void) {
487 * struct MyConf myconf;
488 *
489 * (...codes...)
490 *
491 * // Set callback userdata.
492 * conf->setuserdata(conf, &myconf);
493 * (...codes...)
494 * }
495 *
496 * QAC_CB(confcb_callback_func) {
497 * (...codes...)
498 * // Type casting userdata for convenient use.
499 * struct MyConf *myconf = (struct MyConf *)userdata;
500 * myconf->sample++;
501 * (...codes...)
502 * return NULL;
503 * }
504 * @endcode
505 */
506static void setuserdata(qaconf_t *qaconf, const void *userdata) {
507 qaconf->userdata = (void *) userdata;
508}
509
510/**
511 * qaconf_t->parse(): Run parser.
512 *
513 * @param qaconf qaconf_t object.
514 * @param filepath configuration file path.
515 * @param flags parser options. (0 for default)
516 *
517 * @return A number of option directives parsed. -1 will be returned in case of
518 * error.
519 *
520 * Here is a list of flags. Multiple flags can be ORed.
521 *
522 * QAC_CASEINSENSITIVE: Option name is case-insensitive.
523 *
524 * QAC_IGNOREUNKNOWN : Ignore unknown option directives.
525 * This flag will be ignored if setdefhandler() has set.
526 *
527 * @code
528 * int c;
529 * c = conf->parse(conf, "sm1.conf", 0);
530 * c = conf->parse(conf, "sm2.conf", QAC_CASEINSENSITIVE);
531 * c = conf->parse(conf, "sm3.conf", QAC_CASEINSENSITIVE | QAC_IGNOREUNKNOWN);
532 * @endcode
533 */
534static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags) {
535 // Open file
536 FILE *fp = fopen(filepath, "r");
537 if (fp == NULL) {
538 _seterrmsg(qaconf, "Failed to open file '%s'.", filepath);
539 return -1;
540 }
541
542 // Set info
543 if (qaconf->filepath != NULL)
544 free(qaconf->filepath);
545 qaconf->filepath = strdup(filepath);
546 qaconf->lineno = 0;
547
548 // Parse
549 int optcount = _parse_inline(qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
550
551 // Clean up
552 fclose(fp);
553
554 return optcount;
555}
556
557/**
558 * qaconf_t->errmsg(): Get last error message.
559 *
560 * @param qaconf qaconf_t object.
561 *
562 * @return A const pointer of error message string.
563 *
564 * @code
565 * int c = conf->parse(conf, "sample.conf", 0);
566 * if (c < 0) {
567 * // ERROR
568 * printf("%s\n", conf->errmsg(conf));
569 * }
570 * @endcode
571 */
572static const char *errmsg(qaconf_t *qaconf) {
573 return (const char*) qaconf->errstr;
574}
575
576/**
577 * qaconf_t->reseterror(): Clear error message.
578 *
579 * @param qaconf qaconf_t object.
580 *
581 * @code
582 * conf->reseterror(conf);
583 * conf->parse(conf, "sample.conf", 0);
584 * if (conf->errmsg(conf) != NULL) {
585 * // ERROR
586 * }
587 * @endcode
588 */
589static void reseterror(qaconf_t *qaconf) {
590 if (qaconf->errstr != NULL) {
591 free(qaconf->errstr);
592 qaconf->errstr = NULL;
593 }
594}
595
596/**
597 * qaconf_t->free(): Release resources.
598 *
599 * @param qaconf qaconf_t object.
600 *
601 * @code
602 * conf->free(conf);
603 * @endcode
604 */
605static void free_(qaconf_t *qaconf) {
606 if (qaconf->filepath != NULL)
607 free(qaconf->filepath);
608 if (qaconf->errstr != NULL)
609 free(qaconf->errstr);
610 if (qaconf->options != NULL)
611 free(qaconf->options);
612 free(qaconf);
613}
614
615#ifndef _DOXYGEN_SKIP
616
617#define ARGV_INIT_SIZE (4)
618#define ARGV_INCR_STEP (8)
619#define MAX_TYPECHECK (5)
620static int _parse_inline(qaconf_t *qaconf, FILE *fp, uint8_t flags,
621 enum qaconf_section sectionid,
622 qaconf_cbdata_t *cbdata_parent) {
623 // Assign compare function.
624 int (*cmpfunc)(const char *, const char *) = strcmp;
625 if (flags & QAC_CASEINSENSITIVE)
626 cmpfunc = strcasecmp;
627
628 char buf[MAX_LINESIZE];
629 bool doneloop = false;
630 bool exception = false;
631 int optcount = 0; // number of option entry processed.
632 int newsectionid = 0; // temporary store
633 void *freethis = NULL; // userdata to free
634 while (doneloop == false && exception == false) {
635
636#define EXITLOOP(fmt, args...) do { \
637 _seterrmsg(qaconf, "%s:%d " fmt, \
638 qaconf->filepath, qaconf->lineno, ##args); \
639 exception = true; \
640 goto exitloop; \
641} while (0);
642
643 if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
644 // Check if section was opened and never closed
645 if (cbdata_parent != NULL) {
646 EXITLOOP("<%s> section was not closed.", cbdata_parent->argv[0]);
647 }
648 break;
649 }
650
651 // Increase line number counter
652 qaconf->lineno++;
653
654 // Trim white spaces
655 qstrtrim(buf);
656
657 // Skip blank like and comments.
658 if (IS_EMPTY_STR(buf) || *buf == '#') {
659 continue;
660 }
661
662 DEBUG("%s (line=%d)", buf, qaconf->lineno);
663
664 // Create a callback data
665 qaconf_cbdata_t *cbdata = (qaconf_cbdata_t*) malloc(
666 sizeof(qaconf_cbdata_t));
667 ASSERT(cbdata != NULL);
668 memset(cbdata, '\0', sizeof(qaconf_cbdata_t));
669 if (cbdata_parent != NULL) {
670 cbdata->section = sectionid;
671 cbdata->sections = cbdata_parent->sections | sectionid;
672 cbdata->level = cbdata_parent->level + 1;
673 cbdata->parent = cbdata_parent;
674 } else {
675 cbdata->section = sectionid;
676 cbdata->sections = sectionid;
677 cbdata->level = 0;
678 cbdata->parent = NULL;
679 }
680
681 // Escape section option
682 char *sp = buf;
683 if (*sp == '<') {
684 if (ENDING_CHAR(sp) != '>') {
685 EXITLOOP("Missing closing bracket. - '%s'.", buf);
686 }
687
688 sp++;
689 if (*sp == '/') {
690 cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
691 sp++;
692 } else {
693 cbdata->otype = QAC_OTYPE_SECTIONOPEN;
694 }
695
696 // Remove tailing bracket
697 ENDING_CHAR(sp) = '\0';
698 } else {
699 cbdata->otype = QAC_OTYPE_OPTION;
700 }
701
702 // Brackets has removed at this point
703 // Copy data into cbdata buffer.
704 cbdata->data = strdup(sp);
705 ASSERT(cbdata->data != NULL);
706
707 // Parse and tokenize.
708 int argvsize = 0;
709 char *wp1, *wp2;
710 bool doneparsing = false;
711 for (wp1 = (char *) cbdata->data; doneparsing == false; wp1 = wp2) {
712 // Allocate/Realloc argv array
713 if (argvsize == cbdata->argc) {
714 argvsize += (argvsize == 0) ? ARGV_INIT_SIZE : ARGV_INCR_STEP;
715 cbdata->argv = (char**) realloc((void *) cbdata->argv,
716 sizeof(char*) * argvsize);
717 ASSERT(cbdata->argv != NULL);
718 }
719
720 // Skip whitespaces
721 for (; (*wp1 == ' ' || *wp1 == '\t'); wp1++)
722 ;
723
724 // Quote handling
725 int qtmark = 0; // 1 for singlequotation, 2 for doublequotation
726 if (*wp1 == '\'') {
727 qtmark = 1;
728 wp1++;
729 } else if (*wp1 == '"') {
730 qtmark = 2;
731 wp1++;
732 }
733
734 // Parse a word
735 for (wp2 = wp1;; wp2++) {
736 if (*wp2 == '\0') {
737 doneparsing = true;
738 break;
739 } else if (*wp2 == '\'') {
740 if (qtmark == 1) {
741 qtmark = 0;
742 break;
743 }
744 } else if (*wp2 == '"') {
745 if (qtmark == 2) {
746 qtmark = 0;
747 break;
748 }
749 } else if (*wp2 == '\\') {
750 if (qtmark > 0) {
751 size_t wordlen = wp2 - wp1;
752 if (wordlen > 0)
753 memmove(wp1 + 1, wp1, wordlen);
754 wp1++;
755 wp2++;
756 }
757 } else if (*wp2 == ' ' || *wp2 == '\t') {
758 if (qtmark == 0)
759 break;
760 }
761 }
762 *wp2 = '\0';
763 wp2++;
764
765 // Check quotations has paired.
766 if (qtmark > 0) {
767 EXITLOOP("Quotation hasn't properly closed.");
768 }
769
770 // Store a argument
771 cbdata->argv[cbdata->argc] = wp1;
772 cbdata->argc++;
773 DEBUG(" argv[%d]=%s", cbdata->argc - 1, wp1);
774
775 // For quoted string, this case can be happened.
776 if (*wp2 == '\0') {
777 doneparsing = true;
778 }
779 }
780
781 // Check mismatch sectionclose
782 if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
783 if (cbdata_parent == NULL
784 || cmpfunc(cbdata->argv[0], cbdata_parent->argv[0])) {
785 EXITLOOP("Trying to close <%s> section that wasn't opened.",
786 cbdata->argv[0]);
787 }
788 }
789
790 // Find matching option
791 bool optfound = false;
792 int i;
793 for (i = 0; optfound == false && i < qaconf->numoptions; i++) {
794 qaconf_option_t *option = &qaconf->options[i];
795
796 if (!cmpfunc(cbdata->argv[0], option->name)) {
797 // Check sections
798 if ((cbdata->otype != QAC_OTYPE_SECTIONCLOSE)
799 && (option->sections != QAC_SECTION_ALL)
800 && (option->sections & sectionid) == 0) {
801 EXITLOOP("Option '%s' is in wrong section.", option->name);
802 }
803
804 // Check argument types
805 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
806 // Check number of arguments
807 int numtake = option->take & QAC_TAKEALL;
808 if (numtake != QAC_TAKEALL
809 && numtake != (cbdata->argc - 1)) {
810 EXITLOOP("'%s' option takes %d arguments.",
811 option->name, numtake);
812 }
813
814 // Check argument types
815 int deftype; // 0:str, 1:int, 2:float, 3:bool
816 if (option->take & QAC_AA_INT)
817 deftype = 1;
818 else if (option->take & QAC_AA_FLOAT)
819 deftype = 2;
820 else if (option->take & QAC_AA_BOOL)
821 deftype = 3;
822 else
823 deftype = 0;
824
825 int j;
826 for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
827 int argtype;
828 if (option->take & (QAC_A1_INT << (j - 1)))
829 argtype = 1;
830 else if (option->take & (QAC_A1_FLOAT << (j - 1)))
831 argtype = 2;
832 else if (option->take & (QAC_A1_BOOL << (j - 1)))
833 argtype = 3;
834 else
835 argtype = deftype;
836
837 if (argtype == 1) {
838 // integer type
839 if (_is_str_number(cbdata->argv[j]) != 1) {
840 EXITLOOP(
841 "%dth argument of '%s' must be integer type.",
842 j, option->name);
843 }
844 } else if (argtype == 2) {
845 // floating point type
846 if (_is_str_number(cbdata->argv[j]) == 0) {
847 EXITLOOP(
848 "%dth argument of '%s' must be floating point. type",
849 j, option->name);
850 }
851 } else if (argtype == 3) {
852 // bool type
853 if (_is_str_bool(cbdata->argv[j]) != 0) {
854 // Change argument to "1".
855 strcpy(cbdata->argv[j], "1");
856 } else {
857 EXITLOOP(
858 "%dth argument of '%s' must be bool type.",
859 j, option->name);
860 }
861 }
862 }
863 }
864
865 // Callback
866 //DEBUG("Callback %s", option->name);
867 qaconf_cb_t *usercb = option->cb;
868 if (usercb == NULL)
869 usercb = qaconf->defcb;
870 if (usercb != NULL) {
871 char *cberrmsg = NULL;
872
873 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
874 // Normal option and sectionopen
875 cberrmsg = usercb(cbdata, qaconf->userdata);
876 } else {
877 // QAC_OTYPE_SECTIONCLOSE
878
879 // Change otype
880 ASSERT(cbdata_parent != NULL);
881 enum qaconf_otype orig_otype = cbdata_parent->otype;
882 cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
883
884 // Callback
885 cberrmsg = usercb(cbdata_parent, qaconf->userdata);
886
887 // Restore type
888 cbdata_parent->otype = orig_otype;
889 }
890
891 // Error handling
892 if (cberrmsg != NULL) {
893 freethis = cberrmsg;
894 EXITLOOP("%s", cberrmsg);
895 }
896 }
897
898 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
899 // Store it for later
900 newsectionid = option->sectionid;
901 }
902
903 // Set found flag
904 optfound = true;
905 }
906 }
907
908 // If not found.
909 if (optfound == false) {
910 if (qaconf->defcb != NULL) {
911 qaconf->defcb(cbdata, qaconf->userdata);
912 } else if ((flags & QAC_IGNOREUNKNOWN) == 0) {
913 EXITLOOP("Unregistered option '%s'.", cbdata->argv[0]);
914 }
915 }
916
917 // Section handling
918 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
919 // Enter recursive call
920 DEBUG("Entering next level %d.", cbdata->level+1);
921 int optcount2 = _parse_inline(qaconf, fp, flags, newsectionid,
922 cbdata);
923 if (optcount2 >= 0) {
924 optcount += optcount2;
925 } else {
926 exception = true;
927 }DEBUG("Returned to previous level %d.", cbdata->level);
928 } else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
929 // Leave recursive call
930 doneloop = true;
931 }
932
933 exitloop:
934 // Release resources
935 if (freethis != NULL) {
936 free(freethis);
937 }
938
939 if (cbdata != NULL) {
940 _free_cbdata(cbdata);
941 cbdata = NULL;
942 }
943
944 if (exception == true) {
945 break;
946 }
947
948 // Go up and down
949 // if (otype
950
951 // Increase process counter
952 optcount++;
953 }
954
955 return (exception == false) ? optcount : -1;
956}
957
958static void _seterrmsg(qaconf_t *qaconf, const char *format, ...) {
959 if (qaconf->errstr != NULL)
960 free(qaconf->errstr);
961 DYNAMIC_VSPRINTF(qaconf->errstr, format);
962}
963
964static void _free_cbdata(qaconf_cbdata_t *cbdata) {
965 if (cbdata->argv != NULL)
966 free(cbdata->argv);
967 if (cbdata->data != NULL)
968 free(cbdata->data);
969 free(cbdata);
970}
971
972// return 2 for floating point .
973// return 1 for integer
974// return 0 for non number
975static int _is_str_number(const char *s) {
976 char *op = (char *) s;
977 if (*op == '-') {
978 op++;
979 }
980
981 char *cp, *dp;
982 for (cp = op, dp = NULL; *cp != '\0'; cp++) {
983 if ('0' <= *cp && *cp <= '9') {
984 continue;
985 }
986
987 if (*cp == '.') {
988 if (cp == op)
989 return 0; // dot can't be at the beginning.
990 if (dp != NULL)
991 return 0; // dot can't be appeared more than once.
992 dp = cp;
993 continue;
994 }
995
996 return 0;
997 }
998
999 if (cp == op) {
1000 return 0; // empty string
1001 }
1002
1003 if (dp != NULL) {
1004 if (dp + 1 == cp)
1005 return 0; // dot can't be at the end.
1006 return 2; // float point
1007 }
1008
1009 // integer
1010 return 1;
1011}
1012
1013static int _is_str_bool(const char *s) {
1014 if (!strcasecmp(s, "true"))
1015 return 1;
1016 else if (!strcasecmp(s, "on"))
1017 return 1;
1018 else if (!strcasecmp(s, "yes"))
1019 return 1;
1020 else if (!strcasecmp(s, "1"))
1021 return 1;
1022 return 0;
1023}
1024
1025#endif /* _DOXYGEN_SKIP */
1026
1027#endif /* DISABLE_QACONF */
1028
static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags)
qaconf_t->parse(): Run parser.
Definition qaconf.c:534
static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback)
Set default callback function.
Definition qaconf.c:470
static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options)
qaconf_t->addoptions(): Register option directives.
Definition qaconf.c:438
qaconf_t * qaconf(void)
Create a new configuration object.
Definition qaconf.c:245
static void setuserdata(qaconf_t *qaconf, const void *userdata)
qaconf_t->setuserdata(): Set userdata which will be provided on callback.
Definition qaconf.c:506
static void reseterror(qaconf_t *qaconf)
qaconf_t->reseterror(): Clear error message.
Definition qaconf.c:589
static void free_(qaconf_t *qaconf)
qaconf_t->free(): Release resources.
Definition qaconf.c:605
static const char * errmsg(qaconf_t *qaconf)
qaconf_t->errmsg(): Get last error message.
Definition qaconf.c:572
char * qstrtrim(char *str)
Remove white spaces(including CR, LF) from head and tail of the string.
Definition qstring.c:55