199#ifndef DISABLE_QACONF
208#include "qinternal.h"
209#include "utilities/qstring.h"
210#include "extensions/qaconf.h"
213#define MAX_LINESIZE (1024*4)
219static int parse(qaconf_t *
qaconf,
const char *filepath, uint8_t flags);
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);
247 qaconf_t *
qaconf = (qaconf_t *) malloc(
sizeof(qaconf_t));
252 memset((
void *) (
qaconf),
'\0',
sizeof(qaconf_t));
439 if (
qaconf == NULL || options == NULL) {
440 _seterrmsg(
qaconf,
"Invalid parameters.");
446 for (numopts = 0; options[numopts].name != NULL; numopts++)
452 size_t newsize =
sizeof(qaconf_option_t) * (
qaconf->numoptions + numopts);
453 qaconf->options = (qaconf_option_t *) realloc(
qaconf->options, newsize);
455 sizeof(qaconf_option_t) * numopts);
456 qaconf->numoptions += numopts;
507 qaconf->userdata = (
void *) userdata;
534static int parse(qaconf_t *
qaconf,
const char *filepath, uint8_t flags) {
536 FILE *fp = fopen(filepath,
"r");
538 _seterrmsg(
qaconf,
"Failed to open file '%s'.", filepath);
543 if (
qaconf->filepath != NULL)
545 qaconf->filepath = strdup(filepath);
549 int optcount = _parse_inline(
qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
573 return (
const char*)
qaconf->errstr;
590 if (
qaconf->errstr != NULL) {
606 if (
qaconf->filepath != NULL)
608 if (
qaconf->errstr != NULL)
610 if (
qaconf->options != NULL)
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) {
624 int (*cmpfunc)(
const char *,
const char *) = strcmp;
625 if (flags & QAC_CASEINSENSITIVE)
626 cmpfunc = strcasecmp;
628 char buf[MAX_LINESIZE];
629 bool doneloop =
false;
630 bool exception =
false;
632 int newsectionid = 0;
633 void *freethis = NULL;
634 while (doneloop ==
false && exception ==
false) {
636#define EXITLOOP(fmt, args...) do { \
637 _seterrmsg(qaconf, "%s:%d " fmt, \
638 qaconf->filepath, qaconf->lineno, ##args); \
643 if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
645 if (cbdata_parent != NULL) {
646 EXITLOOP(
"<%s> section was not closed.", cbdata_parent->argv[0]);
658 if (IS_EMPTY_STR(buf) || *buf ==
'#') {
662 DEBUG(
"%s (line=%d)", buf,
qaconf->lineno);
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;
675 cbdata->section = sectionid;
676 cbdata->sections = sectionid;
678 cbdata->parent = NULL;
684 if (ENDING_CHAR(sp) !=
'>') {
685 EXITLOOP(
"Missing closing bracket. - '%s'.", buf);
690 cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
693 cbdata->otype = QAC_OTYPE_SECTIONOPEN;
697 ENDING_CHAR(sp) =
'\0';
699 cbdata->otype = QAC_OTYPE_OPTION;
704 cbdata->data = strdup(sp);
705 ASSERT(cbdata->data != NULL);
710 bool doneparsing =
false;
711 for (wp1 = (
char *) cbdata->data; doneparsing ==
false; wp1 = wp2) {
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);
721 for (; (*wp1 ==
' ' || *wp1 ==
'\t'); wp1++)
729 }
else if (*wp1 ==
'"') {
735 for (wp2 = wp1;; wp2++) {
739 }
else if (*wp2 ==
'\'') {
744 }
else if (*wp2 ==
'"') {
749 }
else if (*wp2 ==
'\\') {
751 size_t wordlen = wp2 - wp1;
753 memmove(wp1 + 1, wp1, wordlen);
757 }
else if (*wp2 ==
' ' || *wp2 ==
'\t') {
767 EXITLOOP(
"Quotation hasn't properly closed.");
771 cbdata->argv[cbdata->argc] = wp1;
773 DEBUG(
" argv[%d]=%s", cbdata->argc - 1, wp1);
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.",
791 bool optfound =
false;
793 for (i = 0; optfound ==
false && i <
qaconf->numoptions; i++) {
794 qaconf_option_t *option = &
qaconf->options[i];
796 if (!cmpfunc(cbdata->argv[0], option->name)) {
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);
805 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
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);
816 if (option->take & QAC_AA_INT)
818 else if (option->take & QAC_AA_FLOAT)
820 else if (option->take & QAC_AA_BOOL)
826 for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
828 if (option->take & (QAC_A1_INT << (j - 1)))
830 else if (option->take & (QAC_A1_FLOAT << (j - 1)))
832 else if (option->take & (QAC_A1_BOOL << (j - 1)))
839 if (_is_str_number(cbdata->argv[j]) != 1) {
841 "%dth argument of '%s' must be integer type.",
844 }
else if (argtype == 2) {
846 if (_is_str_number(cbdata->argv[j]) == 0) {
848 "%dth argument of '%s' must be floating point. type",
851 }
else if (argtype == 3) {
853 if (_is_str_bool(cbdata->argv[j]) != 0) {
855 strcpy(cbdata->argv[j],
"1");
858 "%dth argument of '%s' must be bool type.",
867 qaconf_cb_t *usercb = option->cb;
870 if (usercb != NULL) {
871 char *cberrmsg = NULL;
873 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
875 cberrmsg = usercb(cbdata,
qaconf->userdata);
880 ASSERT(cbdata_parent != NULL);
881 enum qaconf_otype orig_otype = cbdata_parent->otype;
882 cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
885 cberrmsg = usercb(cbdata_parent,
qaconf->userdata);
888 cbdata_parent->otype = orig_otype;
892 if (cberrmsg != NULL) {
894 EXITLOOP(
"%s", cberrmsg);
898 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
900 newsectionid = option->sectionid;
909 if (optfound ==
false) {
910 if (
qaconf->defcb != NULL) {
912 }
else if ((flags & QAC_IGNOREUNKNOWN) == 0) {
913 EXITLOOP(
"Unregistered option '%s'.", cbdata->argv[0]);
918 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
920 DEBUG(
"Entering next level %d.", cbdata->level+1);
921 int optcount2 = _parse_inline(
qaconf, fp, flags, newsectionid,
923 if (optcount2 >= 0) {
924 optcount += optcount2;
927 }DEBUG(
"Returned to previous level %d.", cbdata->level);
928 }
else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
935 if (freethis != NULL) {
939 if (cbdata != NULL) {
940 _free_cbdata(cbdata);
944 if (exception ==
true) {
955 return (exception ==
false) ? optcount : -1;
958static void _seterrmsg(qaconf_t *
qaconf,
const char *format, ...) {
959 if (
qaconf->errstr != NULL)
961 DYNAMIC_VSPRINTF(
qaconf->errstr, format);
964static void _free_cbdata(qaconf_cbdata_t *cbdata) {
965 if (cbdata->argv != NULL)
967 if (cbdata->data != NULL)
975static int _is_str_number(
const char *s) {
976 char *op = (
char *) s;
982 for (cp = op, dp = NULL; *cp !=
'\0'; cp++) {
983 if (
'0' <= *cp && *cp <=
'9') {
1013static int _is_str_bool(
const char *s) {
1014 if (!strcasecmp(s,
"true"))
1016 else if (!strcasecmp(s,
"on"))
1018 else if (!strcasecmp(s,
"yes"))
1020 else if (!strcasecmp(s,
"1"))
static int parse(qaconf_t *qaconf, const char *filepath, uint8_t flags)
qaconf_t->parse(): Run parser.
static void setdefhandler(qaconf_t *qaconf, qaconf_cb_t *callback)
Set default callback function.
static int addoptions(qaconf_t *qaconf, const qaconf_option_t *options)
qaconf_t->addoptions(): Register option directives.
qaconf_t * qaconf(void)
Create a new configuration object.
static void setuserdata(qaconf_t *qaconf, const void *userdata)
qaconf_t->setuserdata(): Set userdata which will be provided on callback.
static void reseterror(qaconf_t *qaconf)
qaconf_t->reseterror(): Clear error message.
static void free_(qaconf_t *qaconf)
qaconf_t->free(): Release resources.
static const char * errmsg(qaconf_t *qaconf)
qaconf_t->errmsg(): Get last error message.
char * qstrtrim(char *str)
Remove white spaces(including CR, LF) from head and tail of the string.