aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2014-01-05 18:31:16 +0800
committerLAN-TW <lantw44@gmail.com>2014-01-05 18:32:10 +0800
commit4a877792392412507a346e3939ef94d411309337 (patch)
tree4199166671171e2db47c66496034c3e70d259d94
parent45084ef2f48737139c4a2f636d8c9a9ded14c554 (diff)
downloadl4basic-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--Makefile2
-rw-r--r--VERSION2
-rw-r--r--l4arg.c299
-rw-r--r--l4arg.h80
-rw-r--r--test-arg.c89
5 files changed, 331 insertions, 141 deletions
diff --git a/Makefile b/Makefile
index 92a1ae3..07ea0c2 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
diff --git a/VERSION b/VERSION
index bf959fd..7f229af 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.91.2
+1.92.0
diff --git a/l4arg.c b/l4arg.c
index 9908665..4281f0c 100644
--- a/l4arg.c
+++ b/l4arg.c
@@ -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);
}
diff --git a/l4arg.h b/l4arg.h
index 68352c8..26d4a8f 100644
--- a/l4arg.h
+++ b/l4arg.h
@@ -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;
+}