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