From 9532597507e271d5a082ada75b22f56e2c582e4a Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 31 Aug 2015 13:31:30 +0000 Subject: [PATCH] finished implementing the initial ini-format reader. added qse_xli_readinistd() --- qse/cmd/xli/xli.c | 2 +- qse/include/qse/xli/stdxli.h | 5 ++ qse/include/qse/xli/xli.h | 36 ++++---- qse/lib/xli/err.c | 2 +- qse/lib/xli/read-ini.c | 161 +++++++++++++++++++++++++++-------- qse/lib/xli/read.c | 4 +- qse/lib/xli/std.c | 25 ++++-- qse/lib/xli/xli.h | 3 +- 8 files changed, 174 insertions(+), 64 deletions(-) diff --git a/qse/cmd/xli/xli.c b/qse/cmd/xli/xli.c index 4647d8ff..4cfaf4f8 100644 --- a/qse/cmd/xli/xli.c +++ b/qse/cmd/xli/xli.c @@ -413,7 +413,7 @@ static int xli_main (int argc, qse_char_t* argv[]) in.u.file.path = g_input_file; in.u.file.cmgr = g_infile_cmgr; - if (qse_xli_readstd (xli, &in) <= -1) + if (qse_xli_readinistd (xli, &in) <= -1) { const qse_xli_loc_t* errloc; diff --git a/qse/include/qse/xli/stdxli.h b/qse/include/qse/xli/stdxli.h index b7bf8808..33ddc231 100644 --- a/qse/include/qse/xli/stdxli.h +++ b/qse/include/qse/xli/stdxli.h @@ -129,6 +129,11 @@ QSE_EXPORT int qse_xli_readstd ( qse_xli_iostd_t* in ); +QSE_EXPORT int qse_xli_readinistd ( + qse_xli_t* xli, + qse_xli_iostd_t* in +); + QSE_EXPORT int qse_xli_writestd ( qse_xli_t* xli, qse_xli_iostd_t* out diff --git a/qse/include/qse/xli/xli.h b/qse/include/qse/xli/xli.h index 61440955..81aea0c0 100644 --- a/qse/include/qse/xli/xli.h +++ b/qse/include/qse/xli/xli.h @@ -71,7 +71,7 @@ enum qse_xli_errnum_t QSE_XLI_ERBRCE, /**< } expected in place of '${0}' */ QSE_XLI_EPAVAL, /**< pair value expected in place of '${0}' */ QSE_XLI_ESTRNC, /**< string not closed */ - QSE_XLI_ETAGNC, /**< string tag not closed */ + QSE_XLI_ETAGNC, /**< tag not closed */ QSE_XLI_EINCLSTR ,/**< '@include' not followed by a string */ QSE_XLI_ELXCHR, /**< invalid character '${0} */ QSE_XLI_ETAGCHR, /**< invalid tag character '${0} */ @@ -543,9 +543,9 @@ QSE_EXPORT void qse_xli_seterrnum ( * message for a given error number. */ QSE_EXPORT void qse_xli_seterrmsg ( - qse_xli_t* xli, /**< stream editor */ - qse_xli_errnum_t errnum, /**< error number */ - const qse_char_t* errmsg, /**< error message */ + qse_xli_t* xli, /**< stream editor */ + qse_xli_errnum_t errnum, /**< error number */ + const qse_char_t* errmsg, /**< error message */ const qse_xli_loc_t* errloc /**< error location */ ); @@ -630,27 +630,27 @@ QSE_EXPORT qse_xli_pair_t* qse_xli_insertpairwithstrs ( ); QSE_EXPORT qse_xli_text_t* qse_xli_inserttext ( - qse_xli_t* xli, - qse_xli_list_t* parent, - qse_xli_atom_t* peer, + qse_xli_t* xli, + qse_xli_list_t* parent, + qse_xli_atom_t* peer, const qse_char_t* str ); QSE_EXPORT qse_xli_file_t* qse_xli_insertfile ( - qse_xli_t* xli, - qse_xli_list_t* parent, - qse_xli_atom_t* peer, + qse_xli_t* xli, + qse_xli_list_t* parent, + qse_xli_atom_t* peer, const qse_char_t* path ); QSE_EXPORT qse_xli_eof_t* qse_xli_inserteof ( - qse_xli_t* xli, - qse_xli_list_t* parent, - qse_xli_atom_t* peer + qse_xli_t* xli, + qse_xli_list_t* parent, + qse_xli_atom_t* peer ); QSE_EXPORT qse_xli_pair_t* qse_xli_findpair ( - qse_xli_t* xli, + qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* fqpn ); @@ -681,10 +681,10 @@ QSE_EXPORT qse_xli_str_t* qse_xli_addsegtostr ( * segment is delimited by double '\0's. The string tags are not included. */ QSE_EXPORT qse_char_t* qse_xli_dupflatstr ( - qse_xli_t* xli, - qse_xli_str_t* str, - qse_size_t* len, - qse_size_t* nsegs + qse_xli_t* xli, + qse_xli_str_t* str, + qse_size_t* len, + qse_size_t* nsegs ); QSE_EXPORT qse_xli_list_t* qse_xli_getroot ( diff --git a/qse/lib/xli/err.c b/qse/lib/xli/err.c index a89e2ad5..35f5e509 100644 --- a/qse/lib/xli/err.c +++ b/qse/lib/xli/err.c @@ -67,7 +67,7 @@ const qse_char_t* qse_xli_dflerrstr ( QSE_T("illegal value for '${0}'"), QSE_T("no value for '${0}'"), QSE_T("uncomplying number of string segments for '${0}'"), - QSE_T("section tag expected in place of '${0}'"), + QSE_T("section tag expected in place of '${0}'") }; return (errnum >= 0 && errnum < QSE_COUNTOF(errstr))? diff --git a/qse/lib/xli/read-ini.c b/qse/lib/xli/read-ini.c index aa02078d..1d55d55b 100644 --- a/qse/lib/xli/read-ini.c +++ b/qse/lib/xli/read-ini.c @@ -81,11 +81,20 @@ enum do { (tok)->type = (code); } while (0) #define MATCH(xli,tok_type) ((xli)->tok.type == (tok_type)) +#define MATCH(xli,tok_type) ((xli)->tok.type == (tok_type)) 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); + if (xli->tok_status & TOK_STATUS_SAME_LINE) + { + while (QSE_ISSPACE(c) && c != QSE_T('\n')) GET_CHAR_TO (xli, c); + } + else + { + while (QSE_ISSPACE(c)) GET_CHAR_TO (xli, c); + } + return 0; } @@ -96,7 +105,6 @@ static int skip_comment (qse_xli_t* xli, qse_xli_tok_t* tok) if (c == QSE_T(';')) { /* skip up to \n */ - /* TODO: support a different line terminator */ qse_str_clear (tok->name); do @@ -149,14 +157,21 @@ static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_SQSTR); - while (1) + do { - GET_CHAR_TO (xli, c); if (c == QSE_CHAR_EOF || c == QSE_T(';')) break; + if (c == QSE_T('\n')) + { + GET_CHAR (xli); + break; + } ADD_TOKEN_CHAR (xli, tok, c); if (!QSE_ISSPACE(c)) xlen = QSE_STR_LEN(tok->name); + + GET_CHAR_TO (xli, c); } + while (1); /* trim away trailing spaces */ qse_str_setlen (tok->name, xlen); @@ -172,7 +187,7 @@ static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) { GET_CHAR_TO (xli, c); - if (c == QSE_CHAR_EOF) + if (c == QSE_CHAR_EOF || c == QSE_T('\n')) { /* the string tag is not closed */ qse_xli_seterror (xli, QSE_XLI_ETAGNC, QSE_NULL, &xli->tok.loc); @@ -238,16 +253,24 @@ static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) { SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_EQ); ADD_TOKEN_CHAR (xli, tok, c); + GET_CHAR (xli); } else { + if ((xli->tok_status & TOK_STATUS_SAME_LINE) && c == QSE_T('\n')) + { + SET_TOKEN_TYPE (xli, tok, QSE_XLI_TOK_NL); + ADD_TOKEN_STR (xli, tok, QSE_T(""), 4); + GET_CHAR (xli); + } /* not handled yet */ - if (c == QSE_T('\0')) + else 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); + return -1; } else { @@ -256,8 +279,8 @@ static int get_token_into (qse_xli_t* xli, qse_xli_tok_t* tok) ea.ptr = &cc; ea.len = 1; qse_xli_seterror (xli, QSE_XLI_ELXCHR, &ea, &tok->loc); + return -1; } - return -1; } return 0; @@ -270,6 +293,9 @@ static int get_token (qse_xli_t* xli) static int read_list (qse_xli_t* xli) { + qse_xli_pair_t* pair; + qse_cstr_t key; + qse_xli_list_t* curlist; while (1) { @@ -277,42 +303,107 @@ static int read_list (qse_xli_t* xli) if (MATCH(xli, QSE_XLI_TOK_TAG)) { - if (get_token(xli) <= -1) return -1; + /* insert a pair with an empty list */ + pair = qse_xli_insertpairwithemptylist (xli, &xli->root->list, QSE_NULL, QSE_STR_PTR(xli->tok.name), QSE_NULL, QSE_NULL); + if (pair == QSE_NULL) return -1; + curlist = (qse_xli_list_t*)pair->val; - if (MATCH(xli, QSE_XLI_TOK_EOF)) break; - if (MATCH(xli, QSE_XLI_TOK_TAG)) continue; - - if (!MATCH(xli, QSE_XLI_TOK_IDENT)) + while (1) { - qse_xli_seterror (xli, QSE_XLI_EKEY, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - return -1; - } + if (get_token(xli) <= -1) return -1; -/* key is the token... */ - xli->tok_status |= TOK_STATUS_SAME_LINE; - if (get_token (xli) <= -1) return -1; + if (MATCH(xli, QSE_XLI_TOK_EOF)) break; + if (MATCH(xli, QSE_XLI_TOK_TAG)) + { + /* switch to a new tag */ + pair = qse_xli_insertpairwithemptylist (xli, &xli->root->list, QSE_NULL, QSE_STR_PTR(xli->tok.name), QSE_NULL, QSE_NULL); + if (pair == QSE_NULL) return -1; + curlist = (qse_xli_list_t*)pair->val; + continue; + } - if (!MATCH(xli, QSE_XLI_TOK_EQ)) - { - qse_xli_seterror (xli, QSE_XLI_EEQ, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - return -1; - } + if (!MATCH(xli, QSE_XLI_TOK_IDENT)) + { + qse_xli_seterror (xli, QSE_XLI_EKEY, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } - xli->tok_status |= TOK_STATUS_UPTO_EOL; - if (get_token (xli) <= -1) return -1; + if (xli->opt.trait & QSE_XLI_KEYNODUP) + { + qse_xli_atom_t* atom; - xli->tok_status &= ~(TOK_STATUS_SAME_LINE | TOK_STATUS_UPTO_EOL); + /* find any key conflicts in the current scope */ + /* TODO: optimization. no sequential search */ + atom = curlist->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; + } - if (MATCH(xli, QSE_XLI_TOK_EOF)) - { - /* empty value */ - break; - } + atom = atom->prev; + } + } - if (!MATCH(xli, QSE_XLI_TOK_SQSTR)) - { - qse_xli_seterror (xli, QSE_XLI_EVAL, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); - return -1; + 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); + return -1; + } + + xli->tok_status |= TOK_STATUS_SAME_LINE; + if (get_token (xli) <= -1) + { + QSE_MMGR_FREE (xli->mmgr, key.ptr); + return -1; + } + if (!MATCH(xli, QSE_XLI_TOK_EQ)) + { + QSE_MMGR_FREE (xli->mmgr, key.ptr); + qse_xli_seterror (xli, QSE_XLI_EEQ, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } + + /* read the value */ + xli->tok_status |= TOK_STATUS_UPTO_EOL; + if (get_token (xli) <= -1) + { + QSE_MMGR_FREE (xli->mmgr, key.ptr); + return -1; + } + xli->tok_status &= ~(TOK_STATUS_SAME_LINE | TOK_STATUS_UPTO_EOL); + + if (MATCH(xli, QSE_XLI_TOK_EOF)) + { + /* empty value */ + qse_cstr_t empty; + + empty.ptr = QSE_T(""); + empty.len = 0; + + pair = qse_xli_insertpairwithstr (xli, curlist, QSE_NULL, key.ptr, QSE_NULL, QSE_NULL, &empty, QSE_NULL); + QSE_MMGR_FREE (xli->mmgr, key.ptr); + if (pair == QSE_NULL) return -1; + break; + } + + if (MATCH(xli, QSE_XLI_TOK_SQSTR)) + { + /* add a new pair with the initial string segment */ + pair = qse_xli_insertpairwithstr (xli, curlist, QSE_NULL, key.ptr, QSE_NULL, QSE_NULL, QSE_STR_XSTR(xli->tok.name), QSE_NULL); + QSE_MMGR_FREE (xli->mmgr, key.ptr); + if (pair == QSE_NULL) return -1; + } + else + { + qse_xli_seterror (xli, QSE_XLI_EVAL, QSE_STR_XSTR(xli->tok.name), &xli->tok.loc); + return -1; + } } } else diff --git a/qse/lib/xli/read.c b/qse/lib/xli/read.c index 3c15dce9..208168f7 100644 --- a/qse/lib/xli/read.c +++ b/qse/lib/xli/read.c @@ -234,7 +234,7 @@ static int skip_comment (qse_xli_t* xli, qse_xli_tok_t* tok) if (c == QSE_T('#')) { /* skip up to \n */ - /* TODO: support a different line terminator */ + qse_str_clear (tok->name); do @@ -635,7 +635,7 @@ retry: { GET_CHAR_TO (xli, c); - if (c == QSE_CHAR_EOF) + if (c == QSE_CHAR_EOF || c == QSE_T('\n')) { /* the string tag is not closed */ qse_xli_seterror (xli, QSE_XLI_ETAGNC, QSE_NULL, &xli->tok.loc); diff --git a/qse/lib/xli/std.c b/qse/lib/xli/std.c index 34379f6e..d1991058 100644 --- a/qse/lib/xli/std.c +++ b/qse/lib/xli/std.c @@ -597,12 +597,6 @@ static qse_ssize_t sf_out ( } } - - - - - - int qse_xli_readstd (qse_xli_t* xli, qse_xli_iostd_t* in) { xtn_t* xtn = (xtn_t*) QSE_XTN (xli); @@ -620,6 +614,25 @@ int qse_xli_readstd (qse_xli_t* xli, qse_xli_iostd_t* in) return qse_xli_read (xli, sf_in); } + +int qse_xli_readinistd (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_readini (xli, sf_in); +} + + int qse_xli_writestd (qse_xli_t* xli, qse_xli_iostd_t* out) { int n; diff --git a/qse/lib/xli/xli.h b/qse/lib/xli/xli.h index 1f9a7ad3..106e0f37 100644 --- a/qse/lib/xli/xli.h +++ b/qse/lib/xli/xli.h @@ -46,7 +46,8 @@ enum qse_xli_tok_type_t QSE_XLI_TOK_NSTR, QSE_XLI_TOK_IDENT, QSE_XLI_TOK_TEXT, - QSE_XLI_TOK_TAG + QSE_XLI_TOK_TAG, + QSE_XLI_TOK_NL }; typedef enum qse_xli_tok_type_t qse_xli_tok_type_t;