diff --git a/qse/cmd/xli/xli.c b/qse/cmd/xli/xli.c index 6a2a8a48..40948a41 100644 --- a/qse/cmd/xli/xli.c +++ b/qse/cmd/xli/xli.c @@ -58,6 +58,8 @@ #define IO_FLAG_INI_INPUT (1 << 0) #define IO_FLAG_INI_OUTPUT (1 << 1) +#define IO_FLAG_JSON_INPUT (1 << 2) +#define IO_FLAG_JSON_OUTPUT (1 << 3) static int g_io_flags = 0; static qse_char_t* g_input_file = QSE_NULL; @@ -158,6 +160,8 @@ static void print_usage (qse_sio_t* out, int argc, qse_char_t* argv[]) qse_fprintf (out, QSE_T(" -o file specify an output file\n")); qse_fprintf (out, QSE_T(" -I file specify an ini input file\n")); qse_fprintf (out, QSE_T(" -O file specify an ini output file\n")); + qse_fprintf (out, QSE_T(" -j file specify a json input file\n")); + qse_fprintf (out, QSE_T(" -J file specify a json 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")); @@ -168,7 +172,6 @@ static void print_usage (qse_sio_t* out, int argc, qse_char_t* argv[]) qse_fprintf (out, QSE_T(" -l disallow lists\n")); qse_fprintf (out, QSE_T(" -K allow key tags\n")); qse_fprintf (out, QSE_T(" -S allow string tags\n")); - qse_fprintf (out, QSE_T(" -j input and output in json format\n")); qse_fprintf (out, QSE_T(" -v perform validation\n")); qse_fprintf (out, QSE_T(" -m number specify the maximum amount of memory to use in bytes\n")); #if defined(QSE_BUILD_DEBUG) @@ -196,15 +199,15 @@ static int handle_args (int argc, qse_char_t* argv[]) static qse_opt_t opt = { #if defined(QSE_BUILD_DEBUG) - QSE_T("hi:o:I:O:uaftsdnlKSjvm:X:"), + QSE_T("hi:o:I:O:j:J:uaftsdnlKSvm:X:"), #else - QSE_T("hi:o:I:O:uaftsdnlKSjvm:"), + QSE_T("hi:o:I:O:j:J:uaftsdnlKSvm:"), #endif lng }; qse_cint_t c; - while ((c = qse_getopt (argc, argv, &opt)) != QSE_CHAR_EOF) + while ((c = qse_getopt(argc, argv, &opt)) != QSE_CHAR_EOF) { switch (c) { @@ -234,7 +237,7 @@ static int handle_args (int argc, qse_char_t* argv[]) case QSE_T('i'): g_input_file = opt.arg; - g_io_flags &= ~IO_FLAG_INI_INPUT; + g_io_flags &= ~(IO_FLAG_INI_INPUT | IO_FLAG_JSON_OUTPUT); break; case QSE_T('I'): @@ -242,9 +245,14 @@ static int handle_args (int argc, qse_char_t* argv[]) g_io_flags |= IO_FLAG_INI_INPUT; break; + case QSE_T('j'): + g_input_file = opt.arg; + g_io_flags |= IO_FLAG_JSON_INPUT; + break; + case QSE_T('o'): g_output_file = opt.arg; - g_io_flags &= ~IO_FLAG_INI_OUTPUT; + g_io_flags &= ~(IO_FLAG_INI_OUTPUT | IO_FLAG_JSON_OUTPUT); break; case QSE_T('O'): @@ -252,6 +260,11 @@ static int handle_args (int argc, qse_char_t* argv[]) g_io_flags |= IO_FLAG_INI_OUTPUT; break; + case QSE_T('J'): + g_output_file = opt.arg; + g_io_flags |= IO_FLAG_JSON_OUTPUT; + break; + case QSE_T('u'): g_trait |= QSE_XLI_KEYNODUP; break; @@ -292,10 +305,6 @@ static int handle_args (int argc, qse_char_t* argv[]) g_trait |= QSE_XLI_STRTAG; break; - case QSE_T('j'): - g_trait |= QSE_XLI_JSON; - break; - case QSE_T('v'): g_trait |= QSE_XLI_VALIDATE; break; @@ -444,7 +453,9 @@ static int xli_main (int argc, qse_char_t* argv[]) in.u.file.path = g_input_file; in.u.file.cmgr = g_infile_cmgr; - n = (g_io_flags & IO_FLAG_INI_INPUT)? qse_xli_readinistd(xli, &in): qse_xli_readstd(xli, &in); + n = (g_io_flags & IO_FLAG_JSON_INPUT)? qse_xli_readjsonstd(xli, &in): + (g_io_flags & IO_FLAG_INI_INPUT)? qse_xli_readinistd(xli, &in): + qse_xli_readstd(xli, &in); if (n <= -1) { const qse_xli_loc_t* errloc; @@ -553,8 +564,9 @@ static int xli_main (int argc, qse_char_t* argv[]) out.u.file.path = g_output_file? g_output_file: QSE_T("-"); out.u.file.cmgr = g_outfile_cmgr; - ret = (g_io_flags & IO_FLAG_INI_OUTPUT)? qse_xli_writeinistd(xli, QSE_NULL, &out): qse_xli_writestd(xli, QSE_NULL, &out); - + ret = (g_io_flags & IO_FLAG_JSON_OUTPUT)? qse_xli_writejsonstd(xli, QSE_NULL, &out): + (g_io_flags & IO_FLAG_INI_OUTPUT)? qse_xli_writeinistd(xli, QSE_NULL, &out): + qse_xli_writestd(xli, QSE_NULL, &out); oops: if (xli) qse_xli_close (xli); if (xma_mmgr.ctx) qse_xma_close (xma_mmgr.ctx); diff --git a/qse/include/qse/xli/stdxli.h b/qse/include/qse/xli/stdxli.h index aa142a39..be487894 100644 --- a/qse/include/qse/xli/stdxli.h +++ b/qse/include/qse/xli/stdxli.h @@ -134,6 +134,12 @@ QSE_EXPORT int qse_xli_readinistd ( qse_xli_iostd_t* in ); +QSE_EXPORT int qse_xli_readjsonstd ( + qse_xli_t* xli, + qse_xli_iostd_t* in +); + + QSE_EXPORT int qse_xli_writestd ( qse_xli_t* xli, qse_xli_list_t* root_list, @@ -146,6 +152,12 @@ QSE_EXPORT int qse_xli_writeinistd ( qse_xli_iostd_t* out ); +QSE_EXPORT int qse_xli_writejsonstd ( + qse_xli_t* xli, + qse_xli_list_t* root_list, + qse_xli_iostd_t* out +); + #if defined(__cplusplus) } #endif diff --git a/qse/include/qse/xli/xli.h b/qse/include/qse/xli/xli.h index 80aceb28..63071383 100644 --- a/qse/include/qse/xli/xli.h +++ b/qse/include/qse/xli/xli.h @@ -65,10 +65,12 @@ enum qse_xli_errnum_t QSE_XLI_EIOUSR, /**< i/o handler error */ QSE_XLI_ESYNTAX, /**< syntax error */ + QSE_XLI_ECOLON, /**< colon expected in place of '${0}' */ QSE_XLI_ESCOLON, /**< semicolon expected in place of '${0}' */ - QSE_XLI_EASSIGN, /**< = expected in place of '${0}' */ + QSE_XLI_EEQ, /**< = expected in place of '${0}' */ QSE_XLI_ELBREQ, /**< { or = expected in place of '${0}' */ - QSE_XLI_ERBRCE, /**< } expected in place of '${0}' */ + QSE_XLI_ERBRACE, /**< } expected in place of '${0}' */ + QSE_XLI_ERBRACK, /**< ] expected in place of '${0}' */ QSE_XLI_EPAVAL, /**< pair value expected in place of '${0}' */ QSE_XLI_ESTRNC, /**< string not closed */ QSE_XLI_ETAGNC, /**< tag not closed */ @@ -115,18 +117,7 @@ enum qse_xli_opt_t */ QSE_XLI_ROOTXTNSIZE, - /** - * It is a character to put between a parent key and a nested key. - * By default, it's a period and used like 'a.b.c' that means c under b under a. - */ - QSE_XLI_KEYSPLITTER, - - /** - * The first character is in the tag marker speicifies the tag opener - * and the second chracter specifies the tag closer. The are used when - * key tags and/or string tags are enabled. By default, it is "[]". - */ - QSE_XLI_TAGMARKER + QSE_XLI_KEYSPLITTER }; typedef enum qse_xli_opt_t qse_xli_opt_t; @@ -156,19 +147,14 @@ enum qse_xli_trait_t * "tg" is stored into the tag field of qse_xli_str_t. */ QSE_XLI_STRTAG = (1 << 10), - /** support the json format */ - QSE_XLI_JSON = (1 << 11), - /** enable pair validation against pair definitions while reading */ - QSE_XLI_VALIDATE = (1 << 12) + QSE_XLI_VALIDATE = (1 << 11) }; typedef enum qse_xli_trait_t qse_xli_trait_t; typedef struct qse_xli_val_t qse_xli_val_t; typedef struct qse_xli_nil_t qse_xli_nil_t; typedef struct qse_xli_str_t qse_xli_str_t; -typedef struct qse_xli_int_t qse_xli_int_t; -typedef struct qse_xli_array_t qse_xli_array_t; typedef struct qse_xli_list_t qse_xli_list_t; typedef struct qse_xli_atom_t qse_xli_atom_t; @@ -223,26 +209,6 @@ struct qse_xli_str_t qse_xli_str_t* next; }; -struct qse_xli_int_t -{ - QSE_XLI_VAL_HDR; - const qse_char_t* tag; - const qse_char_t* ptr; - qse_size_t len; - /* TODO: include a numeric value here??? - qse_intmax_t val; - */ - /* NEED TO SUPPORT MULTI NUBMER? */ -}; - -struct qse_xli_array_t -{ - QSE_XLI_VAL_HDR; - const qse_char_t* tag; - qse_xli_val_t* ptr; - qse_size_t count; -}; - #define QSE_XLI_ATOM_HDR \ qse_xli_atom_type_t type; \ qse_xli_atom_t* prev; \ @@ -261,8 +227,6 @@ struct qse_xli_pair_t const qse_char_t* key; const qse_char_t* alias; const qse_char_t* tag; - unsigned int _key_quoted: 2; /* used internally for output */ - unsigned int _alias_quoted: 2; /* used internally for output - in fact, an alias is always quoted */ qse_xli_val_t* val; }; @@ -404,10 +368,9 @@ enum qse_xli_scm_flag_t QSE_XLI_SCM_VALNIL = (1 << 1), QSE_XLI_SCM_VALSTR = (1 << 2), QSE_XLI_SCM_VALLIST = (1 << 3), - QSE_XLI_SCM_VALARRAY = (1 << 4), - QSE_XLI_SCM_KEYNODUP = (1 << 5), - QSE_XLI_SCM_KEYALIAS = (1 << 6), + QSE_XLI_SCM_KEYNODUP = (1 << 4), + QSE_XLI_SCM_KEYALIAS = (1 << 5), /** Indicates that the value is a list with uncertain definitions with * the following constraints: @@ -418,7 +381,7 @@ enum qse_xli_scm_flag_t * is specified. * * Applies only if #QSE_XLI_SCM_VALLIST is set. */ - QSE_XLI_SCM_VALIFFY = (1 << 7) + QSE_XLI_SCM_VALIFFY = (1 << 6) }; typedef enum qse_xli_scm_flag_t qse_xli_scm_flag_t; @@ -786,6 +749,11 @@ QSE_EXPORT int qse_xli_readini ( qse_xli_io_impl_t io ); +QSE_EXPORT int qse_xli_readjson ( + qse_xli_t* xli, + qse_xli_io_impl_t io +); + QSE_EXPORT int qse_xli_write ( qse_xli_t* xli, qse_xli_list_t* root_list, @@ -798,6 +766,12 @@ QSE_EXPORT int qse_xli_writeini ( qse_xli_io_impl_t io ); +QSE_EXPORT int qse_xli_writejson ( + qse_xli_t* xli, + qse_xli_list_t* root_list, + qse_xli_io_impl_t io +); + QSE_EXPORT void* qse_getxlipairxtn ( qse_xli_pair_t* pair ); diff --git a/qse/lib/xli/Makefile.am b/qse/lib/xli/Makefile.am index d96821f4..36ce7c42 100644 --- a/qse/lib/xli/Makefile.am +++ b/qse/lib/xli/Makefile.am @@ -5,7 +5,10 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/include lib_LTLIBRARIES = libqsexli.la -libqsexli_la_SOURCES = xli.h xli.c err.c read.c read-ini.c write.c write-ini.c std.c +libqsexli_la_SOURCES = xli.h xli.c err.c \ + read.c read-ini.c read-json.c \ + write.c write-ini.c write-json.c \ + std.c libqsexli_la_LDFLAGS = -L../cmn -L../si -version-info 1:0:0 -no-undefined libqsexli_la_LIBADD = -lqsesi -lqsecmn diff --git a/qse/lib/xli/Makefile.in b/qse/lib/xli/Makefile.in index b34dc5ae..cc4e7e16 100644 --- a/qse/lib/xli/Makefile.in +++ b/qse/lib/xli/Makefile.in @@ -135,8 +135,8 @@ am__uninstall_files_from_dir = { \ am__installdirs = "$(DESTDIR)$(libdir)" LTLIBRARIES = $(lib_LTLIBRARIES) libqsexli_la_DEPENDENCIES = -am_libqsexli_la_OBJECTS = xli.lo err.lo read.lo read-ini.lo write.lo \ - write-ini.lo std.lo +am_libqsexli_la_OBJECTS = xli.lo err.lo read.lo read-ini.lo \ + read-json.lo write.lo write-ini.lo write-json.lo std.lo libqsexli_la_OBJECTS = $(am_libqsexli_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -393,7 +393,11 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/include lib_LTLIBRARIES = libqsexli.la -libqsexli_la_SOURCES = xli.h xli.c err.c read.c read-ini.c write.c write-ini.c std.c +libqsexli_la_SOURCES = xli.h xli.c err.c \ + read.c read-ini.c read-json.c \ + write.c write-ini.c write-json.c \ + std.c + libqsexli_la_LDFLAGS = -L../cmn -L../si -version-info 1:0:0 -no-undefined libqsexli_la_LIBADD = -lqsesi -lqsecmn all: all-am @@ -476,9 +480,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/err.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read-ini.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read-json.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/std.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write-ini.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write-json.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/write.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xli.Plo@am__quote@ diff --git a/qse/lib/xli/err.c b/qse/lib/xli/err.c index 42c10145..36e893e3 100644 --- a/qse/lib/xli/err.c +++ b/qse/lib/xli/err.c @@ -27,8 +27,7 @@ #include "xli.h" #include "../cmn/mem-prv.h" -const qse_char_t* qse_xli_dflerrstr ( - const qse_xli_t* xli, qse_xli_errnum_t errnum) +const qse_char_t* qse_xli_dflerrstr (const qse_xli_t* xli, qse_xli_errnum_t errnum) { static const qse_char_t* errstr[] = { @@ -46,10 +45,12 @@ const qse_char_t* qse_xli_dflerrstr ( QSE_T("error returned by user I/O handler"), QSE_T("syntax error"), + QSE_T("colon expected in place of '${0}'"), QSE_T("semicolon expected in place of '${0}'"), - QSE_T("assignment symbol expected in place of '${0}'"), - QSE_T("left-brace or assignment token expected in place of '${0}'"), + QSE_T("equal-sign expected in place of '${0}'"), + QSE_T("left-brace or equal-sign expected in place of '${0}'"), QSE_T("right-brace expected in place of '${0}'"), + QSE_T("right-bracket expected in place of '${0}'"), QSE_T("pair value expected in place of '${0}'"), QSE_T("string not closed"), QSE_T("string tag not closed"), diff --git a/qse/lib/xli/read-ini.c b/qse/lib/xli/read-ini.c index 8b3c7817..9d9e50bc 100644 --- a/qse/lib/xli/read-ini.c +++ b/qse/lib/xli/read-ini.c @@ -378,7 +378,7 @@ static int read_list (qse_xli_t* xli) if (!MATCH(xli, QSE_XLI_TOK_EQ)) { - qse_xli_seterror (xli, QSE_XLI_EASSIGN, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + qse_xli_seterror (xli, QSE_XLI_EEQ, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); goto oops; } diff --git a/qse/lib/xli/read-json.c b/qse/lib/xli/read-json.c new file mode 100644 index 00000000..2cfb09de --- /dev/null +++ b/qse/lib/xli/read-json.c @@ -0,0 +1,891 @@ +/* + * $Id$ + * + Copyright (c) 2006-2014 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xli.h" +#include + +/* + "key1" { + # comment + [keytag]key11 "alias" = [strtag]"test machine; + key1122 { + key112233 = "hello"; + } + } + } +*/ + +static int get_token (qse_xli_t* xli); +static int read_list (qse_xli_t* xli, qse_xli_list_t* lv); +static int read_array (qse_xli_t* xli, qse_xli_list_t* lv); + +enum +{ + TOK_STATUS_ENABLE_NSTR = (1 << 0) +}; + +#define GET_CHAR(xli) \ + do { if (qse_xli_getchar(xli) <= -1) return -1; } while(0) + +#define GET_CHAR_TO(xli,c) \ + do { \ + if (qse_xli_getchar(xli) <= -1) return -1; \ + c = (xli)->rio.last.c; \ + } while(0) + +#define ADD_TOKEN_CHAR(xli,tok,c) \ + do { \ + if (qse_str_ccat((tok)->name,(c)) == (qse_size_t)-1) \ + { \ + qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); \ + return -1; \ + } \ + } while (0) + +#define ADD_TOKEN_STR(xli,tok,s,l) \ + do { \ + if (qse_str_ncat((tok)->name,(s),(l)) == (qse_size_t)-1) \ + { \ + qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); \ + return -1; \ + } \ + } while (0) + +#define SET_TOKEN_TYPE(xli,tok,code) \ + do { (tok)->type = (code); } while (0) + +#define MATCH(xli,tok_type) ((xli)->tok.type == (tok_type)) + +typedef struct kwent_t kwent_t; + +struct kwent_t +{ + qse_cstr_t name; + int type; +}; + +/* note that the keyword must start with @. */ +static kwent_t kwtab[] = +{ + /* keep it sorted by the first field for binary search */ + { { QSE_T("@include"), 8 }, QSE_XLI_TOK_XINCLUDE } +}; + +static int skip_spaces (qse_xli_t* xli) +{ + qse_cint_t c = xli->rio.last.c; + while (QSE_ISSPACE(c)) GET_CHAR_TO (xli, c); + return 0; +} + +static int skip_comment (qse_xli_t* xli, qse_xli_tok_t* tok) +{ + qse_cint_t c = xli->rio.last.c; + + if (c == QSE_T('#')) + { + /* skip up to \n */ + + qse_str_clear (tok->name); + + do + { + GET_CHAR_TO (xli, c); + if (c == QSE_T('\n') || c == QSE_CHAR_EOF) break; + + if (xli->opt.trait & QSE_XLI_KEEPTEXT) ADD_TOKEN_CHAR (xli, tok, c); + } + while (1); + + if ((xli->opt.trait & QSE_XLI_KEEPTEXT) && + qse_xli_inserttext(xli, xli->parlink->list, QSE_NULL, QSE_STR_PTR(tok->name)) == QSE_NULL) return -1; + + GET_CHAR (xli); /* eat the new line letter */ + return 1; /* comment by # */ + } + + return 0; +} + +static int classify_ident (qse_xli_t* xli, const qse_cstr_t* name) +{ + /* perform binary search */ + + /* declaring left, right, mid to be the int type is ok + * because we know kwtab is small enough. */ + int left = 0, right = QSE_COUNTOF(kwtab) - 1, mid; + + while (left <= right) + { + int n; + kwent_t* kwp; + + /*mid = (left + right) / 2;*/ + mid = left + (right - left) / 2; + kwp = &kwtab[mid]; + + n = qse_strxncmp (kwp->name.ptr, kwp->name.len, name->ptr, name->len); + if (n > 0) + { + /* if left, right, mid were of qse_size_t, + * you would need the following line. + if (mid == 0) break; + */ + right = mid - 1; + } + else if (n < 0) left = mid + 1; + else return kwp->type; + } + + return QSE_XLI_TOK_IDENT; +} + +static int get_symbols (qse_xli_t* xli, qse_cint_t c, qse_xli_tok_t* tok) +{ + struct ops_t + { + const qse_char_t* str; + qse_size_t len; + int tid; + }; + + static struct ops_t ops[] = + { + { QSE_T(","), 1, QSE_XLI_TOK_COMMA }, + { QSE_T(":"), 1, QSE_XLI_TOK_COLON }, + { QSE_T(";"), 1, QSE_XLI_TOK_SEMICOLON }, + { QSE_T("{"), 1, QSE_XLI_TOK_LBRACE }, + { QSE_T("}"), 1, QSE_XLI_TOK_RBRACE }, + { QSE_T("["), 1, QSE_XLI_TOK_LBRACK }, + { QSE_T("]"), 1, QSE_XLI_TOK_RBRACK }, + { QSE_NULL, 0, 0, } + }; + + struct ops_t* p; + int idx = 0; + + /* note that the loop below is not generaic enough. + * you must keep the operators strings in a particular order */ + + for (p = ops; p->str != QSE_NULL; ) + { + if (p->str[idx] == QSE_T('\0')) + { + ADD_TOKEN_STR (xli, tok, p->str, p->len); + SET_TOKEN_TYPE (xli, tok, p->tid); + return 1; + } + + if (c == p->str[idx]) + { + idx++; + GET_CHAR_TO (xli, c); + continue; + } + + p++; + } + + return 0; +} + +static int end_include (qse_xli_t* xli, int noeof) +{ + int x; + qse_xli_io_arg_t* cur; + + if (xli->rio.inp == &xli->rio.top) return 0; /* no include */ + + + /* if it is an included file, close it and + * retry to read a character from an outer file */ + + x = xli->rio.impl ( + xli, QSE_XLI_IO_CLOSE, + xli->rio.inp, QSE_NULL, 0); + + /* if closing has failed, still destroy the + * sio structure first as normal and return + * the failure below. this way, the caller + * does not call QSE_XLI_SIO_CLOSE on + * xli->rio.inp again. */ + + cur = xli->rio.inp; + xli->rio.inp = xli->rio.inp->prev; + + QSE_ASSERT (cur->name != QSE_NULL); + QSE_MMGR_FREE (xli->mmgr, cur); + /* xli->parse.depth.incl--; */ + + if ((xli->opt.trait & QSE_XLI_KEEPFILE) && !noeof && + qse_xli_inserteof (xli, xli->parlink->list, QSE_NULL) == QSE_NULL) return -1; + + if (x != 0) + { + /* the failure mentioned above is returned here */ + if (xli->errnum == QSE_XLI_ENOERR) + qse_xli_seterrnum (xli, QSE_XLI_EIOUSR, QSE_NULL); + return -1; + } + + xli->rio.last = xli->rio.inp->last; + return 1; /* ended the included file successfully */ +} + +static int begin_include (qse_xli_t* xli) +{ + qse_link_t* link; + qse_xli_io_arg_t* arg = QSE_NULL; + + link = (qse_link_t*) qse_xli_callocmem (xli, + QSE_SIZEOF(*link) + QSE_SIZEOF(qse_char_t) * (QSE_STR_LEN(xli->tok.name) + 1)); + if (link == QSE_NULL) goto oops; + + qse_strncpy ((qse_char_t*)(link + 1), QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name)); + link->link = xli->rio_names; + xli->rio_names = link; + + arg = (qse_xli_io_arg_t*) qse_xli_callocmem (xli, QSE_SIZEOF(*arg)); + if (arg == QSE_NULL) goto oops; + + arg->name = (const qse_char_t*)(link + 1); + arg->line = 1; + arg->colm = 1; + + /* let the argument's prev point field to the current */ + arg->prev = xli->rio.inp; + + if (qse_xli_openrstream(xli, arg) <= -1) goto oops; + + /* i update the current pointer after opening is successful */ + xli->rio.inp = arg; + /* xli->parse.depth.incl++; */ + + /* read in the first character in the included file. + * so the next call to get_token() sees the character read + * from this file. */ + if (qse_xli_getchar (xli) <= -1 || get_token (xli) <= -1) + { + end_include (xli, 1); + /* i don't jump to oops since i've called + * end_include() where xli->rio.inp/arg is freed. */ + return -1; + } + + if ((xli->opt.trait & QSE_XLI_KEEPFILE) && + qse_xli_insertfile (xli, xli->parlink->list, QSE_NULL, arg->name) == QSE_NULL) + { + end_include (xli, 1); + return -1; + } + + return 0; + +oops: + /* i don't need to free 'link' since it's linked to + * xli->rio_names that's freed at the beginning of qse_xli_read() + * or by qse_xli_fini() */ + if (arg) QSE_MMGR_FREE (xli->mmgr, arg); + return -1; +} + +static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) +{ + qse_cint_t c; + int n; + int skip_semicolon_after_include = 0; + +retry: + do + { + if (skip_spaces (xli) <= -1) return -1; + if ((n = skip_comment (xli, tok)) <= -1) return -1; + } + while (n >= 1); + + qse_str_clear (tok->name); + tok->loc.file = xli->rio.last.file; + tok->loc.line = xli->rio.last.line; + tok->loc.colm = xli->rio.last.colm; + + c = xli->rio.last.c; + + if (c == QSE_CHAR_EOF) + { + n = end_include (xli, 0); + if (n <= -1) return -1; + if (n >= 1) + { + /*xli->rio.last = xli->rio.inp->last;*/ + /* mark that i'm retrying after end of an included file */ + skip_semicolon_after_include = 1; + goto retry; + } + + ADD_TOKEN_STR (xli, tok, QSE_T(""), 5); + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_EOF); + } + else if (c == QSE_T('@')) + { + /* keyword/directive - start with @ */ + + int type; + + ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR_TO (xli, c); + + if (!QSE_ISALPHA (c)) + { + /* this directive is empty, not followed by a valid word */ + qse_xli_seterror (xli, QSE_XLI_EXKWEM, QSE_NULL, &tok->loc); + return -1; + } + + /* expect an identifier starting with an alphabet. the identifier + * forming a keyword/directory is composed of alphabets. */ + do + { + ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR_TO (xli, c); + } + while (QSE_ISALPHA (c)); + + type = classify_ident (xli, QSE_STR_XSTR(tok->name)); + if (type == QSE_XLI_TOK_IDENT) + { + /* this keyword/directive is not recognized */ + qse_xli_seterror (xli, QSE_XLI_EXKWNR, QSE_STR_XSTR(tok->name), &tok->loc); + return -1; + } + SET_TOKEN_TYPE (xli, tok, type); + } + else if (c == QSE_T('_') || QSE_ISALPHA (c) || + (!(xli->tok_status & TOK_STATUS_ENABLE_NSTR) && + (xli->opt.trait & QSE_XLI_LEADDIGIT) && + QSE_ISDIGIT(c))) + { + int lead_digit = QSE_ISDIGIT(c); + int all_digits = 1; + + /* a normal identifier can be composed of wider varieties of + * characters than a keyword/directive */ + while (1) + { + ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR_TO (xli, c); + + if (c == QSE_T('_') || c == QSE_T('-') || c == QSE_T('*') || + c == QSE_T('/') || QSE_ISALPHA (c)) + { + all_digits = 0; + } + else if (QSE_ISDIGIT(c)) + { + /* nothing to do */ + } + else break; + } + + if (lead_digit && all_digits) + { + /* if an identifier begins with a digit, it must contain a non-digits character */ + qse_xli_seterror (xli, QSE_XLI_EIDENT, QSE_STR_XSTR(tok->name), &tok->loc); + return -1; + } + + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_IDENT); + } + else if ((xli->tok_status & TOK_STATUS_ENABLE_NSTR) && QSE_ISDIGIT(c)) + { + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_NSTR); + do + { + ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR_TO (xli, c); + } + while (QSE_ISDIGIT(c)); + } + else if (c == QSE_T('\'')) + { + /* single-quoted string - no escaping */ + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_SQSTR); + + while (1) + { + GET_CHAR_TO (xli, c); + + if (c == QSE_CHAR_EOF) + { + /* the string is not closed */ + qse_xli_seterror (xli, QSE_XLI_ESTRNC, QSE_NULL, &tok->loc); + return -1; + } + + if (c == QSE_T('\'')) + { + /* terminating quote */ + GET_CHAR (xli); + break; + } + + ADD_TOKEN_CHAR (xli, tok, c); + } + } + else if (c == QSE_T('\"')) + { + /* double-quoted string - support escaping */ + int escaped = 0; + + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_DQSTR); + + while (1) + { + GET_CHAR_TO (xli, c); + + if (c == QSE_CHAR_EOF) + { + /* the string is not closed */ + qse_xli_seterror (xli, QSE_XLI_ESTRNC, QSE_NULL, &tok->loc); + return -1; + } + + if (!escaped) + { + if (c == QSE_T('\\')) + { + escaped = 1; + continue; + } + + if (c == QSE_T('\"')) + { + /* terminating quote */ + GET_CHAR (xli); + break; + } + + ADD_TOKEN_CHAR (xli, tok, c); + } + else + { + ADD_TOKEN_CHAR (xli, tok, c); + escaped = 0; + } + } + } + else + { + n = get_symbols (xli, c, tok); + if (n <= -1) return -1; /* hard failure */ + if (n == 0) + { + /* not handled yet */ + if (c == QSE_T('\0')) + { + qse_cstr_t ea; + ea.ptr = QSE_T(""); + ea.len = 5; + qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc); + } + else + { + qse_char_t cc = (qse_char_t)c; + qse_cstr_t ea; + ea.ptr = &cc; + ea.len = 1; + qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc); + } + return -1; + } + + if (skip_semicolon_after_include && tok->type == QSE_XLI_TOK_SEMICOLON) + { + /* this handles the optional semicolon after the + * included file named as in @include "file-name"; */ + skip_semicolon_after_include = 0; + goto retry; + } + } + + if (skip_semicolon_after_include) + { + /* semiclon has not been skipped yet */ + qse_xli_seterror (xli, QSE_XLI_ESCOLON, QSE_STR_XSTR(tok->name), &tok->loc); + return -1; + } + +printf ("TOKEN: %ls\n",QSE_STR_PTR(tok->name)); + return 0; +} + +static int get_token (qse_xli_t* xli) +{ + return get_token_into (xli, &xli->tok); +} + +static int check_token_for_key_eligibility (qse_xli_t* xli, qse_xli_list_t* parlist) +{ + if (xli->opt.trait & QSE_XLI_KEYNODUP) + { + qse_xli_atom_t* atom; + + /* find any key conflicts in the current scope */ + /* TODO: optimization. no sequential search */ + atom = parlist->tail; + while (atom) + { + if (atom->type == QSE_XLI_PAIR && + qse_strcmp(((qse_xli_pair_t*)atom)->key, QSE_STR_PTR(xli->tok.name)) == 0) + { + qse_xli_seterror (xli, QSE_XLI_EEXIST, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } + + atom = atom->prev; + } + } + + return 0; +} + +static qse_xli_val_t* __read_value (qse_xli_t* xli) +{ + while (1) + { + if (MATCH(xli, QSE_XLI_TOK_XINCLUDE)) + { + if (get_token(xli) <= -1) return QSE_NULL; + + if (!MATCH(xli,QSE_XLI_TOK_SQSTR) && !MATCH(xli,QSE_XLI_TOK_DQSTR)) + { + qse_xli_seterror (xli, QSE_XLI_EINCLSTR, QSE_NULL, &xli->tok.loc); + return QSE_NULL; + } + + if (begin_include(xli) <= -1) return QSE_NULL; + } + else if (/*MATCH(xli, QSE_XLI_TOK_IDENT) || */MATCH(xli, QSE_XLI_TOK_DQSTR) || MATCH(xli, QSE_XLI_TOK_SQSTR)) + { + return (qse_xli_val_t*)qse_xli_makestrval(xli, QSE_STR_XSTR(xli->tok.name), QSE_NULL); + } + else if (MATCH(xli, QSE_XLI_TOK_LBRACE)) + { + qse_xli_list_t* lv; + + lv = qse_xli_makelistval (xli); + if (!lv) return QSE_NULL; + + if (get_token(xli) <= -1 || read_list(xli, lv) <= -1) + { + qse_xli_freeval (xli, (qse_xli_val_t*)lv); + return QSE_NULL; + } + + return (qse_xli_val_t*)lv; + } + else if (MATCH(xli, QSE_XLI_TOK_LBRACK)) + { + qse_xli_list_t* lv; + + lv = qse_xli_makelistval (xli); + if (!lv) return QSE_NULL; + + if (get_token(xli) <= -1 || read_array(xli, lv) <= -1) + if (!lv) + { + qse_xli_freeval (xli, (qse_xli_val_t*)lv); + } + + return (qse_xli_val_t*)lv; + } + else if (MATCH(xli, QSE_XLI_TOK_TEXT)) + { + if (get_token(xli) <= -1) return QSE_NULL; + } + else + { + break; + } + } + + return QSE_NULL; +} + + +struct rpair_t +{ + qse_char_t* key; + qse_xli_val_t* val; +}; +typedef struct rpair_t rpair_t; + +static int read_pair (qse_xli_t* xli, rpair_t* pair) +{ + qse_char_t* key = QSE_NULL; + qse_xli_val_t* val = QSE_NULL; + + if (check_token_for_key_eligibility(xli, xli->parlink->list) <= -1) goto oops; + + key = qse_strdup(QSE_STR_PTR(xli->tok.name), xli->mmgr); + if (key == QSE_NULL) + { + qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); + goto oops; + } + + if (get_token(xli) <= -1) goto oops; + + if (!MATCH(xli, QSE_XLI_TOK_COLON)) + { + qse_xli_seterror (xli, QSE_XLI_ECOLON, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + goto oops; + } + + if (get_token(xli) <= -1) goto oops; + + val = __read_value(xli); + if (!val) goto oops; + + pair->key = key; + pair->val = val; + + return 0; + +oops: + if (val) qse_xli_freeval (xli, val); + if (key) QSE_MMGR_FREE (xli->mmgr, key); + return -1; +} + +static int __read_array (qse_xli_t* xli) +{ + qse_xli_val_t* val; + qse_size_t index = 0; + qse_char_t key[64]; + + while (1) + { + val = __read_value(xli); + if (!val) return -1; + + qse_strxfmt (key, QSE_COUNTOF(key), QSE_T("%zu"), index); + if (!qse_xli_insertpair (xli, xli->parlink->list, QSE_NULL, key, QSE_NULL, QSE_NULL, val)) return -1; + index++; + + if (get_token(xli) <= -1) return -1; + + if (MATCH(xli, QSE_XLI_TOK_COMMA)) + { + if (get_token(xli) <= -1) return -1; + continue; + } + + if (MATCH(xli, QSE_XLI_TOK_RBRACK)) break; + } + + return 0; + +} + +static int read_array (qse_xli_t* xli, qse_xli_list_t* lv) +{ + qse_xli_list_link_t* ll; + int n; + + ll = qse_xli_makelistlink (xli, lv); + if (!ll) return -1; + + n = __read_array(xli); + + qse_xli_freelistlink (xli, ll); + + if (n <= -1) return -1; + + if (!MATCH(xli, QSE_XLI_TOK_RBRACK)) + { + qse_xli_seterror (xli, QSE_XLI_ERBRACK, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } + + return 0; +} + +static int __read_list (qse_xli_t* xli) +{ + while (1) + { + if (MATCH(xli, QSE_XLI_TOK_XINCLUDE)) + { + if (get_token(xli) <= -1) return -1; + + if (!MATCH(xli,QSE_XLI_TOK_SQSTR) && !MATCH(xli,QSE_XLI_TOK_DQSTR)) + { + qse_xli_seterror (xli, QSE_XLI_EINCLSTR, QSE_NULL, &xli->tok.loc); + return -1; + } + + if (begin_include (xli) <= -1) return -1; + } + else if (/*MATCH(xli, QSE_XLI_TOK_IDENT) ||*/ MATCH(xli, QSE_XLI_TOK_DQSTR) || MATCH(xli, QSE_XLI_TOK_SQSTR)) + { + rpair_t rpair; + if (read_pair(xli, &rpair) <= -1) return -1; + + if (!qse_xli_insertpair (xli, xli->parlink->list, QSE_NULL, rpair.key, QSE_NULL, QSE_NULL, rpair.val)) + { + QSE_MMGR_FREE (xli->mmgr, rpair.key); + qse_xli_freeval (xli, rpair.val); + return -1; + } + + /* clear the duplicated key. the key is also duplicated in qse_xli_insertpair(). don't need it */ + QSE_MMGR_FREE (xli->mmgr, rpair.key); + + if (get_token(xli) <= -1) return -1; + + if (MATCH(xli, QSE_XLI_TOK_COMMA)) + { + if (get_token(xli) <= -1) return -1; + continue; + } + + break; + } + else if (MATCH(xli, QSE_XLI_TOK_TEXT)) + { + if (get_token(xli) <= -1) return -1; + } + else + { + break; + } + } + + return 0; +} + +static int read_list (qse_xli_t* xli, qse_xli_list_t* lv) +{ + qse_xli_list_link_t* ll; + int n; + + ll = qse_xli_makelistlink (xli, lv); + if (!ll) return -1; + + n = __read_list(xli); + + qse_xli_freelistlink (xli, ll); + + if (n <= -1) return -1; + + if (!MATCH(xli, QSE_XLI_TOK_RBRACE)) + { + qse_xli_seterror (xli, QSE_XLI_ERBRACE, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } + + return 0; +} + +static int read_root_list (qse_xli_t* xli) +{ + qse_xli_list_link_t* link; + + link = qse_xli_makelistlink (xli, &xli->root->list); + if (!link) return -1; + + if (qse_xli_getchar(xli) <= -1 || get_token(xli) <= -1 || __read_list(xli) <= -1) + { + qse_xli_freelistlink (xli, link); + return -1; + } + + QSE_ASSERT (link == xli->parlink); + qse_xli_freelistlink (xli, link); + + return 0; +} + +int qse_xli_readjson (qse_xli_t* xli, qse_xli_io_impl_t io) +{ + if (!io) + { + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return -1; + } + + QSE_MEMSET (&xli->rio, 0, QSE_SIZEOF(xli->rio)); + xli->rio.impl = io; + xli->rio.top.line = 1; + xli->rio.top.colm = 1; + xli->rio.inp = &xli->rio.top; + + xli->tok_status = 0; + + qse_xli_seterrnum (xli, QSE_XLI_ENOERR, QSE_NULL); + qse_xli_clearrionames (xli); + + QSE_ASSERT (QSE_STR_LEN(xli->dotted_curkey) == 0); + + if (qse_xli_openrstream (xli, xli->rio.inp) <= -1) return -1; + /* the input stream is open now */ + + if (read_root_list (xli) <= -1) goto oops; + + QSE_ASSERT (xli->parlink == QSE_NULL); + + if (!MATCH (xli, QSE_XLI_TOK_EOF)) + { + qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc); + goto oops; + } + + QSE_ASSERT (xli->rio.inp == &xli->rio.top); + qse_xli_closeactiverstream (xli); + qse_str_clear (xli->tok.name); + return 0; + +oops: + /* an error occurred and control has reached here + * probably, some included files might not have been + * closed. close them */ + while (xli->rio.inp != &xli->rio.top) + { + qse_xli_io_arg_t* prev; + + /* nothing much to do about a close error */ + qse_xli_closeactiverstream (xli); + + prev = xli->rio.inp->prev; + QSE_ASSERT (xli->rio.inp->name != QSE_NULL); + QSE_MMGR_FREE (xli->mmgr, xli->rio.inp); + xli->rio.inp = prev; + } + + qse_xli_closeactiverstream (xli); + qse_str_clear (xli->tok.name); + return -1; +} diff --git a/qse/lib/xli/read.c b/qse/lib/xli/read.c index 22bb4ad7..42c612db 100644 --- a/qse/lib/xli/read.c +++ b/qse/lib/xli/read.c @@ -27,20 +27,6 @@ #include "xli.h" #include -/* - "key1" { - # comment - [keytag]key11 "alias" = [strtag]"test machine; - key1122 { - key112233 = "hello"; - } - } - } -*/ - -static qse_char_t tag_opener[] = { QSE_T('['), QSE_T('(') }; -static qse_char_t tag_closer[] = { QSE_T(']'), QSE_T(')') }; - static int get_token (qse_xli_t* xli); static int read_list (qse_xli_t* xli, qse_xli_list_t* list, const qse_xli_scm_t* override); @@ -307,37 +293,32 @@ static int get_symbols (qse_xli_t* xli, qse_cint_t c, qse_xli_tok_t* tok) { const qse_char_t* str; qse_size_t len; - int tid[2]; /* normal id, alternative id */ + int tid; }; static struct ops_t ops[] = { - { QSE_T("="), 1, { QSE_XLI_TOK_EQ, QSE_XLI_TOK_COLON } }, - { QSE_T(":"), 1, { QSE_XLI_TOK_COLON, QSE_XLI_TOK_EQ } }, - { QSE_T(","), 1, { QSE_XLI_TOK_COMMA, QSE_XLI_TOK_COMMA } }, - { QSE_T(";"), 1, { QSE_XLI_TOK_SEMICOLON, QSE_XLI_TOK_SEMICOLON } }, - { QSE_T("{"), 1, { QSE_XLI_TOK_LBRACE, QSE_XLI_TOK_LBRACE } }, - { QSE_T("}"), 1, { QSE_XLI_TOK_RBRACE, QSE_XLI_TOK_RBRACE } }, - { QSE_T("["), 1, { QSE_XLI_TOK_LBRACK, QSE_XLI_TOK_LPAREN } }, - { QSE_T("]"), 1, { QSE_XLI_TOK_RBRACK, QSE_XLI_TOK_RPAREN } }, - { QSE_T("("), 1, { QSE_XLI_TOK_LPAREN, QSE_XLI_TOK_LBRACK } }, - { QSE_T(")"), 1, { QSE_XLI_TOK_RPAREN, QSE_XLI_TOK_RBRACK } }, - { QSE_NULL, 0, { 0, 0 } } + { QSE_T("="), 1, QSE_XLI_TOK_EQ }, + { QSE_T(","), 1, QSE_XLI_TOK_COMMA }, + { QSE_T(";"), 1, QSE_XLI_TOK_SEMICOLON }, + { QSE_T("{"), 1, QSE_XLI_TOK_LBRACE }, + { QSE_T("}"), 1, QSE_XLI_TOK_RBRACE }, + { QSE_NULL, 0, 0, } }; struct ops_t* p; int idx = 0; - int tid_mode = (xli->opt.trait & QSE_XLI_JSON)? 1: 0; /* note that the loop below is not generaic enough. * you must keep the operators strings in a particular order */ + for (p = ops; p->str != QSE_NULL; ) { if (p->str[idx] == QSE_T('\0')) { ADD_TOKEN_STR (xli, tok, p->str, p->len); - SET_TOKEN_TYPE (xli, tok, p->tid[tid_mode]); + SET_TOKEN_TYPE (xli, tok, p->tid); return 1; } @@ -458,7 +439,7 @@ oops: static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) { qse_cint_t c; - int n, tag_mode = (xli->opt.trait & QSE_XLI_JSON)? 1: 0; + int n; int skip_semicolon_after_include = 0; retry: @@ -530,8 +511,6 @@ retry: (xli->opt.trait & QSE_XLI_LEADDIGIT) && QSE_ISDIGIT(c))) { - /* if you change the rules here, you need to update - * need_quoting() in write.c */ int lead_digit = QSE_ISDIGIT(c); int all_digits = 1; @@ -543,8 +522,8 @@ retry: GET_CHAR_TO (xli, c); if (c == QSE_T('_') || c == QSE_T('-') || - (!(xli->opt.trait & QSE_XLI_JSON) && c == QSE_T(':')) || - c == QSE_T('*') || c == QSE_T('/') || QSE_ISALPHA (c)) + c == QSE_T(':') || c == QSE_T('*') || + c == QSE_T('/') || QSE_ISALPHA (c)) { all_digits = 0; } @@ -557,7 +536,7 @@ retry: if (lead_digit && all_digits) { - /* if an identifier begins with a digit, it must contain a non-digit character */ + /* if an identifier begins with a digit, it must contain a non-digits character */ qse_xli_seterror (xli, QSE_XLI_EIDENT, QSE_STR_XSTR(tok->name), &tok->loc); return -1; } @@ -642,17 +621,15 @@ retry: } } } - else if ((xli->opt.trait & (QSE_XLI_KEYTAG | QSE_XLI_STRTAG)) && c == tag_opener[tag_mode]) /* [ */ + else if ((xli->opt.trait & (QSE_XLI_KEYTAG | QSE_XLI_STRTAG)) && c == QSE_T('[')) { /* a string tag is a bracketed word placed in front of a string value. * A = [tg] "abc"; * "tg" is stored into the tag field of qse_xli_str_t. - * - * however, the tag opener and the closer are not hard-coded. you may - * use a different opener and closer depending on your requirement. */ - SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_TAG); + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_TAG); + while (1) { GET_CHAR_TO (xli, c); @@ -664,7 +641,7 @@ retry: return -1; } - if (c == tag_closer[tag_mode]) /* ] */ + if (c == QSE_T(']')) { /* terminating quote */ GET_CHAR (xli); @@ -747,7 +724,6 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc const qse_xli_scm_t* scm = QSE_NULL; int key_nodup = 0, key_alias = 0, val_iffy = 0; - /*int key_quoted = 0, alias_quoted = 0;*/ key.ptr = QSE_NULL; name = QSE_NULL; @@ -759,11 +735,9 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc if (xli->opt.trait & QSE_XLI_KEYNODUP) key_nodup = 1; if (xli->opt.trait & QSE_XLI_KEYALIAS) key_alias = 1; - /*if (MATCH(xli, QSE_XLI_TOK_SQSTR)) key_quoted = 1; - else if (MATCH(xli, QSE_XLI_TOK_DQSTR)) key_quoted = 2;*/ kloc = xli->tok.loc; key.len = QSE_STR_LEN(xli->tok.name); - key.ptr = qse_strdup(QSE_STR_PTR(xli->tok.name), xli->mmgr); + 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); @@ -771,8 +745,8 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc } 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) + 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; @@ -810,7 +784,7 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc while (atom) { if (atom->type == QSE_XLI_PAIR && - qse_strcmp(((qse_xli_pair_t*)atom)->key, QSE_STR_PTR(xli->tok.name)) == 0) + qse_strcmp (((qse_xli_pair_t*)atom)->key, QSE_STR_PTR(xli->tok.name)) == 0) { qse_xli_seterror (xli, QSE_XLI_EEXIST, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); goto oops; @@ -820,15 +794,15 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc } } - /* once the key name is read, enable the numeric string for a key alias and a value */ + /* once the key name is read, enable the numeric string for a value */ xli->tok_status |= TOK_STATUS_ENABLE_NSTR; - if (get_token(xli) <= -1) goto oops; + if (get_token (xli) <= -1) goto oops; if (key_alias) { /* the alias part must be unique for the same key(s) */ - if (MATCH(xli, QSE_XLI_TOK_IDENT) || MATCH(xli, QSE_XLI_TOK_SQSTR) || MATCH(xli, QSE_XLI_TOK_DQSTR) || MATCH(xli, QSE_XLI_TOK_NSTR)) + if (MATCH(xli, QSE_XLI_TOK_IDENT) || MATCH(xli, QSE_XLI_TOK_DQSTR) || MATCH(xli, QSE_XLI_TOK_SQSTR) || MATCH(xli, QSE_XLI_TOK_NSTR)) { qse_xli_atom_t* atom; @@ -853,9 +827,6 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc goto oops; } - /*if (MATCH(xli, QSE_XLI_TOK_SQSTR)) alias_quoted = 1; - else if (MATCH(xli, QSE_XLI_TOK_DQSTR)) alias_quoted = 2;*/ - if (get_token (xli) <= -1) goto oops; } else if (key_alias == 2) @@ -871,9 +842,7 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc { if (get_token (xli) <= -1) goto oops; - if (!(xli->opt.trait & QSE_XLI_NOLIST) && MATCH(xli, QSE_XLI_TOK_LBRACE)) goto handle_list; - - if ((xli->opt.trait & QSE_XLI_STRTAG) && MATCH (xli, QSE_XLI_TOK_TAG)) + if ((xli->opt.trait & QSE_XLI_STRTAG) && MATCH(xli, QSE_XLI_TOK_TAG)) { strtag = qse_strxdup (QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name), xli->mmgr); if (strtag == QSE_NULL) @@ -901,11 +870,6 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc pair = qse_xli_insertpairwithstr (xli, parlist, QSE_NULL, key.ptr, name, keytag, QSE_STR_XSTR(xli->tok.name), strtag); if (pair == QSE_NULL) goto oops; -#if 0 - pair->_key_quoted = key_quoted; /* store this for easier output support */ - pair->_alias_quoted = alias_quoted; -#endif - segcount++; curstrseg = (qse_xli_str_t*)pair->val; @@ -923,7 +887,7 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc strtag = QSE_NULL; } - if ((xli->opt.trait & QSE_XLI_STRTAG) && MATCH (xli, QSE_XLI_TOK_TAG)) + if ((xli->opt.trait & QSE_XLI_STRTAG) && MATCH(xli, QSE_XLI_TOK_TAG)) { strtag = qse_strxdup (QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name), xli->mmgr); if (strtag == QSE_NULL) @@ -935,7 +899,7 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc if (get_token (xli) <= -1) goto oops; } - if (!MATCH (xli, QSE_XLI_TOK_SQSTR) && !MATCH (xli, QSE_XLI_TOK_DQSTR) && !MATCH (xli, QSE_XLI_TOK_NSTR) && !MATCH (xli, QSE_XLI_TOK_IDENT)) + if (!MATCH(xli, QSE_XLI_TOK_SQSTR) && !MATCH(xli, QSE_XLI_TOK_DQSTR) && !MATCH(xli, QSE_XLI_TOK_NSTR) && !MATCH(xli, QSE_XLI_TOK_IDENT)) { qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc); goto oops; @@ -948,11 +912,11 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc segcount++; if (get_token (xli) <= -1) goto oops; /* skip the value */ } - while (MATCH (xli, QSE_XLI_TOK_COMMA)); + while (MATCH(xli, QSE_XLI_TOK_COMMA)); } /* semicolon is mandatory for a string */ - if (!MATCH (xli, QSE_XLI_TOK_SEMICOLON)) + if (!MATCH(xli, QSE_XLI_TOK_SEMICOLON)) { qse_xli_seterror (xli, QSE_XLI_ESCOLON, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); goto oops; @@ -975,17 +939,12 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc goto oops; } + /* TODO: check against schema */ + } - else if (xli->opt.trait & QSE_XLI_JSON) + else if (!(xli->opt.trait & QSE_XLI_NOLIST) && MATCH(xli, QSE_XLI_TOK_LBRACE)) { - /* the assignment operator is mandator */ - qse_xli_seterror (xli, QSE_XLI_EASSIGN, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - goto oops; - } - else if (!(xli->opt.trait & QSE_XLI_NOLIST) && MATCH (xli, QSE_XLI_TOK_LBRACE)) - { - handle_list: if (scm && !(scm->flags & QSE_XLI_SCM_VALLIST)) { /* check the value type */ @@ -996,25 +955,25 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc xli->tok_status &= ~TOK_STATUS_ENABLE_NSTR; /* insert a pair with an empty list */ - pair = qse_xli_insertpairwithemptylist (xli, parlist, QSE_NULL, key.ptr, name, keytag); + pair = qse_xli_insertpairwithemptylist(xli, parlist, QSE_NULL, key.ptr, name, keytag); if (pair == QSE_NULL) goto oops; /* skip validations of child pairs if the schema for the * current pair is set with QSE_XLI_SCM_VALIFFY. * the schema for the child pairs, if specified, must not * take effect. */ - if (read_list (xli, (qse_xli_list_t*)pair->val, (val_iffy? &scm_val_iffy: QSE_NULL)) <= -1) goto oops; - - if (!MATCH (xli, QSE_XLI_TOK_RBRACE)) + if (read_list(xli, (qse_xli_list_t*)pair->val, (val_iffy? &scm_val_iffy: QSE_NULL)) <= -1) goto oops; + + if (!MATCH(xli, QSE_XLI_TOK_RBRACE)) { - qse_xli_seterror (xli, QSE_XLI_ERBRCE, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + qse_xli_seterror (xli, QSE_XLI_ERBRACE, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); goto oops; } - if (get_token (xli) <= -1) goto oops; + if (get_token(xli) <= -1) goto oops; /* semicolon is optional for a list */ - if (MATCH (xli, QSE_XLI_TOK_SEMICOLON)) + if (MATCH(xli, QSE_XLI_TOK_SEMICOLON)) { /* skip the semicolon */ if (get_token (xli) <= -1) goto oops; @@ -1022,7 +981,7 @@ static int read_pair (qse_xli_t* xli, const qse_char_t* keytag, const qse_xli_sc /* TODO: check against schema */ } - else if (MATCH (xli, QSE_XLI_TOK_SEMICOLON)) + else if (MATCH(xli, QSE_XLI_TOK_SEMICOLON)) { if (xli->opt.trait & QSE_XLI_NONIL) { @@ -1087,18 +1046,16 @@ qse_xli_list_link_t* qse_xli_makelistlink (qse_xli_t* xli, qse_xli_list_t* parli void qse_xli_freelistlink (qse_xli_t* xli, qse_xli_list_link_t* link) { + QSE_ASSERT (xli->parlink == link); xli->parlink = link->next; qse_xli_freemem (xli, link); } -#if 0 -static int __read_array (qse_xli_t* xli, int opt_outer_brace) +static int __read_list (qse_xli_t* xli, const qse_xli_scm_t* override) { - qse_size_t pair_count = 0; - while (1) { - if (MATCH (xli, QSE_XLI_TOK_XINCLUDE)) + if (MATCH(xli, QSE_XLI_TOK_XINCLUDE)) { if (get_token(xli) <= -1) return -1; @@ -1110,85 +1067,12 @@ static int __read_array (qse_xli_t* xli, int opt_outer_brace) if (begin_include (xli) <= -1) return -1; } - else if (opt_outer_brace == 1 && pair_count == 0 && MATCH(xli, QSE_XLI_TOK_LBRACK)) - { - opt_outer_brace++; - if (get_token(xli) <= -1) return -1; - } - else if (MATCH(xli, QSE_XLI_TOK_IDENT) || MATCH(xli, QSE_XLI_TOK_SQSTR) || MATCH(xli, QSE_XLI_TOK_DQSTR)) - { - //if (read_pair(xli, QSE_NULL, override) <= -1) return -1; - pair_count++; - } - else if (MATCH(xli, QSE_XLI_TOK_LBRACE)) - { - - } - else if (MATCH(xli, QSE_XLI_TOK_LBRACK)) - { - if (get_token(xli) <= -1) return -1; - - if (__read_array(xli, 0) <= -1) return -1; - - if (!MATCH(xli, QSE_XLI_TOK_RBRACK)) - { - } - - if (get_token(xli) <= -1) return -1; - } - else if (MATCH(xli, QSE_XLI_TOK_TEXT)) - { - if (get_token(xli) <= -1) return -1; - } - else - { - break; - } - } - - if (opt_outer_brace >= 2) - { - if (!MATCH(xli, QSE_XLI_TOK_RBRACE)) - { - qse_xli_seterror (xli, QSE_XLI_ERBRCE, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - return -1; - } - if (get_token(xli) <= -1) return -1; - } - - return 0; -} -#endif - -static int __read_list (qse_xli_t* xli, const qse_xli_scm_t* override, int opt_outer_brace) -{ - qse_size_t pair_count = 0; - - while (1) - { - if (MATCH (xli, QSE_XLI_TOK_XINCLUDE)) - { - if (get_token(xli) <= -1) return -1; - - if (!MATCH(xli,QSE_XLI_TOK_SQSTR) && !MATCH(xli,QSE_XLI_TOK_DQSTR)) - { - qse_xli_seterror (xli, QSE_XLI_EINCLSTR, QSE_NULL, &xli->tok.loc); - return -1; - } - - if (begin_include (xli) <= -1) return -1; - } - else if (opt_outer_brace == 1 && pair_count == 0 && MATCH(xli, QSE_XLI_TOK_LBRACE)) - { - opt_outer_brace++; - if (get_token(xli) <= -1) return -1; - } else if ((xli->opt.trait & QSE_XLI_KEYTAG) && MATCH(xli, QSE_XLI_TOK_TAG)) { qse_char_t* keytag; int x; - keytag = qse_strxdup(QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name), xli->mmgr); + keytag = qse_strxdup (QSE_STR_PTR(xli->tok.name), QSE_STR_LEN(xli->tok.name), xli->mmgr); if (keytag == QSE_NULL) { qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); @@ -1201,7 +1085,7 @@ static int __read_list (qse_xli_t* xli, const qse_xli_scm_t* override, int opt_o return -1; } - if (!MATCH(xli,QSE_XLI_TOK_IDENT) && !MATCH(xli,QSE_XLI_TOK_SQSTR) && !MATCH(xli,QSE_XLI_TOK_DQSTR)) + if (!MATCH(xli, QSE_XLI_TOK_IDENT) && !MATCH(xli, QSE_XLI_TOK_DQSTR) && !MATCH(xli, QSE_XLI_TOK_SQSTR)) { QSE_MMGR_FREE (xli->mmgr, keytag); qse_xli_seterror (xli, QSE_XLI_ENOKEY, QSE_NULL, &xli->tok.loc); @@ -1211,12 +1095,10 @@ static int __read_list (qse_xli_t* xli, const qse_xli_scm_t* override, int opt_o x = read_pair(xli, keytag, override); QSE_MMGR_FREE (xli->mmgr, keytag); if (x <= -1) return -1; - pair_count++; } - else if (MATCH(xli, QSE_XLI_TOK_IDENT) || MATCH(xli, QSE_XLI_TOK_SQSTR) || MATCH(xli, QSE_XLI_TOK_DQSTR)) + else if (MATCH(xli, QSE_XLI_TOK_IDENT) || MATCH(xli, QSE_XLI_TOK_DQSTR) || MATCH(xli, QSE_XLI_TOK_SQSTR)) { if (read_pair(xli, QSE_NULL, override) <= -1) return -1; - pair_count++; } else if (MATCH(xli, QSE_XLI_TOK_TEXT)) { @@ -1228,16 +1110,6 @@ static int __read_list (qse_xli_t* xli, const qse_xli_scm_t* override, int opt_o } } - if (opt_outer_brace >= 2) - { - if (!MATCH(xli, QSE_XLI_TOK_RBRACE)) - { - qse_xli_seterror (xli, QSE_XLI_ERBRCE, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - return -1; - } - if (get_token(xli) <= -1) return -1; - } - return 0; } @@ -1248,17 +1120,19 @@ static int read_list (qse_xli_t* xli, qse_xli_list_t* parlist, const qse_xli_scm link = qse_xli_makelistlink(xli, parlist); if (link == QSE_NULL) return -1; + QSE_ASSERT (link == xli->parlink); + /* get_token() here is to read the token after the left brace. * it must be called after the xli->parlink has been updated * in case there are comments at the beginning of the list */ - if (get_token(xli) <= -1 || __read_list(xli, override, 0) <= -1) + if (get_token(xli) <= -1 || __read_list(xli, override) <= -1) { qse_xli_freelistlink (xli, link); return -1; } QSE_ASSERT (link == xli->parlink); - qse_xli_freelistlink(xli, link); + qse_xli_freelistlink (xli, link); return 0; } @@ -1267,18 +1141,19 @@ static int read_root_list (qse_xli_t* xli) { qse_xli_list_link_t* link; - link = qse_xli_makelistlink(xli, &xli->root->list); + link = qse_xli_makelistlink (xli, &xli->root->list); if (!link) return -1; -/* TODO: pass opt_outer_brace 1 to __read_list only if a certian option is enabled */ - if (qse_xli_getchar(xli) <= -1 || get_token(xli) <= -1 || __read_list(xli, QSE_NULL, 1) <= -1) + QSE_ASSERT (link == xli->parlink); + + if (qse_xli_getchar(xli) <= -1 || get_token(xli) <= -1 || __read_list(xli, QSE_NULL) <= -1) { qse_xli_freelistlink (xli, link); return -1; } QSE_ASSERT (link == xli->parlink); - qse_xli_freelistlink(xli, link); + qse_xli_freelistlink (xli, link); return 0; } @@ -1322,7 +1197,7 @@ int qse_xli_read (qse_xli_t* xli, qse_xli_io_impl_t io) QSE_ASSERT (xli->parlink == QSE_NULL); - if (!MATCH (xli, QSE_XLI_TOK_EOF)) + if (!MATCH(xli, QSE_XLI_TOK_EOF)) { qse_xli_seterror (xli, QSE_XLI_ESYNTAX, QSE_NULL, &xli->tok.loc); goto oops; diff --git a/qse/lib/xli/std.c b/qse/lib/xli/std.c index ec1f0061..a9d08641 100644 --- a/qse/lib/xli/std.c +++ b/qse/lib/xli/std.c @@ -123,7 +123,7 @@ static qse_sio_t* open_sio (qse_xli_t* xli, const qse_char_t* file, int flags) if (sio == QSE_NULL) { qse_cstr_t errarg; - errarg.ptr = file; + errarg.ptr = (qse_char_t*)file; errarg.len = qse_strlen(file); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &errarg); } @@ -241,7 +241,7 @@ static qse_ssize_t sf_in_open (qse_xli_t* xli, qse_xli_io_arg_t* arg, xtn_t* xtn if (arg->handle == QSE_NULL) { qse_cstr_t ea; - ea.ptr = arg->name; + ea.ptr = (qse_char_t*)arg->name; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); return -1; @@ -299,7 +299,7 @@ static qse_ssize_t sf_in_read ( if (n <= -1) { qse_cstr_t ea; - ea.ptr = xtn->s.in.x->u.file.path; + ea.ptr = (qse_char_t*)xtn->s.in.x->u.file.path; if (ea.ptr == QSE_NULL) ea.ptr = sio_std_names[QSE_SIO_STDIN].ptr; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); @@ -335,7 +335,7 @@ static qse_ssize_t sf_in_read ( if (n <= -1) { qse_cstr_t ea; - ea.ptr = arg->name; + ea.ptr = (qse_char_t*)arg->name; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); } @@ -468,7 +468,7 @@ static qse_ssize_t sf_out_open (qse_xli_t* xli, qse_xli_io_arg_t* arg, xtn_t* xt if (arg->handle == QSE_NULL) { qse_cstr_t ea; - ea.ptr = arg->name; + ea.ptr = (qse_char_t*)arg->name; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); return -1; @@ -526,7 +526,7 @@ static qse_ssize_t sf_out_write ( if (n <= -1) { qse_cstr_t ea; - ea.ptr = xtn->s.out.x->u.file.path; + ea.ptr = (qse_char_t*)xtn->s.out.x->u.file.path; if (ea.ptr == QSE_NULL) ea.ptr = sio_std_names[QSE_SIO_STDOUT].ptr; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); @@ -564,7 +564,7 @@ static qse_ssize_t sf_out_write ( if (n <= -1) { qse_cstr_t ea; - ea.ptr = arg->name; + ea.ptr = (qse_char_t*)arg->name; ea.len = qse_strlen(ea.ptr); qse_xli_seterrnum (xli, QSE_XLI_EIOFIL, &ea); } @@ -611,7 +611,7 @@ int qse_xli_readstd (qse_xli_t* xli, qse_xli_iostd_t* in) } xtn->s.in.x = in; - return qse_xli_read (xli, sf_in); + return qse_xli_read(xli, sf_in); } @@ -629,9 +629,25 @@ int qse_xli_readinistd (qse_xli_t* xli, qse_xli_iostd_t* in) } xtn->s.in.x = in; - return qse_xli_readini (xli, sf_in); + return qse_xli_readini(xli, sf_in); } +int qse_xli_readjsonstd (qse_xli_t* xli, qse_xli_iostd_t* in) +{ + xtn_t* xtn = (xtn_t*) QSE_XTN (xli); + + if (in == QSE_NULL || (in->type != QSE_XLI_IOSTD_FILE && + in->type != QSE_XLI_IOSTD_STR)) + { + /* the input is a must. at least 1 file or 1 string + * must be specified */ + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return -1; + } + + xtn->s.in.x = in; + return qse_xli_readjson(xli, sf_in); +} int qse_xli_writestd (qse_xli_t* xli, qse_xli_list_t* root_list, qse_xli_iostd_t* out) { @@ -692,3 +708,33 @@ int qse_xli_writeinistd (qse_xli_t* xli, qse_xli_list_t* root_list, qse_xli_iost return n; } + +int qse_xli_writejsonstd (qse_xli_t* xli, qse_xli_list_t* root_list, qse_xli_iostd_t* out) +{ + int n; + xtn_t* xtn = (xtn_t*) QSE_XTN (xli); + + if (out == QSE_NULL || (out->type != QSE_XLI_IOSTD_FILE && + out->type != QSE_XLI_IOSTD_STR)) + { + /* the input is a must. at least 1 file or 1 string + * must be specified */ + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return -1; + } + + xtn->s.out.x = out; + n = qse_xli_writejson (xli, root_list, sf_out); + + if (out->type == QSE_XLI_IOSTD_STR) + { + if (n >= 0) + { + QSE_ASSERT (xtn->s.out.u.str.buf != QSE_NULL); + qse_str_yield (xtn->s.out.u.str.buf, &out->u.str, 0); + } + if (xtn->s.out.u.str.buf) qse_str_close (xtn->s.out.u.str.buf); + } + + return n; +} diff --git a/qse/lib/xli/write-ini.c b/qse/lib/xli/write-ini.c index cc8ea5e5..e8121b82 100644 --- a/qse/lib/xli/write-ini.c +++ b/qse/lib/xli/write-ini.c @@ -119,7 +119,16 @@ static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) } case QSE_XLI_LIST: - if (write_list (xli, (qse_xli_list_t*)pair->val, depth + 1) <= -1) return -1; + if (depth < 1) + { + if (write_list(xli, (qse_xli_list_t*)pair->val, depth + 1) <= -1 || + write_to_current_stream (xli, QSE_T("\n"), 1) <= -1) return -1; + } + else + { + /* the ini format doesn't support deep nesting */ + if (write_to_current_stream (xli, QSE_T("={}\n"), 4) <= -1) return -1; + } break; } break; @@ -129,9 +138,9 @@ static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) { const qse_char_t* str = ((qse_xli_text_t*)curatom)->ptr; - if (write_to_current_stream (xli, QSE_T(";"), 1) <= -1 || - write_to_current_stream (xli, str, qse_strlen(str)) <= -1 || - write_to_current_stream (xli, QSE_T("\n"), 1) <= -1) return -1; + if (write_to_current_stream(xli, QSE_T(";"), 1) <= -1 || + write_to_current_stream(xli, str, qse_strlen(str)) <= -1 || + write_to_current_stream(xli, QSE_T("\n"), 1) <= -1) return -1; break; } diff --git a/qse/lib/xli/write-json.c b/qse/lib/xli/write-json.c new file mode 100644 index 00000000..44fb86cb --- /dev/null +++ b/qse/lib/xli/write-json.c @@ -0,0 +1,357 @@ +/* + * $Id$ + * + Copyright (c) 2006-2014 Chung, Hyung-Hwan. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xli.h" +#include + +typedef struct arg_data_t arg_data_t; +struct arg_data_t +{ + int org_depth; +}; + + +#if 0 +static int write_to_current_stream(qse_xli_t* xli, const qse_char_t* ptr, qse_size_t len, int escape) +{ + qse_xli_io_arg_t* arg; + qse_size_t i; + + arg = xli->wio.inp; + + for (i = 0; i < len; i++) + { + if (escape && (ptr[i] == QSE_T('\\') || ptr[i] == QSE_T('\"'))) + { + if (arg->b.len + 2 > QSE_COUNTOF(arg->b.buf) && qse_xli_flushwstream (xli, arg) <= -1) return -1; + arg->b.buf[arg->b.len++] = QSE_T('\\'); + } + else + { + if (arg->b.len + 1 > QSE_COUNTOF(arg->b.buf) && qse_xli_flushwstream (xli, arg) <= -1) return -1; + } + arg->b.buf[arg->b.len++] = ptr[i]; + } + + return 0; +} + +static int write_indentation (qse_xli_t* xli, int depth) +{ + static const qse_char_t tabs[16] = + { + QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), + QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), + QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), + QSE_T('\t'), QSE_T('\t'), QSE_T('\t'), QSE_T('\t') + }; + + if (depth <= QSE_COUNTOF(tabs)) + { + if (write_to_current_stream(xli, tabs, depth, 0) <= -1) return -1; + } + else + { + int i; + if (write_to_current_stream(xli, tabs, QSE_COUNTOF(tabs), 0) <= -1) return -1; + for (i = QSE_COUNTOF(tabs); i < depth; i++) + { + if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + } + } + return 0; +} + + +static int key_needs_quoting (qse_xli_t* xli, const qse_char_t* str, int nstr) +{ + /* this determines if a key or an alias requires quoting for output. + * NSTR is not taken into account because it's only allowed as a value */ + + /* refer to the tokenization rule in get_token_into() in read.c */ + qse_char_t c; + + c = *str++; + if (c == QSE_T('\0')) return 1; /* an empty string requires a quoting */ + + if (c == QSE_T('_') || QSE_ISALPHA(c) || (!nstr && (xli->opt.trait & QSE_XLI_LEADDIGIT) && QSE_ISDIGIT(c))) + { + int lead_digit = QSE_ISDIGIT(c); + int all_digits = 1; + + /* a normal identifier can be composed of wider varieties of + * characters than a keyword/directive */ + while (1) + { + c = *str++; + if (c == QSE_T('\0')) break; + + if (c == QSE_T('_') || c == QSE_T('-') || + (!(xli->opt.trait & QSE_XLI_JSON) && c == QSE_T(':')) || + c == QSE_T('*') || c == QSE_T('/') || QSE_ISALPHA(c)) + { + all_digits = 0; + } + else if (QSE_ISDIGIT(c)) + { + /* nothing to do */ + } + else + { + /* a disallowed character for an identifier */ + return 1; /* quote it */ + } + } + + if (lead_digit && all_digits) + { + /* if an identifier begins with a digit, it must contain a non-digit character */ + /* in fact, it is not a valid identifer. so quote it */ + return 1; + } + else + { + /* this must be a normal identifer */ + return 0; /* no quoting needed */ + } + } + else if (nstr && QSE_ISDIGIT(c)) + { + do + { + c = *str++; + if (c == QSE_T('\0')) return 0; /* it's a numeric string */ + } + while (QSE_ISDIGIT(c)); + } + + /* quote all the rest */ + return 1; +} + +static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) +{ + static struct + { + qse_char_t* qptr; + qse_size_t qlen; + } quotes[] = + { + { QSE_T(""), 0 }, + { QSE_T("\'"), 1 }, + { QSE_T("\""), 1 } + }; + + static qse_char_t tag_opener[] = { QSE_T('['), QSE_T('(') }; + static qse_char_t tag_closer[] = { QSE_T(']'), QSE_T(')') }; + + static struct + { + qse_char_t* ptr; + qse_size_t len; + } assign_symbol[] = + { + { QSE_T(" = "), 3 }, + { QSE_T(": "), 2 } + }; + + static struct + { + qse_char_t* ptr; + qse_size_t len; + } list_assign_symbol[] = + { + { QSE_T(" "), 1 }, + { QSE_T(": "), 2 } + }; + + qse_xli_atom_t* curatom; + int tag_mode = (xli->opt.trait & QSE_XLI_JSON)? 1: 0; + + for (curatom = list->head; curatom; curatom = curatom->next) + { + switch (curatom->type) + { + case QSE_XLI_PAIR: + { + int qtype; + qse_xli_pair_t* pair = (qse_xli_pair_t*)curatom; + + if (write_indentation(xli, depth) <= -1) return -1; + + if (pair->tag) + { + if (write_to_current_stream(xli, &tag_opener[tag_mode], 1, 0) <= -1 || + write_to_current_stream(xli, pair->tag, qse_strlen(pair->tag), 0) <= -1 || + write_to_current_stream(xli, &tag_closer[tag_mode], 1, 0) <= -1) return -1; + } + + QSE_ASSERT(pair->_key_quoted >= 0 && pair->_key_quoted < QSE_COUNTOF(quotes)); + qtype = pair->_key_quoted; + if (qtype <= 0 && key_needs_quoting(xli, pair->key, 0)) qtype = 2; /* force double quoting */ + + if (write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1 || + write_to_current_stream(xli, pair->key, qse_strlen(pair->key), (qtype >= 2)) <= -1 || + write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1) return -1; + + if (pair->alias) + { + QSE_ASSERT(pair->_alias_quoted >= 0 && pair->_alias_quoted < QSE_COUNTOF(quotes)); + qtype = pair->_alias_quoted; + if (qtype <= 0 && key_needs_quoting(xli, pair->alias, 1)) qtype = 2; + + if (write_to_current_stream(xli, QSE_T(" "), 1, 0) <= -1 || + write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1 || + write_to_current_stream(xli, pair->alias, qse_strlen(pair->alias), (qtype >= 2)) <= -1 || + write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1) return -1; + } + + switch (pair->val->type) + { + case QSE_XLI_NIL: + if (write_to_current_stream(xli, QSE_T(";\n"), 2, 0) <= -1) return -1; + break; + + case QSE_XLI_STR: + { + qse_xli_str_t* str = (qse_xli_str_t*)pair->val; + + if (write_to_current_stream(xli, assign_symbol[tag_mode].ptr, assign_symbol[tag_mode].len, 0) <= -1) return -1; + + while (1) + { + if (str->tag) + { + if (write_to_current_stream(xli, &tag_opener[tag_mode], 1, 0) <= -1 || + write_to_current_stream(xli, str->tag, qse_strlen(str->tag), 0) <= -1 || + write_to_current_stream(xli, &tag_closer[tag_mode], 1, 0) <= -1) return -1; + } + + if (write_to_current_stream(xli, QSE_T("\""), 1, 0) <= -1 || + write_to_current_stream(xli, str->ptr, str->len, 1) <= -1 || + write_to_current_stream(xli, QSE_T("\""), 1, 0) <= -1) return -1; + if (!str->next) break; + + if (write_to_current_stream(xli, QSE_T(", "), 2, 0) <= -1) return -1; + str = str->next; + } + if (write_to_current_stream(xli, QSE_T(";\n"), 2, 0) <= -1) return -1; + break; + } + + case QSE_XLI_LIST: + { + if (write_to_current_stream(xli, list_assign_symbol[tag_mode].ptr, list_assign_symbol[tag_mode].len, 0) <= -1) return -1; + + if (write_to_current_stream(xli, QSE_T("{\n"), 2, 0) <= -1 || + write_list (xli, (qse_xli_list_t*)pair->val, depth + 1) <= -1 || + write_indentation (xli, depth) <= -1 || + write_to_current_stream(xli, QSE_T("}\n"), 2, 0) <= -1) return -1; + break; + } + } + break; + } + + case QSE_XLI_TEXT: + { + int i; + const qse_char_t* str = ((qse_xli_text_t*)curatom)->ptr; + + for (i = 0; i < depth; i++) + { + if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + } + + if (write_to_current_stream(xli, QSE_T("#"), 1, 0) <= -1 || + write_to_current_stream(xli, str, qse_strlen(str), 0) <= -1 || + write_to_current_stream(xli, QSE_T("\n"), 1, 0) <= -1) return -1; + break; + } + + case QSE_XLI_FILE: + { + int i; + const qse_char_t* path = ((qse_xli_file_t*)curatom)->path; + + for (i = 0; i < depth; i++) + { + if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + } + + if (write_to_current_stream(xli, QSE_T("@include \""), 10, 0) <= -1 || + write_to_current_stream(xli, path, qse_strlen(path), 1) <= -1 || + write_to_current_stream(xli, QSE_T("\";\n"), 3, 0) <= -1) return -1; + + if (qse_xli_openwstream (xli, ((qse_xli_file_t*)curatom)->path, depth) <= -1) return -1; + depth = 0; + break; + } + + case QSE_XLI_EOF: + if (qse_xli_closeactivewstream (xli, &depth) <= -1) return -1; + break; + } + } + + return 0; +} +#endif + + +int qse_xli_writejson (qse_xli_t* xli, qse_xli_list_t* root_list, qse_xli_io_impl_t io) +{ + int n; + +#if 0 + if (io == QSE_NULL) + { + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return -1; + } + + QSE_MEMSET (&xli->wio, 0, QSE_SIZEOF(xli->wio)); + xli->wio.impl = io; + xli->wio.inp = &xli->wio.top; + + qse_xli_seterrnum (xli, QSE_XLI_ENOERR, QSE_NULL); + qse_xli_clearwionames (xli); + + /* open the top level stream */ + if (qse_xli_openwstream (xli, QSE_NULL, 0) <= -1) return -1; + + /* begin writing the root list */ + n = write_list (xli, (root_list? root_list: &xli->root->list), 0); + + /* close all open streams. there should be only the + * top-level stream here if there occurred no errors */ + while (xli->wio.inp) qse_xli_closeactivewstream (xli, QSE_NULL); +#endif + + return n; +} + + diff --git a/qse/lib/xli/write.c b/qse/lib/xli/write.c index 6277d46c..1a612d1f 100644 --- a/qse/lib/xli/write.c +++ b/qse/lib/xli/write.c @@ -139,7 +139,7 @@ int qse_xli_closeactivewstream (qse_xli_t* xli, int* org_depth) return 0; } -static int write_to_current_stream(qse_xli_t* xli, const qse_char_t* ptr, qse_size_t len, int escape) +static int write_to_current_stream (qse_xli_t* xli, const qse_char_t* ptr, qse_size_t len, int escape) { qse_xli_io_arg_t* arg; qse_size_t i; @@ -175,21 +175,20 @@ static int write_indentation (qse_xli_t* xli, int depth) if (depth <= QSE_COUNTOF(tabs)) { - if (write_to_current_stream(xli, tabs, depth, 0) <= -1) return -1; + if (write_to_current_stream (xli, tabs, depth, 0) <= -1) return -1; } else { int i; - if (write_to_current_stream(xli, tabs, QSE_COUNTOF(tabs), 0) <= -1) return -1; + if (write_to_current_stream (xli, tabs, QSE_COUNTOF(tabs), 0) <= -1) return -1; for (i = QSE_COUNTOF(tabs); i < depth; i++) { - if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("\t"), 1, 0) <= -1) return -1; } } return 0; } - static int key_needs_quoting (qse_xli_t* xli, const qse_char_t* str, int nstr) { /* this determines if a key or an alias requires quoting for output. @@ -213,8 +212,7 @@ static int key_needs_quoting (qse_xli_t* xli, const qse_char_t* str, int nstr) c = *str++; if (c == QSE_T('\0')) break; - if (c == QSE_T('_') || c == QSE_T('-') || - (!(xli->opt.trait & QSE_XLI_JSON) && c == QSE_T(':')) || + if (c == QSE_T('_') || c == QSE_T('-') || c == QSE_T(':') || c == QSE_T('*') || c == QSE_T('/') || QSE_ISALPHA(c)) { all_digits = 0; @@ -258,122 +256,78 @@ static int key_needs_quoting (qse_xli_t* xli, const qse_char_t* str, int nstr) static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) { - static struct - { - qse_char_t* qptr; - qse_size_t qlen; - } quotes[] = - { - { QSE_T(""), 0 }, - { QSE_T("\'"), 1 }, - { QSE_T("\""), 1 } - }; - - static qse_char_t tag_opener[] = { QSE_T('['), QSE_T('(') }; - static qse_char_t tag_closer[] = { QSE_T(']'), QSE_T(')') }; - - static struct - { - qse_char_t* ptr; - qse_size_t len; - } assign_symbol[] = - { - { QSE_T(" = "), 3 }, - { QSE_T(": "), 2 } - }; - - static struct - { - qse_char_t* ptr; - qse_size_t len; - } list_assign_symbol[] = - { - { QSE_T(" "), 1 }, - { QSE_T(": "), 2 } - }; - qse_xli_atom_t* curatom; - int tag_mode = (xli->opt.trait & QSE_XLI_JSON)? 1: 0; for (curatom = list->head; curatom; curatom = curatom->next) { + int quote_key = 0; + switch (curatom->type) { case QSE_XLI_PAIR: { - int qtype; qse_xli_pair_t* pair = (qse_xli_pair_t*)curatom; - - if (write_indentation(xli, depth) <= -1) return -1; + + if (write_indentation (xli, depth) <= -1) return -1; if (pair->tag) { - if (write_to_current_stream(xli, &tag_opener[tag_mode], 1, 0) <= -1 || - write_to_current_stream(xli, pair->tag, qse_strlen(pair->tag), 0) <= -1 || - write_to_current_stream(xli, &tag_closer[tag_mode], 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("["), 1, 0) <= -1 || + write_to_current_stream (xli, pair->tag, qse_strlen(pair->tag), 0) <= -1 || + write_to_current_stream (xli, QSE_T("]"), 1, 0) <= -1) return -1; } - QSE_ASSERT(pair->_key_quoted >= 0 && pair->_key_quoted < QSE_COUNTOF(quotes)); - qtype = pair->_key_quoted; - if (qtype <= 0 && key_needs_quoting(xli, pair->key, 0)) qtype = 2; /* force double quoting */ + if (key_needs_quoting(xli, pair->key, 0)) quote_key = 1; - if (write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1 || - write_to_current_stream(xli, pair->key, qse_strlen(pair->key), (qtype >= 2)) <= -1 || - write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1) return -1; + if ((quote_key && write_to_current_stream (xli, QSE_T("\""), 1, 0) <= -1) || + write_to_current_stream (xli, pair->key, qse_strlen(pair->key), 0) <= -1 || + (quote_key && write_to_current_stream (xli, QSE_T("\""), 1, 0) <= -1)) return -1; if (pair->alias) { - QSE_ASSERT(pair->_alias_quoted >= 0 && pair->_alias_quoted < QSE_COUNTOF(quotes)); - qtype = pair->_alias_quoted; - if (qtype <= 0 && key_needs_quoting(xli, pair->alias, 1)) qtype = 2; - - if (write_to_current_stream(xli, QSE_T(" "), 1, 0) <= -1 || - write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1 || - write_to_current_stream(xli, pair->alias, qse_strlen(pair->alias), (qtype >= 2)) <= -1 || - write_to_current_stream(xli, quotes[qtype].qptr, quotes[qtype].qlen, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T(" \""), 2, 0) <= -1 || + write_to_current_stream (xli, pair->alias, qse_strlen(pair->alias), 1) <= -1 || + write_to_current_stream (xli, QSE_T("\""), 1, 0) <= -1) return -1; } switch (pair->val->type) { case QSE_XLI_NIL: - if (write_to_current_stream(xli, QSE_T(";\n"), 2, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T(";\n"), 2, 0) <= -1) return -1; break; case QSE_XLI_STR: { qse_xli_str_t* str = (qse_xli_str_t*)pair->val; - if (write_to_current_stream(xli, assign_symbol[tag_mode].ptr, assign_symbol[tag_mode].len, 0) <= -1) return -1; - + if (write_to_current_stream (xli, QSE_T(" = "), 3, 0) <= -1) return -1; while (1) { if (str->tag) { - if (write_to_current_stream(xli, QSE_T("["), 1, 0) <= -1 || - write_to_current_stream(xli, str->tag, qse_strlen(str->tag), 0) <= -1 || - write_to_current_stream(xli, QSE_T("]"), 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("["), 1, 0) <= -1 || + write_to_current_stream (xli, str->tag, qse_strlen(str->tag), 0) <= -1 || + write_to_current_stream (xli, QSE_T("]"), 1, 0) <= -1) return -1; } - - if (write_to_current_stream(xli, QSE_T("\""), 1, 0) <= -1 || - write_to_current_stream(xli, str->ptr, str->len, 1) <= -1 || - write_to_current_stream(xli, QSE_T("\""), 1, 0) <= -1) return -1; + + if (write_to_current_stream (xli, QSE_T("\""), 1, 0) <= -1 || + write_to_current_stream (xli, str->ptr, str->len, 1) <= -1 || + write_to_current_stream (xli, QSE_T("\""), 1, 0) <= -1) return -1; if (!str->next) break; - if (write_to_current_stream(xli, QSE_T(", "), 2, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T(", "), 2, 0) <= -1) return -1; str = str->next; } - if (write_to_current_stream(xli, QSE_T(";\n"), 2, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T(";\n"), 2, 0) <= -1) return -1; break; } case QSE_XLI_LIST: { - if (write_to_current_stream(xli, list_assign_symbol[tag_mode].ptr, list_assign_symbol[tag_mode].len, 0) <= -1) return -1; - - if (write_to_current_stream(xli, QSE_T("{\n"), 2, 0) <= -1 || + if (write_to_current_stream (xli, QSE_T(" {\n"), 3, 0) <= -1 || write_list (xli, (qse_xli_list_t*)pair->val, depth + 1) <= -1 || write_indentation (xli, depth) <= -1 || - write_to_current_stream(xli, QSE_T("}\n"), 2, 0) <= -1) return -1; + write_to_current_stream (xli, QSE_T("}\n"), 2, 0) <= -1) return -1; break; } } @@ -387,12 +341,12 @@ static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) for (i = 0; i < depth; i++) { - if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("\t"), 1, 0) <= -1) return -1; } - if (write_to_current_stream(xli, QSE_T("#"), 1, 0) <= -1 || - write_to_current_stream(xli, str, qse_strlen(str), 0) <= -1 || - write_to_current_stream(xli, QSE_T("\n"), 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("#"), 1, 0) <= -1 || + write_to_current_stream (xli, str, qse_strlen(str), 0) <= -1 || + write_to_current_stream (xli, QSE_T("\n"), 1, 0) <= -1) return -1; break; } @@ -403,12 +357,12 @@ static int write_list (qse_xli_t* xli, qse_xli_list_t* list, int depth) for (i = 0; i < depth; i++) { - if (write_to_current_stream(xli, QSE_T("\t"), 1, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("\t"), 1, 0) <= -1) return -1; } - if (write_to_current_stream(xli, QSE_T("@include \""), 10, 0) <= -1 || - write_to_current_stream(xli, path, qse_strlen(path), 1) <= -1 || - write_to_current_stream(xli, QSE_T("\";\n"), 3, 0) <= -1) return -1; + if (write_to_current_stream (xli, QSE_T("@include \""), 10, 0) <= -1 || + write_to_current_stream (xli, path, qse_strlen(path), 1) <= -1 || + write_to_current_stream (xli, QSE_T("\";\n"), 3, 0) <= -1) return -1; if (qse_xli_openwstream (xli, ((qse_xli_file_t*)curatom)->path, depth) <= -1) return -1; depth = 0; diff --git a/qse/lib/xli/xli.c b/qse/lib/xli/xli.c index 9c63b40a..f4bc0e0f 100644 --- a/qse/lib/xli/xli.c +++ b/qse/lib/xli/xli.c @@ -28,9 +28,9 @@ #include static qse_xli_root_list_t* make_root (qse_xli_t* xli); -static void free_val (qse_xli_root_list_t* xli, qse_xli_val_t* val); -static void free_list (qse_xli_root_list_t* xli, qse_xli_list_t* list); -static void free_atom (qse_xli_root_list_t* xli, qse_xli_atom_t* atom); +static void free_val (qse_xli_root_list_t* root, qse_xli_val_t* val); +static void free_list (qse_xli_root_list_t* root, qse_xli_list_t* list); +static void free_atom (qse_xli_root_list_t* root, qse_xli_atom_t* atom); qse_xli_t* qse_xli_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_size_t rootxtnsize, qse_xli_errnum_t* errnum) { @@ -289,18 +289,18 @@ qse_xli_pair_t* qse_xli_insertpair ( qse_cstr_t a, * ap = QSE_NULL; qse_cstr_t t, * tp = QSE_NULL; - k.ptr = key; - k.len = qse_strlen (key); + k.ptr = (qse_char_t*)key; + k.len = qse_strlen(key); if (alias) { - a.ptr = alias; + a.ptr = (qse_char_t*)alias; a.len = qse_strlen (alias); ap = &a; } if (keytag) { - t.ptr = keytag; + t.ptr = (qse_char_t*)keytag; t.len = qse_strlen (keytag); tp = &t; } @@ -339,29 +339,10 @@ void qse_xli_deletepair (qse_xli_t* xli, qse_xli_pair_t* pair) } /* ------------------------------------------------------ */ -qse_xli_pair_t* qse_xli_insertpairwithemptylist ( - qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, - const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag) -{ - qse_xli_list_t* val; - qse_xli_pair_t* tmp; - val = qse_xli_callocmem (xli, QSE_SIZEOF(*val)); - if (!val) return QSE_NULL; - - val->type = QSE_XLI_LIST; - tmp = qse_xli_insertpair (xli, parent, peer, key, alias, keytag, (qse_xli_val_t*)val); - if (!tmp) qse_xli_freemem (xli, val); - return tmp; -} - -qse_xli_pair_t* qse_xli_insertpairwithstr ( - qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, - const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag, - const qse_cstr_t* value, const qse_char_t* strtag) +qse_xli_str_t* qse_xli_makestrval (qse_xli_t* xli, const qse_cstr_t* value, const qse_char_t* strtag) { qse_xli_str_t* val; - qse_xli_pair_t* tmp; qse_size_t reqlen; reqlen = QSE_SIZEOF(*val) + ((value->len + 1) * QSE_SIZEOF(*value->ptr)); @@ -381,7 +362,52 @@ qse_xli_pair_t* qse_xli_insertpairwithstr ( val->tag = val->ptr + val->len + 1; qse_strcpy ((qse_char_t*)val->tag, strtag); } - + + return val; +} + +qse_xli_list_t* qse_xli_makelistval (qse_xli_t* xli) +{ + qse_xli_list_t* val; + + val = qse_xli_callocmem (xli, QSE_SIZEOF(*val)); + if (!val) return QSE_NULL; + + val->type = QSE_XLI_LIST; + val->head = QSE_NULL; + val->tail = QSE_NULL; + + return val; +} + +/* ------------------------------------------------------ */ + +qse_xli_pair_t* qse_xli_insertpairwithemptylist ( + qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, + const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag) +{ + qse_xli_list_t* val; + qse_xli_pair_t* tmp; + + val = qse_xli_makelistval(xli); + if (!val) return QSE_NULL; + + tmp = qse_xli_insertpair (xli, parent, peer, key, alias, keytag, (qse_xli_val_t*)val); + if (!tmp) qse_xli_freemem (xli, val); + return tmp; +} + +qse_xli_pair_t* qse_xli_insertpairwithstr ( + qse_xli_t* xli, qse_xli_list_t* parent, qse_xli_atom_t* peer, + const qse_char_t* key, const qse_char_t* alias, const qse_char_t* keytag, + const qse_cstr_t* value, const qse_char_t* strtag) +{ + qse_xli_str_t* val; + qse_xli_pair_t* tmp; + + val = qse_xli_makestrval (xli, value, strtag); + if (!val) return QSE_NULL; + tmp = qse_xli_insertpair (xli, parent, peer, key, alias, keytag, (qse_xli_val_t*)val); if (!tmp) qse_xli_freemem (xli, val); return tmp; @@ -496,28 +522,39 @@ static qse_xli_root_list_t* make_root (qse_xli_t* xli) return tmp; } + +static void unsafe_free_val (qse_xli_root_list_t* root, qse_xli_val_t* val) +{ + if (val->type == QSE_XLI_LIST) + { + free_list (root, (qse_xli_list_t*)val); + } + else if (val->type == QSE_XLI_STR) + { + qse_xli_str_t* cur, * next; + + cur = ((qse_xli_str_t*)val)->next; + while (cur) + { + next = cur->next; + QSE_MMGR_FREE (root->mmgr, cur); + cur = next; + } + } + + QSE_MMGR_FREE (root->mmgr, val); +} + +void qse_xli_freeval (qse_xli_t* xli, qse_xli_val_t* val) +{ + unsafe_free_val (xli->root, val); +} + static void free_val (qse_xli_root_list_t* root, qse_xli_val_t* val) { if ((qse_xli_nil_t*)val != &root->xnil) { - if (val->type == QSE_XLI_LIST) - { - free_list (root, (qse_xli_list_t*)val); - } - else if (val->type == QSE_XLI_STR) - { - qse_xli_str_t* cur, * next; - - cur = ((qse_xli_str_t*)val)->next; - while (cur) - { - next = cur->next; - QSE_MMGR_FREE (root->mmgr, cur); - cur = next; - } - } - - QSE_MMGR_FREE (root->mmgr, val); + unsafe_free_val (root, val); } } @@ -579,7 +616,7 @@ qse_xli_list_t* qse_xli_yieldroot (qse_xli_t* xli) { qse_xli_root_list_t* tmp, * tmp2; - tmp = make_root (xli); + tmp = make_root(xli); if (!tmp) return QSE_NULL; tmp2 = xli->root; @@ -732,7 +769,7 @@ const qse_char_t* get_next_fqpn_segment (qse_xli_t* xli, const qse_char_t* fqpn, * so if your alias contains these characters (in a quoted string), * you can't reference it using a dotted key name. */ seg->idxtype = FQPN_SEG_IDX_ALIAS; - seg->idx.alias.ptr = ptr; + seg->idx.alias.ptr = (qse_char_t*)ptr; while (*ptr != QSE_T('}') && *ptr != QSE_T('\0')) ptr++; seg->idx.alias.len = ptr - seg->idx.alias.ptr; @@ -1010,7 +1047,7 @@ int qse_xli_definepair (qse_xli_t* xli, const qse_char_t* fqpn, const qse_xli_sc return -1; } - if (qse_rbt_upsert (xli->schema, fqpn, qse_strlen(fqpn), scm, QSE_SIZEOF(*scm)) == QSE_NULL) + if (qse_rbt_upsert (xli->schema, (void*)fqpn, qse_strlen(fqpn), (void*)scm, QSE_SIZEOF(*scm)) == QSE_NULL) { qse_xli_seterrnum (xli, QSE_XLI_ENOMEM, QSE_NULL); return -1; @@ -1171,8 +1208,6 @@ noent: return QSE_NULL; #endif } - - #endif void* qse_getxlipairxtn (qse_xli_pair_t* pair) diff --git a/qse/lib/xli/xli.h b/qse/lib/xli/xli.h index 8219de4c..f3f1b2f4 100644 --- a/qse/lib/xli/xli.h +++ b/qse/lib/xli/xli.h @@ -36,8 +36,8 @@ enum qse_xli_tok_type_t { QSE_XLI_TOK_EOF, QSE_XLI_TOK_XINCLUDE, - QSE_XLI_TOK_SEMICOLON, QSE_XLI_TOK_COLON, + QSE_XLI_TOK_SEMICOLON, QSE_XLI_TOK_LBRACE, QSE_XLI_TOK_RBRACE, QSE_XLI_TOK_LBRACK, @@ -103,7 +103,7 @@ struct qse_xli_t qse_xli_ecb_t* ecb; qse_xli_root_list_t* root; - qse_xli_list_link_t* parlink; /* link that points to the list being read currently */ + qse_xli_list_link_t* parlink; /* internal use only - link that points to the list being read currently */ qse_str_t* dotted_curkey; qse_rbt_t* schema; @@ -138,12 +138,11 @@ int qse_xli_init (qse_xli_t* xli, qse_mmgr_t* mmgr, qse_size_t rootxtnsize); void qse_xli_fini (qse_xli_t* xli); const qse_char_t* qse_xli_dflerrstr ( - const qse_xli_t* xli, qse_xli_errnum_t errnum); + const qse_xli_t* xli, qse_xli_errnum_t errnum); void qse_xli_clearrionames (qse_xli_t* xli); void qse_xli_clearwionames (qse_xli_t* xli); - int qse_xli_getchar (qse_xli_t* xli); int qse_xli_openrstream (qse_xli_t* xli, qse_xli_io_arg_t* arg); int qse_xli_closeactiverstream (qse_xli_t* xli); @@ -155,6 +154,12 @@ int qse_xli_flushwstream (qse_xli_t* xli, qse_xli_io_arg_t* arg); qse_xli_list_link_t* qse_xli_makelistlink (qse_xli_t* xli, qse_xli_list_t* parlist); void qse_xli_freelistlink (qse_xli_t* xli, qse_xli_list_link_t* link); + +qse_xli_str_t* qse_xli_makestrval (qse_xli_t* xli, const qse_cstr_t* value, const qse_char_t* strtag); +qse_xli_list_t* qse_xli_makelistval (qse_xli_t* xli); +qse_xli_pair_t* qse_xli_makepairval (qse_xli_t* xli); +void qse_xli_freeval (qse_xli_t* xli, qse_xli_val_t* val); + #if defined(__cplusplus) } #endif