diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index 94ad4bac..efbd69eb 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -846,8 +846,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) for (i = 0; i < QSE_COUNTOF(loc_xcfg_items); i++) { - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, loc_xcfg_items[i].x); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, loc_xcfg_items[i].y); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, loc_xcfg_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, loc_xcfg_items[i].y); if (pair && pair->val->type == QSE_XLI_STR) { cfg->xcfg[i] = qse_httpd_strntombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr, ((qse_xli_str_t*)pair->val)->len); @@ -860,8 +860,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) } } - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("index")); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.index")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("index")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.index")); if (pair && pair->val->type == QSE_XLI_STR) { qse_char_t* duptmp; @@ -886,8 +886,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) cfg->index.count = count; } - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("cgi")); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.cgi")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("cgi")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.cgi")); if (pair && pair->val->type == QSE_XLI_LIST) { /* TODO: more sanity check... this can be done with xli schema... if supported */ @@ -963,8 +963,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) } } - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("auth-rule")); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.auth-rule")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("auth-rule")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.auth-rule")); if (pair && pair->val->type == QSE_XLI_LIST) { qse_xli_list_t* auth_rule_list = (qse_xli_list_t*)pair->val; @@ -1015,8 +1015,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) } } - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("mime")); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.mime")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("mime")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.mime")); if (pair && pair->val->type == QSE_XLI_LIST) { qse_xli_list_t* mimelist = (qse_xli_list_t*)pair->val; @@ -1072,8 +1072,8 @@ static int load_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, loccfg_t* cfg) for (i = 0; i < 2; i++) { - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, loc_acc_items[i].x); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, loc_acc_items[i].y); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, loc_acc_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, loc_acc_items[i].y); if (pair && pair->val->type == QSE_XLI_LIST) { qse_xli_list_t* acclist = (qse_xli_list_t*)pair->val; @@ -1208,8 +1208,8 @@ static int load_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server, q { qse_xli_pair_t* pair; - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, scfg_items[i].x); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, scfg_items[i].y); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, scfg_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, scfg_items[i].y); if (pair && pair->val->type == QSE_XLI_STR) { server_xtn->scfg[i] = qse_httpd_strntombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr, ((qse_xli_str_t*)pair->val)->len); @@ -1223,7 +1223,7 @@ static int load_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server, q } /* load host/location specific configuration */ - host_count = qse_xli_getnumpairsbyalias (httpd_xtn->xli, list, QSE_T("host")); + host_count = qse_xli_getnumpairsbyname (httpd_xtn->xli, list, QSE_T("host")); if (host_count <= 0) return 0; /* nothing to load */ QSE_ASSERT (server_xtn->cfgtab == QSE_NULL); @@ -1246,12 +1246,12 @@ static int load_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server, q qse_char_t buf[32]; qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("host[%d]"), i); - host = qse_xli_findpairbyalias (httpd_xtn->xli, list, buf); + host = qse_xli_findpairbyname (httpd_xtn->xli, list, buf); if (!host) break; if (host->val->type == QSE_XLI_LIST && host->alias) { - loc_count = qse_xli_getnumpairsbyalias (httpd_xtn->xli, (qse_xli_list_t*)host->val, QSE_T("location")); + loc_count = qse_xli_getnumpairsbyname (httpd_xtn->xli, (qse_xli_list_t*)host->val, QSE_T("location")); if (((hostcfg = qse_httpd_callocmem (httpd, QSE_SIZEOF(*hostcfg))) == QSE_NULL) || ((hostcfg->hostname = qse_httpd_strtombsdup (httpd, (host->alias[0] == QSE_T('\0')? QSE_T("*"):host->alias))) == QSE_NULL)) goto oops; @@ -1263,7 +1263,7 @@ static int load_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server, q j--; qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("location[%d]"), j); - loc = qse_xli_findpairbyalias (httpd_xtn->xli, (qse_xli_list_t*)host->val, buf); + loc = qse_xli_findpairbyname (httpd_xtn->xli, (qse_xli_list_t*)host->val, buf); if (!loc) break; if (loc->val->type == QSE_XLI_LIST && loc->alias) @@ -1335,7 +1335,7 @@ static qse_httpd_server_t* attach_server (qse_httpd_t* httpd, int num, qse_xli_l httpd_xtn = qse_httpd_getxtnstd (httpd); - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("bind")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("bind")); if (pair == QSE_NULL || pair->val->type != QSE_XLI_STR) { /* TOOD: logging */ @@ -1351,8 +1351,8 @@ static qse_httpd_server_t* attach_server (qse_httpd_t* httpd, int num, qse_xli_l return QSE_NULL; } - pair = qse_xli_findpairbyalias (httpd_xtn->xli, list, QSE_T("ssl")); - if (!pair) pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.ssl")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("ssl")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default.ssl")); if (pair && pair->val->type == QSE_XLI_STR && qse_strxcmp (((qse_xli_str_t*)pair->val)->ptr, ((qse_xli_str_t*)pair->val)->len, QSE_T("yes")) == 0) dope.flags |= QSE_HTTPD_SERVER_SECURE; @@ -1440,7 +1440,7 @@ static void set_limit (qse_httpd_t* httpd, const qse_char_t* name, int what) httpd_xtn = (httpd_xtn_t*)qse_httpd_getxtnstd (httpd); - pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, name); + pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, name); if (pair && pair->val->type == QSE_XLI_STR) { #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) @@ -1479,7 +1479,7 @@ static int load_config (qse_httpd_t* httpd) if (open_config_file (httpd) <= -1) goto oops; - pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("name")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("name")); if (pair && pair->val->type == QSE_XLI_STR) { qse_mchar_t* tmp; @@ -1499,7 +1499,7 @@ static int load_config (qse_httpd_t* httpd) { qse_char_t buf[32]; qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("server[%d]"), i); - pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, buf); + pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, buf); if (pair == QSE_NULL) break; if (pair->val->type != QSE_XLI_LIST) @@ -1529,7 +1529,7 @@ static int load_config (qse_httpd_t* httpd) } /* load the global default */ - pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, QSE_T("server-default")); + pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("server-default")); if (pair && pair->val->type == QSE_XLI_LIST) { if (load_loccfg (httpd, (qse_xli_list_t*)pair->val, &httpd_xtn->dflcfg) <= -1) @@ -1563,7 +1563,7 @@ static void reconf_server (qse_httpd_t* httpd, qse_httpd_server_t* server) { qse_char_t buf[32]; qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("server[%d]"), server_xtn->num); - pair = qse_xli_findpairbyalias (httpd_xtn->xli, QSE_NULL, buf); + pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, buf); if (pair && pair->val->type == QSE_XLI_LIST) { diff --git a/qse/cmd/xli/xli.c b/qse/cmd/xli/xli.c index c233148e..d05d4465 100644 --- a/qse/cmd/xli/xli.c +++ b/qse/cmd/xli/xli.c @@ -130,6 +130,7 @@ static void print_usage (QSE_FILE* out, int argc, qse_char_t* argv[]) qse_fprintf (out, QSE_T(" --version show version\n")); qse_fprintf (out, QSE_T(" -i file specify an input file\n")); qse_fprintf (out, QSE_T(" -o file specify an output file\n")); + qse_fprintf (out, QSE_T(" -u disallow duplicate keys\n")); qse_fprintf (out, QSE_T(" -a allow a key alias\n")); qse_fprintf (out, QSE_T(" -f keep file inclusion info\n")); qse_fprintf (out, QSE_T(" -t keep comment text\n")); @@ -159,9 +160,9 @@ static int handle_args (int argc, qse_char_t* argv[]) static qse_opt_t opt = { #if defined(QSE_BUILD_DEBUG) - QSE_T("hi:o:aftm:X:"), + QSE_T("hi:o:uaftvm:X:"), #else - QSE_T("hi:o:aftm:"), + QSE_T("hi:o:uaftvm:"), #endif lng }; @@ -203,6 +204,10 @@ static int handle_args (int argc, qse_char_t* argv[]) g_output_file = opt.arg; break; + case QSE_T('u'): + g_trait |= QSE_XLI_KEYNODUP; + break; + case QSE_T('a'): g_trait |= QSE_XLI_KEYALIAS; break; @@ -215,6 +220,10 @@ static int handle_args (int argc, qse_char_t* argv[]) g_trait |= QSE_XLI_KEEPTEXT; break; + case QSE_T('v'): + g_trait |= QSE_XLI_VALIDATE; + break; + case QSE_T('m'): g_memlimit = qse_strtoulong (opt.arg); break; @@ -352,6 +361,12 @@ static int xli_main (int argc, qse_char_t* argv[]) in.u.file.path = g_input_file; in.u.file.cmgr = g_infile_cmgr; +{ +qse_xli_scm_t scm; +scm.flags = QSE_XLI_SCM_VAL_LIST | QSE_XLI_SCM_KEY_NODUP; +qse_xli_setschema (xli, QSE_T("a.b"), &scm); +} + if (qse_xli_readstd (xli, &in) <= -1) { const qse_xli_loc_t* errloc; @@ -384,7 +399,7 @@ static int xli_main (int argc, qse_char_t* argv[]) if (g_lookup_key) { qse_xli_pair_t* pair; - pair = qse_xli_findpairbyalias (xli, QSE_NULL, g_lookup_key); + pair = qse_xli_findpairbyname (xli, QSE_NULL, g_lookup_key); if (pair == QSE_NULL) { qse_fprintf (QSE_STDERR, diff --git a/qse/include/qse/xli/xli.h b/qse/include/qse/xli/xli.h index 033d031b..00525017 100644 --- a/qse/include/qse/xli/xli.h +++ b/qse/include/qse/xli/xli.h @@ -49,7 +49,10 @@ enum qse_xli_errnum_t QSE_XLI_EINCLSTR,/**< '@include' not followed by a string */ QSE_XLI_ELXCHR, /**< invalid character '${0} */ QSE_XLI_EXKWNR, /**< @word '${0}' not recognized */ - QSE_XLI_EXKWEM /**< @ not followed by a valid word */ + QSE_XLI_EXKWEM, /**< @ not followed by a valid word */ + QSE_XLI_EILKEY, /**< illegal key '${0}' */ + QSE_XLI_EILVAL, /**< illegal value for '${0}' */ + QSE_XLI_ESTRSEG /**< too many string segments for '${0}' */ }; typedef enum qse_xli_errnum_t qse_xli_errnum_t; @@ -78,6 +81,8 @@ enum qse_xli_trait_t QSE_XLI_KEEPTEXT = (1 << 3), /**< keep comment text */ QSE_XLI_KEEPFILE = (1 << 4), /**< keep inclusion file info */ + + QSE_XLI_VALIDATE = (1 << 5) }; typedef enum qse_xli_trait_t qse_xli_trait_t; @@ -287,11 +292,30 @@ typedef qse_ssize_t (*qse_xli_io_impl_t) ( qse_size_t count ); + +enum qse_xli_scm_flag_t +{ + QSE_XLI_SCM_REQUIRED = (1 << 0), + QSE_XLI_SCM_VAL_NIL = (1 << 1), + QSE_XLI_SCM_VAL_STR = (1 << 2), + QSE_XLI_SCM_VAL_LIST = (1 << 3), + QSE_XLI_SCM_KEY_NODUP = (1 << 4), + QSE_XLI_SCM_KEY_ALIAS = (1 << 5) +}; + +struct qse_xli_scm_t +{ + int flags; + int str_minseg; + int str_maxseg; +}; + +typedef struct qse_xli_scm_t qse_xli_scm_t; + #if defined(__cplusplus) extern "C" { #endif - QSE_EXPORT qse_xli_t* qse_xli_open ( qse_mmgr_t* mmgr, qse_size_t xtnsize @@ -518,20 +542,16 @@ QSE_EXPORT qse_xli_eof_t* qse_xli_inserteof ( qse_xli_atom_t* peer ); -QSE_EXPORT qse_xli_list_t* qse_xli_getroot ( - qse_xli_t* xli -); - -QSE_EXPORT qse_xli_pair_t* qse_xli_findpairbyalias ( +QSE_EXPORT qse_xli_pair_t* qse_xli_findpairbyname ( qse_xli_t* xli, const qse_xli_list_t* list, - const qse_char_t* alias + const qse_char_t* dotted_name ); -QSE_EXPORT qse_size_t qse_xli_getnumpairsbyalias ( +QSE_EXPORT qse_size_t qse_xli_getnumpairsbyname ( qse_xli_t* xli, const qse_xli_list_t* list, - const qse_char_t* alias + const qse_char_t* dotted_name ); /** @@ -558,10 +578,28 @@ qse_char_t* qse_xli_dupflatstr ( qse_size_t* nsegs ); +QSE_EXPORT qse_xli_list_t* qse_xli_getroot ( + qse_xli_t* xli +); + +QSE_EXPORT void qse_xli_clearroot ( + qse_xli_t* xli +); + +QSE_EXPORT void qse_xli_clearschema ( + qse_xli_t* xli +); + QSE_EXPORT void qse_xli_clear ( qse_xli_t* xli ); +QSE_EXPORT int qse_xli_setschema ( + qse_xli_t* xli, + const qse_char_t* dotted_name, + const qse_xli_scm_t* scm +); + QSE_EXPORT int qse_xli_read ( qse_xli_t* xli, qse_xli_io_impl_t io diff --git a/qse/lib/cmn/time.c b/qse/lib/cmn/time.c index 75e04867..bb863c27 100644 --- a/qse/lib/cmn/time.c +++ b/qse/lib/cmn/time.c @@ -156,6 +156,7 @@ int qse_gettime (qse_ntime_t* t) struct timeval tv; int n; + /* TODO: consider using clock_gettime() if it's avaialble.. -lrt may be needed */ n = QSE_GETTIMEOFDAY (&tv, QSE_NULL); if (n == -1) return -1; @@ -227,13 +228,12 @@ int qse_settime (const qse_ntime_t* t) tv.tv_usec = QSE_NSEC_TO_USEC(t->nsec); /* -#if defined CLOCK_REALTIME && HAVE_CLOCK_SETTIME +#if defined(CLOCK_REALTIME) && defined(HAVE_CLOCK_SETTIME) { int r = clock_settime (CLOCK_REALTIME, ts); - if (r == 0 || errno == EPERM) - return r; + if (r == 0 || errno == EPERM) return r; } -#elif HAVE_STIME +#elif defined(HAVE_STIME) return stime (&ts->tv_sec); #else */ diff --git a/qse/lib/xli/err.c b/qse/lib/xli/err.c index bea0cdd7..7084908b 100644 --- a/qse/lib/xli/err.c +++ b/qse/lib/xli/err.c @@ -48,7 +48,10 @@ const qse_char_t* qse_xli_dflerrstr ( QSE_T("'@include' not followed by a string"), QSE_T("invalid character '${0}'"), QSE_T("'${0}' not recognized"), - QSE_T("@ not followed by a valid word") + QSE_T("@ not followed by a valid word"), + QSE_T("illegal key '${0}'"), + QSE_T("illegal value for '${0}'"), + QSE_T("too many string segments for '${0}'") }; return (errnum >= 0 && errnum < QSE_COUNTOF(errstr))? diff --git a/qse/lib/xli/read.c b/qse/lib/xli/read.c index 3842f3bb..1ee6f7df 100644 --- a/qse/lib/xli/read.c +++ b/qse/lib/xli/read.c @@ -594,10 +594,17 @@ static int get_token (qse_xli_t* xli) static int read_pair (qse_xli_t* xli) { - qse_char_t* key = QSE_NULL; - qse_char_t* name = QSE_NULL; + qse_xstr_t key; + qse_xli_loc_t kloc; + qse_char_t* name; qse_xli_pair_t* pair; qse_xli_list_t* parlist; + qse_size_t dotted_curkey_len; + qse_xli_scm_t* scm = QSE_NULL; + + key.ptr = QSE_NULL; + name = QSE_NULL; + dotted_curkey_len = (qse_size_t)-1; parlist = xli->parlink->list; @@ -606,6 +613,7 @@ static int read_pair (qse_xli_t* xli) qse_xli_atom_t* atom; /* find any key conflicts in the current scope */ + /* TODO: optimization. no sequential search */ atom = parlist->tail; while (atom) { @@ -620,18 +628,47 @@ static int read_pair (qse_xli_t* xli) } } - key = qse_strdup (QSE_STR_PTR(xli->tok.name), xli->mmgr); - if (key == QSE_NULL) + kloc = xli->tok.loc; + key.len = QSE_STR_LEN(xli->tok.name); + key.ptr = qse_strdup (QSE_STR_PTR(xli->tok.name), xli->mmgr); + if (key.ptr == QSE_NULL) { qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); goto oops; } + dotted_curkey_len = QSE_STR_LEN (xli->dotted_curkey); + if ((dotted_curkey_len > 0 && qse_str_cat (xli->dotted_curkey, QSE_T(".")) == (qse_size_t)-1) || + qse_str_cat (xli->dotted_curkey, key.ptr) == (qse_size_t)-1) + { + qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); + goto oops; + } + + + if (xli->opt.trait & QSE_XLI_VALIDATE) + { + qse_rbt_pair_t* pair; + + pair = qse_rbt_search (xli->schema, QSE_STR_PTR(xli->dotted_curkey), QSE_STR_LEN(xli->dotted_curkey)); + if (pair == QSE_NULL) + { + qse_xli_seterror (xli, QSE_XLI_EILKEY, (const qse_cstr_t*)&key, &kloc); + goto oops; + } + + scm = (qse_xli_scm_t*)QSE_RBT_VPTR(pair); + + if (scm->flags & QSE_XLI_SCM_KEY_NODUP) + { + } + } + xli->tok_status |= TOK_STATUS_ENABLE_NSTR; if (get_token (xli) <= -1) goto oops; - if (xli->opt.trait & QSE_XLI_KEYALIAS) + if (xli->opt.trait & QSE_XLI_KEYALIAS) { /* the name part must be unique for the same key(s) */ if (MATCH (xli, TOK_IDENT) || MATCH (xli, TOK_DQSTR) || MATCH (xli, TOK_SQSTR) || MATCH(xli, TOK_NSTR)) @@ -643,7 +680,7 @@ static int read_pair (qse_xli_t* xli) { if (atom->type == QSE_XLI_PAIR && ((qse_xli_pair_t*)atom)->alias && - qse_strcmp (((qse_xli_pair_t*)atom)->key, key) == 0 && + qse_strcmp (((qse_xli_pair_t*)atom)->key, key.ptr) == 0 && qse_strcmp (((qse_xli_pair_t*)atom)->alias, QSE_STR_PTR(xli->tok.name)) == 0) { qse_xli_seterror (xli, QSE_XLI_EEXIST, QSE_STR_CSTR(xli->tok.name), &xli->tok.loc); @@ -670,11 +707,20 @@ static int read_pair (qse_xli_t* xli) if (MATCH (xli, TOK_SQSTR) || MATCH (xli, TOK_DQSTR) || MATCH(xli, TOK_NSTR) || MATCH (xli, TOK_IDENT)) { qse_xli_str_t* curstrseg; + qse_size_t segcount = 0; + + if (scm && !(scm->flags & QSE_XLI_SCM_VAL_STR)) + { + /* check the value type */ + qse_xli_seterror (xli, QSE_XLI_EILVAL, (const qse_cstr_t*)&key, &kloc); + goto oops; + } /* add a new pair with the initial string segment */ - pair = qse_xli_insertpairwithstr (xli, parlist, QSE_NULL, key, name, QSE_STR_CSTR(xli->tok.name)); + pair = qse_xli_insertpairwithstr (xli, parlist, QSE_NULL, key.ptr, name, QSE_STR_CSTR(xli->tok.name)); if (pair == QSE_NULL) goto oops; + segcount++; curstrseg = (qse_xli_str_t*)pair->val; if (get_token (xli) <= -1) goto oops; @@ -691,11 +737,11 @@ static int read_pair (qse_xli_t* xli) goto oops; } - /* add an additional segment to the string */ curstrseg = qse_xli_addsegtostr (xli, curstrseg, QSE_STR_CSTR(xli->tok.name)); if (curstrseg == QSE_NULL) goto oops; + segcount++; if (get_token (xli) <= -1) goto oops; /* skip the value */ } while (MATCH (xli, TOK_COMMA)); @@ -708,6 +754,13 @@ static int read_pair (qse_xli_t* xli) goto oops; } + if (scm && (segcount < scm->str_minseg || segcount > scm->str_maxseg)) + { + /* too many string segments for the key */ + qse_xli_seterror (xli, QSE_XLI_ESTRSEG, (const qse_cstr_t*)&key, &kloc); + goto oops; + } + if (get_token (xli) <= -1) goto oops; } else @@ -716,13 +769,23 @@ static int read_pair (qse_xli_t* xli) goto oops; } + + /* TODO: check against schema */ + } else if (MATCH (xli, TOK_LBRACE)) { + if (scm && !(scm->flags & QSE_XLI_SCM_VAL_LIST)) + { + /* check the value type */ + qse_xli_seterror (xli, QSE_XLI_EILVAL, (const qse_cstr_t*)&key, &kloc); + goto oops; + } + xli->tok_status &= ~TOK_STATUS_ENABLE_NSTR; /* insert a pair with an empty list */ - pair = qse_xli_insertpairwithemptylist (xli, parlist, QSE_NULL, key, name); + pair = qse_xli_insertpairwithemptylist (xli, parlist, QSE_NULL, key.ptr, name); if (pair == QSE_NULL) goto oops; if (read_list (xli, (qse_xli_list_t*)pair->val) <= -1) goto oops; @@ -741,17 +804,28 @@ static int read_pair (qse_xli_t* xli) /* skip the semicolon */ if (get_token (xli) <= -1) goto oops; } + + /* TODO: check against schema */ } else if (MATCH (xli, TOK_SEMICOLON)) { + if (scm && !(scm->flags & QSE_XLI_SCM_VAL_NIL)) + { + /* check the value type */ + qse_xli_seterror (xli, QSE_XLI_EILVAL, (const qse_cstr_t*)&key, &kloc); + goto oops; + } + xli->tok_status &= ~TOK_STATUS_ENABLE_NSTR; /* no value has been specified for the pair */ - pair = qse_xli_insertpair (xli, parlist, QSE_NULL, key, name, (qse_xli_val_t*)&xli->xnil); + pair = qse_xli_insertpair (xli, parlist, QSE_NULL, key.ptr, name, (qse_xli_val_t*)&xli->xnil); if (pair == QSE_NULL) goto oops; /* skip the semicolon */ if (get_token (xli) <= -1) goto oops; + + /* TODO: check against schema */ } else { @@ -760,13 +834,16 @@ static int read_pair (qse_xli_t* xli) } QSE_MMGR_FREE (xli->mmgr, name); - QSE_MMGR_FREE (xli->mmgr, key); + QSE_MMGR_FREE (xli->mmgr, key.ptr); + qse_str_setlen (xli->dotted_curkey, dotted_curkey_len); return 0; oops: xli->tok_status &= ~TOK_STATUS_ENABLE_NSTR; if (name) QSE_MMGR_FREE (xli->mmgr, name); - if (key) QSE_MMGR_FREE (xli->mmgr, key); + if (key.ptr) QSE_MMGR_FREE (xli->mmgr, key.ptr); + if (dotted_curkey_len != (qse_size_t)-1) + qse_str_setlen (xli->dotted_curkey, dotted_curkey_len); return -1; } @@ -894,6 +971,8 @@ int qse_xli_read (qse_xli_t* xli, qse_xli_io_impl_t io) qse_xli_seterrnum (xli, QSE_XLI_ENOERR, QSE_NULL); qse_xli_clearrionames (xli); + QSE_ASSERT (QSE_STR_LEN(xli->dotted_curkey) == 0); + n = xli->rio.impl (xli, QSE_XLI_IO_OPEN, xli->rio.inp, QSE_NULL, 0); if (n <= -1) { @@ -915,6 +994,7 @@ int qse_xli_read (qse_xli_t* xli, qse_xli_io_impl_t io) QSE_ASSERT (xli->rio.inp == &xli->rio.top); close_current_stream (xli); + qse_str_clear (xli->tok.name); return 0; oops: @@ -935,5 +1015,6 @@ oops: } close_current_stream (xli); + qse_str_clear (xli->tok.name); return -1; } diff --git a/qse/lib/xli/xli.c b/qse/lib/xli/xli.c index a1fd2824..99bd7a50 100644 --- a/qse/lib/xli/xli.c +++ b/qse/lib/xli/xli.c @@ -56,26 +56,35 @@ int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr) xli->mmgr = mmgr; xli->errstr = qse_xli_dflerrstr; + xli->dotted_curkey = qse_str_open (mmgr, 0, 128); + if (xli->dotted_curkey == QSE_NULL) goto oops; + xli->tok.name = qse_str_open (mmgr, 0, 128); if (xli->tok.name == QSE_NULL) goto oops; + xli->schema = qse_rbt_open (mmgr, 0, QSE_SIZEOF(qse_char_t), 1); + if (xli->schema == QSE_NULL) goto oops; + xli->root.type = QSE_XLI_LIST; xli->xnil.type = QSE_XLI_NIL; + return 0; oops: qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); + if (xli->schema) qse_rbt_close (xli->schema); if (xli->tok.name) qse_str_close (xli->tok.name); + if (xli->dotted_curkey) qse_str_close (xli->dotted_curkey); return -1; } void qse_xli_fini (qse_xli_t* xli) { qse_xli_clear (xli); - qse_str_close (xli->tok.name); - qse_xli_clearrionames (xli); - qse_xli_clearwionames (xli); + qse_rbt_close (xli->schema); + qse_str_close (xli->tok.name); + qse_str_close (xli->dotted_curkey); } qse_mmgr_t* qse_xli_getmmgr (qse_xli_t* xli) @@ -156,13 +165,6 @@ void qse_xli_freemem (qse_xli_t* xli, void* ptr) } /* ------------------------------------------------------ */ -qse_xli_list_t* qse_xli_getroot (qse_xli_t* xli) -{ - return &xli->root; -} - -/* ------------------------------------------------------ */ - static void insert_atom ( qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, qse_xli_atom_t* atom) @@ -375,9 +377,31 @@ static void free_list (qse_xli_t* xli, qse_xli_list_t* list) void qse_xli_clear (qse_xli_t* xli) { free_list (xli, &xli->root); + qse_rbt_clear (xli->schema); + + qse_xli_seterrnum (xli, QSE_XLI_ENOERR, QSE_NULL); + qse_xli_clearrionames (xli); + qse_xli_clearwionames (xli); } -static qse_size_t count_pair_byalias ( +qse_xli_list_t* qse_xli_getroot (qse_xli_t* xli) +{ + return &xli->root; +} + +void qse_xli_clearroot (qse_xli_t* xli) +{ + free_list (xli, &xli->root); +} + +void qse_xli_clearschema (qse_xli_t* xli) +{ + qse_rbt_clear (xli->schema); +} + +/* ------------------------------------------------------ */ + +static qse_size_t count_pairs_by_key_and_alias ( qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, const qse_cstr_t* alias) { @@ -404,7 +428,7 @@ static qse_size_t count_pair_byalias ( return count; } -static qse_xli_pair_t* find_pair_byalias ( +static qse_xli_pair_t* find_pair_by_key_and_alias ( qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, const qse_cstr_t* alias) { @@ -430,7 +454,7 @@ static qse_xli_pair_t* find_pair_byalias ( return QSE_NULL; } -static qse_xli_pair_t* find_pair_byindex ( +static qse_xli_pair_t* find_pair_by_key_and_index ( qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, qse_size_t index) { @@ -457,7 +481,7 @@ static qse_xli_pair_t* find_pair_byindex ( return QSE_NULL; } -qse_xli_pair_t* qse_xli_findpairbyalias (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* alias) +qse_xli_pair_t* qse_xli_findpairbyname (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* dotted_name) { const qse_char_t* ptr; const qse_xli_list_t* curlist; @@ -466,11 +490,11 @@ qse_xli_pair_t* qse_xli_findpairbyalias (qse_xli_t* xli, const qse_xli_list_t* l curlist = list? list: &xli->root; - ptr = alias; + ptr = dotted_name; while (1) { seg.ptr = ptr; - while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[')) ptr++; + while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[') && *ptr != QSE_T('{')) ptr++; if (ptr == seg.ptr) goto inval; seg.len = ptr - seg.ptr; @@ -479,14 +503,14 @@ qse_xli_pair_t* qse_xli_findpairbyalias (qse_xli_t* xli, const qse_xli_list_t* l /* check the type of curlist. this check is needed * because of the unconditional switching at the bottom of the * this loop. this implementation strategy has been chosen - * to provide the segment alias easily. */ + * to provide the segment name easily. */ goto noent; } if (*ptr == QSE_T('[')) { /* index is specified */ - ptr++; + ptr++; /* skip [ */ if (QSE_ISDIGIT(*ptr)) { @@ -501,50 +525,13 @@ qse_xli_pair_t* qse_xli_findpairbyalias (qse_xli_t* xli, const qse_xli_list_t* l if (*ptr != QSE_T(']')) goto inval; - pair = find_pair_byindex (xli, curlist, &seg, index); + pair = find_pair_by_key_and_index (xli, curlist, &seg, index); if (pair == QSE_NULL) { seg.len += count + 2; /* adjustment for error message */ goto noent; } } - else if (QSE_ISALPHA(*ptr)) - { - /* word index */ - qse_cstr_t idx; - - idx.ptr = ptr; - do ptr++; while (QSE_ISALNUM(*ptr) || *ptr == QSE_T('_') || *ptr == QSE_T('-')); - idx.len = ptr - idx.ptr; - - if (*ptr != QSE_T(']')) goto inval; - - pair = find_pair_byalias (xli, curlist, &seg, &idx); - if (pair == QSE_NULL) - { - seg.len += idx.len + 2; /* adjustment for error message */ - goto noent; - } - } - else if (*ptr == QSE_T('\'') || *ptr == QSE_T('\"')) - { - qse_cstr_t idx; - qse_char_t cc = *ptr++; - - idx.ptr = ptr; - do ptr++; while (*ptr != cc && *ptr != QSE_T('\0')); - idx.len = ptr - idx.ptr; - - if (*ptr != cc) goto inval; - if (*++ptr != QSE_T(']')) goto inval; - - pair = find_pair_byalias (xli, curlist, &seg, &idx); - if (pair == QSE_NULL) - { - seg.len += idx.len + 4; /* adjustment for error message */ - goto noent; - } - } else goto inval; ptr++; /* skip ] */ @@ -552,9 +539,37 @@ qse_xli_pair_t* qse_xli_findpairbyalias (qse_xli_t* xli, const qse_xli_list_t* l if (*ptr == QSE_T('\0')) break; /* no more segments */ else if (*ptr != QSE_T('.')) goto inval; } + else if (*ptr == QSE_T('{')) + { + /* word index - alias */ + qse_cstr_t idx; + + ptr++; /* skip { */ + + /* no escaping is supported for the alias inside {}. + * so if your alias contains these characters (in a quoted string), + * you can't reference it using a dotted key name. */ + idx.ptr = ptr; + while (*ptr != QSE_T('}') && *ptr != QSE_T('\0')) ptr++; + idx.len = ptr - idx.ptr; + + if (*ptr != QSE_T('}') || idx.len == 0) goto inval; + + pair = find_pair_by_key_and_alias (xli, curlist, &seg, &idx); + if (pair == QSE_NULL) + { + seg.len += idx.len + 2; /* adjustment for error message */ + goto noent; + } + + ptr++; /* skip } */ + + if (*ptr == QSE_T('\0')) break; /* no more segments */ + else if (*ptr != QSE_T('.')) goto inval; + } else { - pair = find_pair_byalias (xli, curlist, &seg, QSE_NULL); + pair = find_pair_by_key_and_alias (xli, curlist, &seg, QSE_NULL); if (pair == QSE_NULL) goto noent; if (*ptr == QSE_T('\0')) break; /* no more segments */ @@ -581,7 +596,7 @@ noent: return QSE_NULL; } -qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* alias) +qse_size_t qse_xli_getnumpairsbyname (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* dotted_name) { const qse_char_t* ptr; const qse_xli_list_t* curlist; @@ -590,11 +605,11 @@ qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* lis curlist = list? list: &xli->root; - ptr = alias; + ptr = dotted_name; while (1) { seg.ptr = ptr; - while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[')) ptr++; + while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[') && *ptr != QSE_T('{')) ptr++; if (ptr == seg.ptr) goto inval; seg.len = ptr - seg.ptr; @@ -603,14 +618,14 @@ qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* lis /* check the type of curlist. this check is needed * because of the unconditional switching at the bottom of the * this loop. this implementation strategy has been chosen - * to provide the segment alias easily. */ + * to provide the segment name easily. */ goto noent; } if (*ptr == QSE_T('[')) { /* index is specified */ - ptr++; + ptr++; /* skip [ */ if (QSE_ISDIGIT(*ptr)) { @@ -625,50 +640,13 @@ qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* lis if (*ptr != QSE_T(']')) goto inval; - pair = find_pair_byindex (xli, curlist, &seg, index); + pair = find_pair_by_key_and_index (xli, curlist, &seg, index); if (pair == QSE_NULL) { seg.len += count + 2; /* adjustment for error message */ goto noent; } } - else if (QSE_ISALPHA(*ptr)) - { - /* word index */ - qse_cstr_t idx; - - idx.ptr = ptr; - do ptr++; while (QSE_ISALNUM(*ptr) || *ptr == QSE_T('_') || *ptr == QSE_T('-')); - idx.len = ptr - idx.ptr; - - if (*ptr != QSE_T(']')) goto inval; - - pair = find_pair_byalias (xli, curlist, &seg, &idx); - if (pair == QSE_NULL) - { - seg.len += idx.len + 2; /* adjustment for error message */ - goto noent; - } - } - else if (*ptr == QSE_T('\'') || *ptr == QSE_T('\"')) - { - qse_cstr_t idx; - qse_char_t cc = *ptr++; - - idx.ptr = ptr; - do ptr++; while (*ptr != cc && *ptr != QSE_T('\0')); - idx.len = ptr - idx.ptr; - - if (*ptr != cc) goto inval; - if (*++ptr != QSE_T(']')) goto inval; - - pair = find_pair_byalias (xli, curlist, &seg, &idx); - if (pair == QSE_NULL) - { - seg.len += idx.len + 4; /* adjustment for error message */ - goto noent; - } - } else goto inval; ptr++; /* skip ] */ @@ -680,18 +658,47 @@ qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* lis } else if (*ptr != QSE_T('.')) goto inval; } + else if (*ptr == QSE_T('{')) + { + /* word index - alias */ + qse_cstr_t idx; + + ptr++; /* skip { */ + + idx.ptr = ptr; + while (*ptr != QSE_T('}') && *ptr != QSE_T('\0')) ptr++; + idx.len = ptr - idx.ptr; + + if (*ptr != QSE_T('}') || idx.len == 0) goto inval; + + pair = find_pair_by_key_and_alias (xli, curlist, &seg, &idx); + if (pair == QSE_NULL) + { + seg.len += idx.len + 2; /* adjustment for error message */ + goto noent; + } + + ptr++; /* skip } */ + + if (*ptr == QSE_T('\0')) + { + /* no more segments */ + return 1; + } + else if (*ptr != QSE_T('.')) goto inval; + } else { - pair = find_pair_byalias (xli, curlist, &seg, QSE_NULL); + pair = find_pair_by_key_and_alias (xli, curlist, &seg, QSE_NULL); if (pair == QSE_NULL) goto noent; if (*ptr == QSE_T('\0')) { - return count_pair_byalias (xli, curlist, &seg, QSE_NULL); + return count_pairs_by_key_and_alias (xli, curlist, &seg, QSE_NULL); } else { - pair = find_pair_byalias (xli, curlist, &seg, QSE_NULL); + pair = find_pair_by_key_and_alias (xli, curlist, &seg, QSE_NULL); if (pair == QSE_NULL) goto noent; } } @@ -702,7 +709,7 @@ qse_size_t qse_xli_getnumpairsbyalias (qse_xli_t* xli, const qse_xli_list_t* lis /* switch to the value regardless of its type. * check if it is a list in the beginning of the loop - * just after having gotten the next segment alias */ + * just after having gotten the next segment name */ curlist = (qse_xli_list_t*)pair->val; } @@ -760,3 +767,25 @@ qse_char_t* qse_xli_dupflatstr (qse_xli_t* xli, qse_xli_str_t* str, qse_size_t* return tmp; } + + +int qse_xli_setschema (qse_xli_t* xli, const qse_char_t* dotted_name, const qse_xli_scm_t* scm) +{ + int tmp; + + tmp = scm->flags & (QSE_XLI_SCM_VAL_LIST | QSE_XLI_SCM_VAL_STR | QSE_XLI_SCM_VAL_NIL); + if (tmp != QSE_XLI_SCM_VAL_LIST && tmp != QSE_XLI_SCM_VAL_STR && tmp != QSE_XLI_SCM_VAL_NIL) + { + /* VAL_LIST, VAL_STR, VAL_NIL can't co-exist */ + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return -1; + } + + if (qse_rbt_upsert (xli->schema, dotted_name, qse_strlen(dotted_name), scm, QSE_SIZEOF(scm)) == QSE_NULL) + { + qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); + return -1; + } + + return 0; +} diff --git a/qse/lib/xli/xli.h b/qse/lib/xli/xli.h index a21c8195..57499bd6 100644 --- a/qse/lib/xli/xli.h +++ b/qse/lib/xli/xli.h @@ -23,6 +23,7 @@ #include #include +#include #include "../cmn/mem.h" typedef struct qse_xli_tok_t qse_xli_tok_t; @@ -60,7 +61,11 @@ struct qse_xli_t qse_xli_nil_t xnil; qse_xli_list_t root; - qse_xli_list_link_t* parlink; + + qse_xli_list_link_t* parlink; /* link that points to the list being read currently */ + + qse_str_t* dotted_curkey; + qse_rbt_t* schema; qse_xli_tok_t tok; int tok_status;