195 #ifndef DISABLE_QACONF
204 #include "qinternal.h"
205 #include "utilities/qstring.h"
206 #include "extensions/qaconf.h"
208 #ifndef _DOXYGEN_SKIP
209 #define MAX_LINESIZE (1024*4)
215 static int parse(qaconf_t *
qaconf,
const char *filepath, uint8_t flags);
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);
243 qaconf_t *
qaconf = (qaconf_t *) malloc(
sizeof(qaconf_t));
248 memset((
void *) (qaconf),
'\0',
sizeof(qaconf_t));
254 qaconf->parse =
parse;
257 qaconf->free =
free_;
435 if (qaconf == NULL || options == NULL) {
436 _seterrmsg(qaconf,
"Invalid parameters.");
442 for (numopts = 0; options[numopts].name != NULL; numopts++)
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;
467 qaconf->defcb = callback;
503 qaconf->userdata = (
void *) userdata;
530 static int parse(qaconf_t *
qaconf,
const char *filepath, uint8_t flags) {
532 FILE *fp = fopen(filepath,
"r");
534 _seterrmsg(qaconf,
"Failed to open file '%s'.", filepath);
539 if (qaconf->filepath != NULL)
540 free(qaconf->filepath);
541 qaconf->filepath = strdup(filepath);
545 int optcount = _parse_inline(qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
569 return (
const char*) qaconf->errstr;
586 if (qaconf->errstr != NULL) {
587 free(qaconf->errstr);
588 qaconf->errstr = NULL;
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);
611 #ifndef _DOXYGEN_SKIP
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) {
620 int (*cmpfunc)(
const char *,
const char *) = strcmp;
621 if (flags & QAC_CASEINSENSITIVE)
622 cmpfunc = strcasecmp;
624 char buf[MAX_LINESIZE];
625 bool doneloop =
false;
626 bool exception =
false;
628 int newsectionid = 0;
629 void *freethis = NULL;
630 while (doneloop ==
false && exception ==
false) {
632 #define EXITLOOP(fmt, args...) do { \
633 _seterrmsg(qaconf, "%s:%d " fmt, \
634 qaconf->filepath, qaconf->lineno, ##args); \
639 if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
641 if (cbdata_parent != NULL) {
642 EXITLOOP(
"<%s> section was not closed.", cbdata_parent->argv[0]);
654 if (IS_EMPTY_STR(buf) || *buf ==
'#') {
658 DEBUG(
"%s (line=%d)", buf, qaconf->lineno);
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;
671 cbdata->section = sectionid;
672 cbdata->sections = sectionid;
674 cbdata->parent = NULL;
680 if (ENDING_CHAR(sp) !=
'>') {
681 EXITLOOP(
"Missing closing bracket. - '%s'.", buf);
686 cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
689 cbdata->otype = QAC_OTYPE_SECTIONOPEN;
693 ENDING_CHAR(sp) =
'\0';
695 cbdata->otype = QAC_OTYPE_OPTION;
700 cbdata->data = strdup(sp);
701 ASSERT(cbdata->data != NULL);
706 bool doneparsing =
false;
707 for (wp1 = (
char *) cbdata->data; doneparsing ==
false; wp1 = wp2) {
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);
717 for (; (*wp1 ==
' ' || *wp1 ==
'\t'); wp1++)
725 }
else if (*wp1 ==
'"') {
731 for (wp2 = wp1;; wp2++) {
735 }
else if (*wp2 ==
'\'') {
740 }
else if (*wp2 ==
'"') {
745 }
else if (*wp2 ==
'\\') {
747 size_t wordlen = wp2 - wp1;
749 memmove(wp1 + 1, wp1, wordlen);
753 }
else if (*wp2 ==
' ' || *wp2 ==
'\t') {
763 EXITLOOP(
"Quotation hasn't properly closed.");
767 cbdata->argv[cbdata->argc] = wp1;
769 DEBUG(
" argv[%d]=%s", cbdata->argc - 1, wp1);
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.",
787 bool optfound =
false;
789 for (i = 0; optfound ==
false && i < qaconf->numoptions; i++) {
790 qaconf_option_t *option = &qaconf->options[i];
792 if (!cmpfunc(cbdata->argv[0], option->name)) {
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);
801 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
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);
812 if (option->take & QAC_AA_INT)
814 else if (option->take & QAC_AA_FLOAT)
816 else if (option->take & QAC_AA_BOOL)
822 for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
824 if (option->take & (QAC_A1_INT << (j - 1)))
826 else if (option->take & (QAC_A1_FLOAT << (j - 1)))
828 else if (option->take & (QAC_A1_BOOL << (j - 1)))
835 if (_is_str_number(cbdata->argv[j]) != 1) {
837 "%dth argument of '%s' must be integer type.",
840 }
else if (argtype == 2) {
842 if (_is_str_number(cbdata->argv[j]) == 0) {
844 "%dth argument of '%s' must be floating point. type",
847 }
else if (argtype == 3) {
849 if (_is_str_bool(cbdata->argv[j]) != 0) {
851 strcpy(cbdata->argv[j],
"1");
854 "%dth argument of '%s' must be bool type.",
863 qaconf_cb_t *usercb = option->cb;
865 usercb = qaconf->defcb;
866 if (usercb != NULL) {
867 char *cberrmsg = NULL;
869 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
871 cberrmsg = usercb(cbdata, qaconf->userdata);
876 ASSERT(cbdata_parent != NULL);
877 enum qaconf_otype orig_otype = cbdata_parent->otype;
878 cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
881 cberrmsg = usercb(cbdata_parent, qaconf->userdata);
884 cbdata_parent->otype = orig_otype;
888 if (cberrmsg != NULL) {
890 EXITLOOP(
"%s", cberrmsg);
894 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
896 newsectionid = option->sectionid;
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]);
914 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
916 DEBUG(
"Entering next level %d.", cbdata->level+1);
917 int optcount2 = _parse_inline(qaconf, fp, flags, newsectionid,
919 if (optcount2 >= 0) {
920 optcount += optcount2;
923 }DEBUG(
"Returned to previous level %d.", cbdata->level);
924 }
else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
931 if (freethis != NULL) {
935 if (cbdata != NULL) {
936 _free_cbdata(cbdata);
940 if (exception ==
true) {
951 return (exception ==
false) ? optcount : -1;
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);
960 static void _free_cbdata(qaconf_cbdata_t *cbdata) {
961 if (cbdata->argv != NULL)
963 if (cbdata->data != NULL)
971 static int _is_str_number(
const char *s) {
972 char *op = (
char *) s;
978 for (cp = op, dp = NULL; *cp !=
'\0'; cp++) {
979 if (
'0' <= *cp && *cp <=
'9') {
1009 static int _is_str_bool(
const char *s) {
1010 if (!strcasecmp(s,
"true"))
1012 else if (!strcasecmp(s,
"on"))
1014 else if (!strcasecmp(s,
"yes"))
1016 else if (!strcasecmp(s,
"1"))
static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags)
qaconf_t->parse(): Run parser.
static void setuserdata(qaconf_t *qaconf, const void *userdata)
qaconf_t->setuserdata(): Set userdata which will be provided on callback.
static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options)
qaconf_t->addoptions(): Register option directives.
char * qstrtrim(char *str)
Remove white spaces(including CR, LF) from head and tail of the string.
static void free_(qaconf_t *qaconf)
qaconf_t->free(): Release resources.
qaconf_t * qaconf(void)
Create a new configuration object.
static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback)
Set default callback function.
static const char * errmsg(qaconf_t *qaconf)
qaconf_t->errmsg(): Get last error message.
static void reseterror(qaconf_t *qaconf)
qaconf_t->reseterror(): Clear error message.