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));
440 if (
qaconf == NULL || options == NULL) {
441 _seterrmsg(
qaconf,
"Invalid parameters.");
447 for (numopts = 0; options[numopts].name != NULL; numopts++)
453 size_t newsize =
sizeof(qaconf_option_t) * (
qaconf->numoptions + numopts);
454 qaconf->options = (qaconf_option_t *) realloc(
qaconf->options, newsize);
456 sizeof(qaconf_option_t) * numopts);
457 qaconf->numoptions += numopts;
508 qaconf->userdata = (
void *) userdata;
535static int parse(qaconf_t *
qaconf,
const char *filepath, uint8_t flags) {
537 FILE *fp = fopen(filepath,
"r");
539 _seterrmsg(
qaconf,
"Failed to open file '%s'.", filepath);
544 if (
qaconf->filepath != NULL)
546 qaconf->filepath = strdup(filepath);
550 int optcount = _parse_inline(
qaconf, fp, flags, QAC_SECTION_ROOT, NULL);
574 return (
const char*)
qaconf->errstr;
591 if (
qaconf->errstr != NULL) {
607 if (
qaconf->filepath != NULL)
609 if (
qaconf->errstr != NULL)
611 if (
qaconf->options != NULL)
618#define ARGV_INIT_SIZE (4)
619#define ARGV_INCR_STEP (8)
620#define MAX_TYPECHECK (5)
621static int _parse_inline(qaconf_t *
qaconf, FILE *fp, uint8_t flags,
622 enum qaconf_section sectionid,
623 qaconf_cbdata_t *cbdata_parent) {
625 int (*cmpfunc)(
const char *,
const char *) = strcmp;
626 if (flags & QAC_CASEINSENSITIVE)
627 cmpfunc = strcasecmp;
629 char buf[MAX_LINESIZE];
630 bool doneloop =
false;
631 bool exception =
false;
633 int newsectionid = 0;
634 void *freethis = NULL;
635 while (doneloop ==
false && exception ==
false) {
637#define EXITLOOP(fmt, args...) do { \
638 _seterrmsg(qaconf, "%s:%d " fmt, \
639 qaconf->filepath, qaconf->lineno, ##args); \
644 if (fgets(buf, MAX_LINESIZE, fp) == NULL) {
646 if (cbdata_parent != NULL) {
647 EXITLOOP(
"<%s> section was not closed.", cbdata_parent->argv[0]);
659 if (IS_EMPTY_STR(buf) || *buf ==
'#') {
663 DEBUG(
"%s (line=%d)", buf,
qaconf->lineno);
666 qaconf_cbdata_t *cbdata = (qaconf_cbdata_t*) malloc(
667 sizeof(qaconf_cbdata_t));
668 ASSERT(cbdata != NULL);
669 memset(cbdata,
'\0',
sizeof(qaconf_cbdata_t));
670 if (cbdata_parent != NULL) {
671 cbdata->section = sectionid;
672 cbdata->sections = cbdata_parent->sections | sectionid;
673 cbdata->level = cbdata_parent->level + 1;
674 cbdata->parent = cbdata_parent;
676 cbdata->section = sectionid;
677 cbdata->sections = sectionid;
679 cbdata->parent = NULL;
685 if (ENDING_CHAR(sp) !=
'>') {
686 EXITLOOP(
"Missing closing bracket. - '%s'.", buf);
691 cbdata->otype = QAC_OTYPE_SECTIONCLOSE;
694 cbdata->otype = QAC_OTYPE_SECTIONOPEN;
698 ENDING_CHAR(sp) =
'\0';
700 cbdata->otype = QAC_OTYPE_OPTION;
705 cbdata->data = strdup(sp);
706 ASSERT(cbdata->data != NULL);
711 bool doneparsing =
false;
712 for (wp1 = (
char *) cbdata->data; doneparsing ==
false; wp1 = wp2) {
714 if (argvsize == cbdata->argc) {
715 argvsize += (argvsize == 0) ? ARGV_INIT_SIZE : ARGV_INCR_STEP;
716 cbdata->argv = (
char**) realloc((
void *) cbdata->argv,
717 sizeof(
char*) * argvsize);
718 ASSERT(cbdata->argv != NULL);
722 for (; (*wp1 ==
' ' || *wp1 ==
'\t'); wp1++)
730 }
else if (*wp1 ==
'"') {
736 for (wp2 = wp1;; wp2++) {
740 }
else if (*wp2 ==
'\'') {
745 }
else if (*wp2 ==
'"') {
750 }
else if (*wp2 ==
'\\') {
752 size_t wordlen = wp2 - wp1;
754 memmove(wp1 + 1, wp1, wordlen);
758 }
else if (*wp2 ==
' ' || *wp2 ==
'\t') {
768 EXITLOOP(
"Quotation marks were not closed properly.");
772 cbdata->argv[cbdata->argc] = wp1;
774 DEBUG(
" argv[%d]=%s", cbdata->argc - 1, wp1);
783 if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
784 if (cbdata_parent == NULL
785 || cmpfunc(cbdata->argv[0], cbdata_parent->argv[0])) {
786 EXITLOOP(
"Trying to close <%s> section that wasn't opened.",
792 bool optfound =
false;
794 for (i = 0; optfound ==
false && i <
qaconf->numoptions; i++) {
795 qaconf_option_t *option = &
qaconf->options[i];
797 if (!cmpfunc(cbdata->argv[0], option->name)) {
799 if ((cbdata->otype != QAC_OTYPE_SECTIONCLOSE)
800 && (option->sections != QAC_SECTION_ALL)
801 && (option->sections & sectionid) == 0) {
802 EXITLOOP(
"Option '%s' is in wrong section.", option->name);
806 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
808 int numtake = option->take & QAC_TAKEALL;
809 if (numtake != QAC_TAKEALL
810 && numtake != (cbdata->argc - 1)) {
811 EXITLOOP(
"'%s' option takes %d arguments.",
812 option->name, numtake);
817 if (option->take & QAC_AA_INT)
819 else if (option->take & QAC_AA_FLOAT)
821 else if (option->take & QAC_AA_BOOL)
827 for (j = 1; j < cbdata->argc && j <= MAX_TYPECHECK; j++) {
829 if (option->take & (QAC_A1_INT << (j - 1)))
831 else if (option->take & (QAC_A1_FLOAT << (j - 1)))
833 else if (option->take & (QAC_A1_BOOL << (j - 1)))
840 if (_is_str_number(cbdata->argv[j]) != 1) {
842 "%dth argument of '%s' must be integer type.",
845 }
else if (argtype == 2) {
847 if (_is_str_number(cbdata->argv[j]) == 0) {
849 "%dth argument of '%s' must be floating-point type.",
852 }
else if (argtype == 3) {
854 if (_is_str_bool(cbdata->argv[j]) != 0) {
856 strcpy(cbdata->argv[j],
"1");
859 "%dth argument of '%s' must be bool type.",
868 qaconf_cb_t *usercb = option->cb;
871 if (usercb != NULL) {
872 char *cberrmsg = NULL;
874 if (cbdata->otype != QAC_OTYPE_SECTIONCLOSE) {
876 cberrmsg = usercb(cbdata,
qaconf->userdata);
881 ASSERT(cbdata_parent != NULL);
882 enum qaconf_otype orig_otype = cbdata_parent->otype;
883 cbdata_parent->otype = QAC_OTYPE_SECTIONCLOSE;
886 cberrmsg = usercb(cbdata_parent,
qaconf->userdata);
889 cbdata_parent->otype = orig_otype;
893 if (cberrmsg != NULL) {
895 EXITLOOP(
"%s", cberrmsg);
899 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
901 newsectionid = option->sectionid;
910 if (optfound ==
false) {
911 if (
qaconf->defcb != NULL) {
913 }
else if ((flags & QAC_IGNOREUNKNOWN) == 0) {
914 EXITLOOP(
"Unregistered option '%s'.", cbdata->argv[0]);
919 if (cbdata->otype == QAC_OTYPE_SECTIONOPEN) {
921 DEBUG(
"Entering next level %d.", cbdata->level+1);
922 int optcount2 = _parse_inline(
qaconf, fp, flags, newsectionid,
924 if (optcount2 >= 0) {
925 optcount += optcount2;
928 }DEBUG(
"Returned to previous level %d.", cbdata->level);
929 }
else if (cbdata->otype == QAC_OTYPE_SECTIONCLOSE) {
936 if (freethis != NULL) {
940 if (cbdata != NULL) {
941 _free_cbdata(cbdata);
945 if (exception ==
true) {
956 return (exception ==
false) ? optcount : -1;
959static void _seterrmsg(qaconf_t *
qaconf,
const char *format, ...) {
960 if (
qaconf->errstr != NULL)
962 DYNAMIC_VSPRINTF(
qaconf->errstr, format);
965static void _free_cbdata(qaconf_cbdata_t *cbdata) {
966 if (cbdata->argv != NULL)
968 if (cbdata->data != NULL)
976static int _is_str_number(
const char *s) {
977 char *op = (
char *) s;
983 for (cp = op, dp = NULL; *cp !=
'\0'; cp++) {
984 if (
'0' <= *cp && *cp <=
'9') {
1014static int _is_str_bool(
const char *s) {
1015 if (!strcasecmp(s,
"true"))
1017 else if (!strcasecmp(s,
"on"))
1019 else if (!strcasecmp(s,
"yes"))
1021 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 whitespace, including CR and LF, from both ends of a string.