diff options
author | LAN-TW <lantw44@gmail.com> | 2014-01-05 18:31:16 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2014-01-05 18:32:10 +0800 |
commit | 4a877792392412507a346e3939ef94d411309337 (patch) | |
tree | 4199166671171e2db47c66496034c3e70d259d94 | |
parent | 45084ef2f48737139c4a2f636d8c9a9ded14c554 (diff) | |
download | l4basic-4a877792392412507a346e3939ef94d411309337.tar.gz l4basic-4a877792392412507a346e3939ef94d411309337.tar.zst l4basic-4a877792392412507a346e3939ef94d411309337.zip |
Port argument parsers (LbsArg) to new LbsStrv and LbsArray APIl4basic-unstable-1.92
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | l4arg.c | 299 | ||||
-rw-r--r-- | l4arg.h | 80 | ||||
-rw-r--r-- | test-arg.c | 89 |
5 files changed, 331 insertions, 141 deletions
@@ -47,7 +47,7 @@ test_array_o_DEPENDS= l4array.o test_array2_o_DEPENDS= l4array2.o test_file_o_DEPENDS= l4file.o l4array.o test_strv_o_DEPENDS= l4strv.o l4array.o -test_arg_o_DEPENDS= l4arg.o l4array.o +test_arg_o_DEPENDS= l4arg.o l4array.o l4strv.o test_list_o_DEPENDS= l4list.o .POSIX: @@ -1 +1 @@ -1.91.2 +1.92.0 @@ -1,163 +1,216 @@ #include "l4arg.h" #include "l4array.h" -#include <string.h> #include <stdlib.h> +#include <string.h> -/* 基本上優先順序是 escape -> quoting -> delimiter */ - -#define abort_l4arg_toargv \ - do{ \ - l4da_free(parr); \ - l4da_free(tmpstr); \ - return NULL; \ - }while(0) - -char** l4arg_toargv(const char* str, - const char* delim, const char* quoting, const char* esc){ - int i; - char escaped = 0, quoted = 0, delimed = 0; - L4DA* parr; - L4DA* tmpstr; - char* addstr, tmpchar; - char** rval; - parr = l4da_create(sizeof(char*), 0); - if(parr == NULL){ +LbsStrv* lbs_arg_parse (const char* str, const char* delim, + const char* esc, const LbsArgQuote* q, LbsArray** detail_ptr) { + + LbsStrv* strv = lbs_strv_new (); + if (strv == NULL) { return NULL; } - tmpstr = l4da_create(sizeof(char), 0); - if(tmpstr == NULL){ - l4da_free(parr); - return NULL; + /* goto label => free_strv */ + + // string length cache + int qlen; // user-supplied quoting string table length + for (qlen = 0; q[qlen].left != NULL && q[qlen].right != NULL; qlen++); + + // qlen will not be too long, so we can use VLA + int qllen[qlen <= 0 ? 1 : qlen]; // left quoting string length + int qrlen[qlen <= 0 ? 1 : qlen]; // right quoting string length + for (int i = 0; i < qlen; i++) { + // empty strings are not allowed + qllen[i] = strlen (q[i].left); + if (qllen[i] <= 0) { + goto free_strv; + } + qrlen[i] = strlen (q[i].right); + if (qrlen[i] <= 0) { + goto free_strv; + } } - for(i=0; str[i]!='\0'; i++){ - if(escaped){ - if(l4da_pushback(tmpstr, &str[i]) < 0){ - abort_l4arg_toargv; + + LbsArray* detail; + if (detail_ptr != NULL) { + detail = lbs_array_new (sizeof (int)); + if (detail == NULL) { + goto free_strv; + } + if (lbs_array_push_back (detail, &(int){-1}) < 0) { + goto free_detail; + } + } else { + detail = NULL; + } + /* goto label => free_detail */ + + bool is_delimed = true; + bool is_escaped = false; + bool is_quoted = false; + bool ignore_esc = false; + int stri = 0; // strv index + int qi; // quoting string index currently used + + const char* p = str; + for (; *p != '\0'; p++) { +loop_start: + if (is_escaped) { + if (lbs_strv_append_char (strv, stri, *p) < 0) { + goto free_detail; } - escaped = 0; - delimed = 0; + is_escaped = false; continue; } - if(quoted){ - if(strchr(quoting, str[i]) != NULL){ - quoted = 0; - continue; - } - if(l4da_pushback(tmpstr, &str[i]) < 0){ - abort_l4arg_toargv; + + if (is_quoted) { + if (strncmp (p, q[qi].right, qrlen[qi]) == 0) { + is_quoted = false; + is_escaped = false; + ignore_esc = false; + p += qrlen[qi] - 1; + } else { + if (!ignore_esc && strchr (esc, *p)) { + is_escaped = true; + } else { + if (lbs_strv_append_char (strv, stri, *p) < 0) { + goto free_detail; + } + } } - delimed = 0; continue; } - if(strchr(esc, str[i]) != NULL){ - escaped = 1; + + if (strchr (delim, *p)) { + if (is_delimed) { + continue; + } + if (lbs_strv_append_str_empty (strv) < 0) { + goto free_detail; + } + if (detail != NULL && lbs_array_push_back (detail, &(int){-1}) < 0) { + goto free_detail; + } + stri++; + is_delimed = true; continue; } - if(strchr(quoting, str[i]) != NULL){ - quoted = 1; + if (strchr (esc, *p)) { + is_escaped = true; continue; } - if(strchr(delim, str[i]) != NULL){ - if(l4da_getlen(tmpstr) > 0){ - tmpchar = '\0'; - if(l4da_pushback(tmpstr, &tmpchar) < 0){ - abort_l4arg_toargv; - } - addstr = (char*)l4da_drop_struct(tmpstr); - if(l4da_pushback(parr, &addstr) < 0){ - l4da_free(parr); - return NULL; - } - tmpstr = l4da_create(sizeof(char), 0); - if(tmpstr == NULL){ - l4da_free(parr); - return NULL; + + is_delimed = false; + + for (int i = 0; i < qlen; i++) { + if (strncmp (p, q[i].left, qllen[i]) == 0) { + is_quoted = true; + ignore_esc = q[i].super; + qi = i; + p += qllen[qi]; // p++ will be skipped, so do not minus 1 here + if (detail != NULL) { + lbs_array_v (detail, int, stri) = qi; } + goto loop_start; // restart the loop } - delimed = 1; - continue; } - if(l4da_pushback(tmpstr, &str[i]) < 0){ - abort_l4arg_toargv; + + if (lbs_strv_append_char (strv, stri, *p) < 0) { + goto free_detail; } - delimed = 0; } - if(!delimed){ - tmpchar = '\0'; - if(l4da_pushback(tmpstr, &tmpchar) < 0){ - abort_l4arg_toargv; - } - addstr = (char*)l4da_drop_struct(tmpstr); - if(l4da_pushback(parr, &addstr) < 0){ - l4da_free(parr); - return NULL; - } + + if (is_delimed && lbs_strv_get_str_len (strv, stri) == 0) { + lbs_strv_remove_str (strv); + lbs_array_pop_back (detail); } - addstr = NULL; - if(l4da_pushback(parr, &addstr) < 0){ - l4da_free(parr); - return NULL; + + if (detail_ptr != NULL) { + *detail_ptr = detail; } - rval = (char**)l4da_drop_struct(parr); - return rval; -} -void l4arg_toargv_free(char** pargv){ - int i; - for(i=0; pargv[i]!=NULL; i++){ - free(pargv[i]); + return strv; + + + /* Error-handling goto label */ + +free_detail: + if (detail != NULL) { + lbs_array_unref (detail); } - free(pargv); + +free_strv: + lbs_strv_unref (strv); + return NULL; } -/* 為什麼叫做 qarg 呢?因為這是用來解析很像 QEMU 命令列參數的參數 */ -L4QARG* l4qarg_parse(const char* str){ - char** pargv = l4arg_toargv(str, ",", "\"\'", "\\"); - if(pargv == NULL){ +LbsArgQopt* lbs_arg_qopt_new (const char* str) { + LbsStrv* strv; + LbsArray* detail; + + strv = lbs_arg_parse (str, ",", "\\", (LbsArgQuote[]) { + { "\"", "\"", false }, { "\'", "\'", true }}, &detail); + + if (strv == NULL || detail == NULL) { return NULL; } - int i, allc; - L4QARG* qargarr; - char* pos; - for(i=0; pargv[i]!=NULL; i++); - allc = i + 1; - qargarr = (L4QARG*) malloc(sizeof(L4QARG) * allc); - if(qargarr == NULL){ - l4arg_toargv_free(pargv); - return NULL; + /* goto label => free_detail_and_strv */ + + size_t strv_len = lbs_strv_get_len (strv); + LbsArgQopt* qopt = malloc (sizeof (LbsArgQopt) + + sizeof (LbsArgQoptItem) * (strv_len + 1)); + if (qopt == NULL) { + goto free_detail_and_strv; } - for(i=0; pargv[i]!=NULL; i++){ - pos = strchr(pargv[i], '='); - if(pos == NULL){ - qargarr[i].arg_name = pargv[i]; - qargarr[i].arg_value = NULL; - }else{ + + qopt->len = strv_len; + qopt->strv = NULL; + qopt->detail = NULL; + qopt->opts[strv_len] = (LbsArgQoptItem) { NULL, NULL }; + + for (size_t i = 0; i < strv_len; i++) { + qopt->opts[i].name = lbs_strv_dup_str (strv, i); + if (qopt->opts[i].name == NULL) { + goto free_qopt; + } + + char* pos = strchr (qopt->opts[i].name, '='); + if (pos == NULL) { + qopt->opts[i].value = NULL; + } else { *pos = '\0'; - qargarr[i].arg_name = pargv[i]; - pos++; - qargarr[i].arg_value = (char*) malloc(strlen(pos)+1); - if(qargarr[i].arg_value == NULL){ - l4arg_toargv_free(pargv); - return NULL; - } - strcpy(qargarr[i].arg_value, pos); + qopt->opts[i].value = pos + 1; } + } - free(pargv); - qargarr[i].arg_name = NULL; - qargarr[i].arg_value = NULL; - return qargarr; + + qopt->strv = strv; + qopt->detail = detail; + return qopt; + +free_qopt: + lbs_arg_qopt_free (qopt); + +free_detail_and_strv: + lbs_strv_unref (strv); + lbs_array_unref (detail); + return NULL; } -void l4qarg_free(L4QARG* qarg){ - int i; - for(i=0; !(qarg[i].arg_name == NULL && qarg[i].arg_value == NULL); i++){ - free(qarg[i].arg_name); - if(qarg[i].arg_value != NULL){ - free(qarg[i].arg_value); - } +void lbs_arg_qopt_free_generic (void* qopt_generic) { + if (qopt_generic == NULL) { + return; } - free(qarg); + + LbsArgQopt* qopt = qopt_generic; + lbs_strv_unref (qopt->strv); + lbs_array_unref (qopt->detail); + + for (int i = 0; qopt->opts[i].name != NULL; i++) { + free (qopt->opts[i].name); + } + + free (qopt); } @@ -1,28 +1,76 @@ -#ifndef L4LIB_ARG_PARSER -#define L4LIB_ARG_PARSER +/* vim: set sw=4 ts=4 sts=4 et: */ +#ifndef LBS_ARG_H +#define LBS_ARG_H + +#include <l4common.h> +#include <l4array.h> +#include <l4strv.h> #ifdef __cplusplus extern "C" { #endif -char** l4arg_toargv(const char*, const char*, const char*, const char*); -void l4arg_toargv_free(char**); +typedef struct LbsArgQuoteStruct { + char* left; + char* right; + bool super; +} LbsArgQuote; + +LbsStrv* lbs_arg_parse (const char* str, + const char* delim, + const char* escape, + const LbsArgQuote* quote, + LbsArray** detail); + +typedef struct LbsArgQoptItemStruct { + char* name; + char* value; +} LbsArgQoptItem; + +typedef struct LbsArgQoptStruct { + /*< private >*/ + LbsStrv* strv; + LbsArray* detail; -typedef struct l4lib_qarg { - char* arg_name; - char* arg_value; -} L4QARG; + /*< public >*/ + size_t len; + LbsArgQoptItem opts[]; +} LbsArgQopt; -L4QARG* l4qarg_parse(const char*); -void l4qarg_free(L4QARG*); -#define l4qarg_n(qargitem) ((qargitem).arg_name) -#define l4qarg_v(qargitem) ((qargitem).arg_value) -#define l4qarg_hasvalue(qargitem) (((qargitem).arg_value != NULL)) -#define l4qarg_end(qargitem) \ - ((((qargitem).arg_name) == NULL) && (((qargitem).arg_value) == NULL)) +LbsArgQopt* lbs_arg_qopt_new (const char* str); +void lbs_arg_qopt_free_generic (void* qopt); +#define lbs_arg_qopt_free(qopt) \ + (lbs_arg_qopt_free_generic (LBS_COMMON_CHECK_TYPE ((qopt), LbsArgQopt*))) + +#define lbs_arg_qopt_item_get_name(item) \ + ((LBS_COMMON_CHECK_TYPE (item, LbsArgQoptItem*))->name) +#define lbs_arg_qopt_item_get_value(item) \ + ((LBS_COMMON_CHECK_TYPE (item, LbsArgQoptItem*))->value) +#define lbs_arg_qopt_item_has_name(item) \ + (((LBS_COMMON_CHECK_TYPE (item, LbsArgQoptItem*))->name) != NULL) +#define lbs_arg_qopt_item_has_value(item) \ + (((LBS_COMMON_CHECK_TYPE (item, LbsArgQoptItem*))->value) != NULL) +#define lbs_arg_qopt_item_is_empty(item) \ + ((!lbs_arg_qopt_item_has_name (item)) && \ + (!lbs_arg_qopt_item_has_value (item))) + +#define lbs_arg_qopt_get_len(qopt) \ + ((LBS_COMMON_CHECK_TYPE (qopt, LbsArgQopt*))->len) +#define lbs_arg_qopt_get_opt(qopt,index) \ + (&((LBS_COMMON_CHECK_TYPE (qopt, LbsArgQopt*))->opts[index])) +#define lbs_arg_qopt_get_name(qopt,index) \ + (lbs_arg_qopt_item_get_name (lbs_arg_qopt_get_opt (qopt, index))) +#define lbs_arg_qopt_get_value(qopt,index) \ + (lbs_arg_qopt_item_get_value (lbs_arg_qopt_get_opt (qopt, index))) +#define lbs_arg_qopt_has_name(qopt,index) \ + (lbs_arg_qopt_item_has_name (lbs_arg_qopt_get_opt (qopt, index))) +#define lbs_arg_qopt_has_value(qopt,index) \ + (lbs_arg_qopt_item_has_value (lbs_arg_qopt_get_opt (qopt, index))) +#define lbs_arg_qopt_is_empty(qopt,index) \ + (lbs_arg_qopt_item_is_empty (lbs_arg_qopt_get_opt (qopt, index))) #ifdef __cplusplus } #endif -#endif +#endif /* LBS_ARG_H */ diff --git a/test-arg.c b/test-arg.c new file mode 100644 index 0000000..d4dd957 --- /dev/null +++ b/test-arg.c @@ -0,0 +1,89 @@ +/* vim: set sw=4 ts=4 sts=4 et: */ +#undef NDEBUG +#define _POSIX_C_SOURCE 200809L +#include <l4arg.h> + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void test_arg_parse (void) { + LbsStrv* strv; + LbsArray* detail; + + strv = lbs_arg_parse ( + ",''/test-arg yes,, \"AB\\^^\\CDE\" \'^^\\\\\' YY\\ ZZ $(F GH\"I\"^)),", + " ,", "^\\", (LbsArgQuote[]) { + { "\"", "\"", false }, + { "\'", "\'", true }, + { "$(", ")", false }, + { NULL, NULL, false } + }, &detail); + + assert (lbs_strv_get_len (strv) == lbs_array_get_len (detail)); + assert (lbs_strv_get_len (strv) == 6); + + char** strv_copy = lbs_strv_copy_strv (strv); + char* strv_ans[] = { + "/test-arg", "yes", "AB^\\CDE", "^^\\\\", "YY ZZ", "F GH\"I\")", NULL}; + for (int i = 0; strv_ans[i] != NULL; i++) { + assert (strcmp (strv_copy[i], strv_ans[i]) == 0); + } + + int detail_ans[] = { 1, -1, 0, 1, -1, 2 }; + for (size_t i = 0; i < lbs_array_get_len (detail); i++) { + assert (lbs_array_v (detail, int, i) == detail_ans[i]); + } + + lbs_strv_generic_free (strv_copy); + lbs_strv_unref (strv); + lbs_array_unref (detail); + + printf ("%s => PASS!\n", __func__); +} + +void test_qarg_parse (void) { + LbsArgQopt* qopt = lbs_arg_qopt_new ( + "if=virt,file=/Yes/Good\\ Night/Great,\"name=\\Y\",good"); + + assert (lbs_arg_qopt_get_len (qopt) == 4); + assert (lbs_arg_qopt_get_len (qopt) == lbs_strv_get_len (qopt->strv)); + assert (lbs_arg_qopt_get_len (qopt) == lbs_array_get_len (qopt->detail)); + + assert (strcmp (lbs_arg_qopt_get_name (qopt, 0), "if") == 0); + assert (strcmp (lbs_arg_qopt_get_value (qopt, 0), "virt") == 0); + assert (lbs_arg_qopt_has_name (qopt, 0)); + assert (lbs_arg_qopt_has_value (qopt, 0)); + assert (!lbs_arg_qopt_is_empty (qopt, 0)); + + assert (strcmp (lbs_arg_qopt_get_name (qopt, 1), "file") == 0); + assert (strcmp (lbs_arg_qopt_get_value (qopt, 1), "/Yes/Good Night/Great") == 0); + assert (lbs_arg_qopt_has_name (qopt, 1)); + assert (lbs_arg_qopt_has_value (qopt, 1)); + assert (!lbs_arg_qopt_is_empty (qopt, 1)); + + assert (strcmp (lbs_arg_qopt_get_name (qopt, 2), "name") == 0); + assert (strcmp (lbs_arg_qopt_get_value (qopt, 2), "Y") == 0); + assert (lbs_arg_qopt_has_name (qopt, 2)); + assert (lbs_arg_qopt_has_value (qopt, 2)); + assert (!lbs_arg_qopt_is_empty (qopt, 2)); + + assert (strcmp (lbs_arg_qopt_get_name (qopt, 3), "good") == 0); + assert (lbs_arg_qopt_get_value (qopt, 3) == NULL); + assert (lbs_arg_qopt_has_name (qopt, 3)); + assert (!lbs_arg_qopt_has_value (qopt, 3)); + assert (!lbs_arg_qopt_is_empty (qopt, 3)); + + assert (lbs_arg_qopt_is_empty (qopt, 4)); + + lbs_arg_qopt_free (qopt); + + printf ("%s => PASS!\n", __func__); +} + +int main () { + test_arg_parse (); + test_qarg_parse (); + return 0; +} |