CFLAGS = -g -O0 -Wall -Werror -W -fPIC
CFLAGS += -I.
-srcs := ecoli_tk.c ecoli_tk_str.c ecoli_tk_seq.c
+srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c
+srcs += ecoli_tk_str.c ecoli_tk_seq.c
srcs += ecoli_tk_space.c ecoli_tk_or.c ecoli_test.c
srcs += ecoli_tk_empty.c ecoli_tk_int.c
-srcs += ecoli_malloc.c ecoli_log.c ecoli_tk_option.c
+srcs += ecoli_tk_option.c ecoli_tk_many.c
srcs += ecoli_tk_shlex.c
shlib-y-$(O)libecoli.so := $(srcs)
+ldflags-$(O)test = -rdynamic
exe-y-$(O)test = $(srcs) main.c
ldflags-$(O)readline = -lreadline
if (ptr == NULL)
return NULL;
- memset(ptr, 0, size);
+ memset(ptr, 0, total);
return ptr;
}
#define ECOLI_MALLOC_
#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
typedef void *(*ec_malloc_t)(size_t size, const char *file, unsigned int line);
typedef void (*ec_free_t)(void *ptr, const char *file, unsigned int line);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS AND CONTRIBUTORS 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 <sys/types.h>
+#include <stdlib.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
+
+struct ec_strvec *ec_strvec_new(void)
+{
+ struct ec_strvec *strvec;
+
+ strvec = ec_calloc(1, sizeof(*strvec));
+ if (strvec == NULL)
+ return NULL;
+
+ return strvec;
+}
+
+int ec_strvec_add(struct ec_strvec *strvec, const char *s)
+{
+ char **new_vec;
+
+ new_vec = ec_realloc(strvec->vec,
+ sizeof(*strvec->vec) * (strvec->len + 1));
+ if (new_vec == NULL)
+ return -1;
+
+ strvec->vec = new_vec;
+ strvec->vec[strvec->len] = ec_strdup(s);
+ if (strvec->vec[strvec->len] == NULL)
+ return -1;
+
+ strvec->len++;
+ return 0;
+}
+
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len)
+{
+ struct ec_strvec *copy = NULL;
+ size_t i, veclen;
+
+ copy = ec_strvec_new();
+ if (copy == NULL)
+ goto fail;
+
+ if (len == 0)
+ return copy;
+
+ veclen = ec_strvec_len(strvec);
+ if (len > veclen)
+ len = veclen;
+ copy->vec = ec_calloc(len, sizeof(*copy->vec));
+ if (copy->vec == NULL)
+ goto fail;
+
+ for (i = 0; i < len; i++) {
+ copy->vec[i] = ec_strdup(strvec->vec[i]);
+ if (copy->vec[i] == NULL)
+ goto fail;
+ copy->len++;
+ }
+
+ return copy;
+
+fail:
+ ec_strvec_free(copy);
+ return NULL;
+}
+
+struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec)
+{
+ return ec_strvec_ndup(strvec, ec_strvec_len(strvec));
+}
+
+void ec_strvec_free(struct ec_strvec *strvec)
+{
+ size_t i;
+
+ if (strvec == NULL)
+ return;
+
+ for (i = 0; i < ec_strvec_len(strvec); i++)
+ ec_free(ec_strvec_val(strvec, i));
+
+ ec_free(strvec->vec);
+ ec_free(strvec);
+}
+
+size_t ec_strvec_len(const struct ec_strvec *strvec)
+{
+ return strvec->len;
+}
+
+char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
+{
+ if (strvec == NULL || idx >= strvec->len)
+ return NULL;
+
+ return strvec->vec[idx];
+}
+
+int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
+ size_t off)
+{
+ if (off > from->len)
+ return -1;
+
+ strvec->len = from->len - off;
+ strvec->vec = &from->vec[off];
+
+ return 0;
+}
+
+void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out)
+{
+ size_t i;
+
+ if (strvec == NULL) {
+ fprintf(out, "empty strvec\n");
+ return;
+ }
+
+ fprintf(out, "strvec:\n");
+ for (i = 0; i < ec_strvec_len(strvec); i++)
+ fprintf(out, " %zd: %s\n", i, strvec->vec[i]);
+}
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS AND CONTRIBUTORS 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.
+ */
+
+#ifndef ECOLI_STRVEC_
+#define ECOLI_STRVEC_
+
+#include <stdio.h>
+
+struct ec_strvec {
+ size_t len;
+ char **vec;
+};
+
+struct ec_strvec *ec_strvec_new(void);
+int ec_strvec_add(struct ec_strvec *strvec, const char *s);
+struct ec_strvec *ec_strvec_dup(const struct ec_strvec *strvec);
+struct ec_strvec *ec_strvec_ndup(const struct ec_strvec *strvec, size_t len);
+void ec_strvec_free(struct ec_strvec *strvec);
+size_t ec_strvec_len(const struct ec_strvec *strvec);
+char *ec_strvec_val(const struct ec_strvec *strvec, size_t idx);
+int ec_strvec_slice(struct ec_strvec *strvec, const struct ec_strvec *from,
+ size_t off);
+void ec_strvec_dump(const struct ec_strvec *strvec, FILE *out);
+
+#endif
#include <ecoli_log.h>
#include <ecoli_malloc.h>
#include <ecoli_test.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
TAILQ_INSERT_TAIL(&test_list, test, next);
}
-int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
- const char *expected)
+int ec_test_check_tk_parse(const struct ec_tk *tk, int expected, ...)
{
struct ec_parsed_tk *p;
+ struct ec_strvec *vec = NULL;
const char *s;
- int ret = -1;
-
- p = ec_tk_parse(tk, input);
- s = ec_parsed_tk_to_string(p);
- if (s == NULL && expected == NULL)
- ret = 0;
- else if (s != NULL && expected != NULL &&
- !strcmp(s, expected))
- ret = 0;
-
- if (expected == NULL && ret != 0)
- ec_log(EC_LOG_ERR, "tk should not match but matches <%s>\n", s);
- if (expected != NULL && ret != 0)
- ec_log(EC_LOG_ERR, "tk should match <%s> but matches <%s>\n",
- expected, s);
+ int ret = -1, match;
+ va_list ap;
- ec_parsed_tk_free(p);
+ va_start(ap, expected);
- return ret;
-}
+ /* build a string vector */
+ vec = ec_strvec_new();
+ if (vec == NULL)
+ goto out;
-int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
- const char *expected)
-{
- struct ec_completed_tk *c;
- const char *s;
- int ret = -1;
+ for (s = va_arg(ap, const char *);
+ s != EC_TK_ENDLIST;
+ s = va_arg(ap, const char *)) {
+ if (s == NULL)
+ goto out;
- assert(expected != NULL);
+ if (ec_strvec_add(vec, s) < 0)
+ goto out;
+ }
- c = ec_tk_complete(tk, input);
- s = ec_completed_tk_smallest_start(c);
- if (!strcmp(s, expected))
+ p = ec_tk_parse_tokens(tk, vec);
+ /* XXX only for debug */
+ ec_parsed_tk_dump(stdout, p);
+ if (p == NULL) {
+ ec_log(EC_LOG_ERR, "parsed_tk is NULL\n");
+ }
+ if (ec_parsed_tk_matches(p))
+ match = ec_parsed_tk_len(p);
+ else
+ match = -1;
+ if (expected == match) {
ret = 0;
-
- if (ret != 0)
+ } else {
ec_log(EC_LOG_ERR,
- "should complete with <%s> but completes with <%s>\n",
- expected, s);
+ "tk parsed len (%d) does not match expected (%d)\n",
+ match, expected);
+ }
- ec_completed_tk_free(c);
+ ec_parsed_tk_free(p);
+out:
+ ec_strvec_free(vec);
+ va_end(ap);
return ret;
}
-int ec_test_check_tk_complete_list(const struct ec_tk *tk,
- const char *input, ...)
+int ec_test_check_tk_complete(const struct ec_tk *tk, ...)
{
struct ec_completed_tk *c = NULL;
struct ec_completed_tk_elt *elt;
- const char *s;
- int ret = -1;
+ struct ec_strvec *vec = NULL;
+ const char *s, *expected;
+ int ret = 0;
unsigned int count = 0;
va_list ap;
- va_start(ap, input);
+ va_start(ap, tk);
- c = ec_tk_complete(tk, input);
- if (c == NULL)
+ /* build a string vector */
+ vec = ec_strvec_new();
+ if (vec == NULL)
goto out;
for (s = va_arg(ap, const char *);
if (s == NULL)
goto out;
+ if (ec_strvec_add(vec, s) < 0)
+ goto out;
+ }
+
+ c = ec_tk_complete_tokens(tk, vec);
+ if (c == NULL) {
+ ret = -1;
+ goto out;
+ }
+
+ for (s = va_arg(ap, const char *);
+ s != EC_TK_ENDLIST;
+ s = va_arg(ap, const char *)) {
+ if (s == NULL) {
+ ret = -1;
+ goto out;
+ }
+
count++;
TAILQ_FOREACH(elt, &c->elts, next) {
- if (strcmp(elt->add, s) == 0)
+ /* only check matching completions */
+ if (elt->add != NULL && strcmp(elt->add, s) == 0)
break;
}
if (elt == NULL) {
ec_log(EC_LOG_ERR,
"completion <%s> not in list\n", s);
- goto out;
+ ret = -1;
}
}
- if (count != ec_completed_tk_count(c)) {
+ if (count != ec_completed_tk_count_match(c)) {
ec_log(EC_LOG_ERR,
"nb_completion (%d) does not match (%d)\n",
- count, ec_completed_tk_count(c));
+ count, ec_completed_tk_count_match(c));
ec_completed_tk_dump(stdout, c);
- goto out;
+ ret = -1;
}
- ret = 0;
+
+ expected = va_arg(ap, const char *);
+ s = ec_completed_tk_smallest_start(c);
+ if (strcmp(s, expected)) {
+ ret = -1;
+ ec_log(EC_LOG_ERR,
+ "should complete with <%s> but completes with <%s>\n",
+ expected, s);
+ }
out:
+ ec_strvec_free(vec);
ec_completed_tk_free(c);
va_end(ap);
return ret;
int ret = 0;
TAILQ_FOREACH(test, &test_list, next) {
+ ec_log(EC_LOG_INFO, "== starting test %-20s\n", test->name);
+
if (test->test() == 0) {
ec_log(EC_LOG_INFO, "== test %-20s success\n",
test->name);
int ec_test_all(void);
-int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
- const char *expected);
+/* expected == -1 means no match */
+int ec_test_check_tk_parse(const struct ec_tk *tk, int expected, ...);
#define TEST_ERR() \
ec_log(EC_LOG_ERR, "%s:%d: error: test failed\n", \
__FILE__, __LINE__); \
-#define EC_TEST_CHECK_TK_PARSE(tk, input, expected) ({ \
- int ret = ec_test_check_tk_parse(tk, input, expected); \
- if (ret) \
+#define EC_TEST_CHECK_TK_PARSE(tk, input, expected...) ({ \
+ int ret_ = ec_test_check_tk_parse(tk, input, expected); \
+ if (ret_) \
TEST_ERR(); \
- ret; \
+ ret_; \
})
-int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
- const char *expected);
+int ec_test_check_tk_complete(const struct ec_tk *tk, ...);
-#define EC_TEST_CHECK_TK_COMPLETE(tk, input, expected) ({ \
- int ret = ec_test_check_tk_complete(tk, input, expected); \
- if (ret) \
+#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({ \
+ int ret_ = ec_test_check_tk_complete(tk, args); \
+ if (ret_) \
TEST_ERR(); \
- ret; \
-})
-
-int ec_test_check_tk_complete_list(const struct ec_tk *tk,
- const char *input, ...);
-
-#define EC_TEST_CHECK_TK_COMPLETE_LIST(tk, input, expected...) ({ \
- int ret = ec_test_check_tk_complete_list(tk, input, expected); \
- if (ret) \
- TEST_ERR(); \
- ret; \
+ ret_; \
})
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <errno.h>
#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
{
+ struct ec_strvec *strvec = NULL;
struct ec_parsed_tk *parsed_tk;
- /* by default, it does not match anything */
- if (tk->ops->parse == NULL)
+ errno = ENOMEM;
+ strvec = ec_strvec_new();
+ if (strvec == NULL)
+ goto fail;
+
+ if (ec_strvec_add(strvec, str) < 0)
+ goto fail;
+
+ parsed_tk = ec_tk_parse_tokens(tk, strvec);
+ if (parsed_tk == NULL)
+ goto fail;
+
+ ec_strvec_free(strvec);
+ return parsed_tk;
+
+ fail:
+ ec_strvec_free(strvec);
+ return NULL;
+}
+
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_parsed_tk *parsed_tk;
+
+ if (tk->ops->parse == NULL) {
+ errno = ENOTSUP;
return NULL;
+ }
- parsed_tk = tk->ops->parse(tk, str);
+ parsed_tk = tk->ops->parse(tk, strvec);
return parsed_tk;
}
-struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk)
+struct ec_parsed_tk *ec_parsed_tk_new(void)
{
- struct ec_parsed_tk *parsed_tk;
+ struct ec_parsed_tk *parsed_tk = NULL;
parsed_tk = ec_calloc(1, sizeof(*parsed_tk));
if (parsed_tk == NULL)
goto fail;
- parsed_tk->tk = tk;
TAILQ_INIT(&parsed_tk->children);
return parsed_tk;
return NULL;
}
-void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
+ const struct ec_tk *tk, struct ec_strvec *strvec)
+{
+ parsed_tk->tk = tk;
+ parsed_tk->strvec = strvec;
+}
+
+void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk)
{
struct ec_parsed_tk *child;
TAILQ_REMOVE(&parsed_tk->children, child, next);
ec_parsed_tk_free(child);
}
- ec_free(parsed_tk->str);
+}
+
+void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk)
+{
+ if (parsed_tk == NULL)
+ return;
+
+ ec_parsed_tk_free_children(parsed_tk);
+ ec_strvec_free(parsed_tk->strvec);
ec_free(parsed_tk);
}
-static void __ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk,
- size_t indent)
+static void __ec_parsed_tk_dump(FILE *out,
+ const struct ec_parsed_tk *parsed_tk, size_t indent)
{
struct ec_parsed_tk *child;
size_t i;
- const char *s;
+ const char *s, *id = "None", *typename = "None";
/* XXX enhance */
for (i = 0; i < indent; i++)
fprintf(out, " ");
+
s = ec_parsed_tk_to_string(parsed_tk);
- fprintf(out, "id=%s, s=<%s>\n", parsed_tk->tk->id, s);
+ if (parsed_tk->tk != NULL) {
+ if (parsed_tk->tk->id != NULL)
+ id = parsed_tk->tk->id;
+ typename = parsed_tk->tk->ops->typename;
+ }
+
+ fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
TAILQ_FOREACH(child, &parsed_tk->children, next)
__ec_parsed_tk_dump(out, child, indent + 2);
void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk)
{
if (parsed_tk == NULL) {
+ fprintf(out, "parsed_tk is NULL, error in parse\n");
+ return;
+ }
+ if (!ec_parsed_tk_matches(parsed_tk)) {
fprintf(out, "no match\n");
return;
}
if (parsed_tk == NULL)
return NULL;
- if (parsed_tk->tk->id != NULL && !strcmp(parsed_tk->tk->id, id))
+ if (parsed_tk->tk != NULL &&
+ parsed_tk->tk->id != NULL &&
+ !strcmp(parsed_tk->tk->id, id))
return parsed_tk;
TAILQ_FOREACH(child, &parsed_tk->children, next) {
return NULL;
}
+/* XXX return NUL if it matches several tokens?
+ or add a parameter to join() the tokens ? */
const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk)
{
- if (parsed_tk == NULL)
+ if (parsed_tk == NULL || parsed_tk->strvec == NULL)
return NULL;
- return parsed_tk->str;
+ return ec_strvec_val(parsed_tk->strvec, 0);
+}
+
+/* number of parsed tokens */
+size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk)
+{
+ if (parsed_tk == NULL || parsed_tk->strvec == NULL)
+ return 0;
+
+ return ec_strvec_len(parsed_tk->strvec);
+}
+
+size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk)
+{
+ if (parsed_tk == NULL)
+ return 0;
+
+ if (parsed_tk->tk == NULL && TAILQ_EMPTY(&parsed_tk->children))
+ return 0;
+
+ return 1;
}
struct ec_completed_tk *ec_completed_tk_new(void)
return NULL;
TAILQ_INIT(&completed_tk->elts);
- completed_tk->count = 0;
+ completed_tk->count_match = 0;
return completed_tk;
}
struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
- const char *add, const char *full)
+ const char *add)
{
struct ec_completed_tk_elt *elt = NULL;
return NULL;
}
}
- if (full != NULL) {
- elt->full = ec_strdup(full);
- if (elt->full == NULL) {
- ec_completed_tk_elt_free(elt);
- return NULL;
- }
- }
return elt;
}
*/
struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
const char *str)
+{
+ struct ec_strvec *strvec = NULL;
+ struct ec_completed_tk *completed_tk;
+
+ errno = ENOMEM;
+ strvec = ec_strvec_new();
+ if (strvec == NULL)
+ goto fail;
+
+ if (ec_strvec_add(strvec, str) < 0)
+ goto fail;
+
+ completed_tk = ec_tk_complete_tokens(tk, strvec);
+ if (completed_tk == NULL)
+ goto fail;
+
+ ec_strvec_free(strvec);
+ return completed_tk;
+
+ fail:
+ ec_strvec_free(strvec);
+ return NULL;
+}
+
+/* default completion function: return a no-match element */
+struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
{
struct ec_completed_tk *completed_tk;
+ struct ec_completed_tk_elt *completed_tk_elt;
+
+ (void)strvec;
+
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ return NULL;
- if (tk->ops->complete == NULL)
- return ec_completed_tk_new();
+ completed_tk_elt = ec_completed_tk_elt_new(gen_tk, NULL);
+ if (completed_tk_elt == NULL) {
+ ec_completed_tk_free(completed_tk);
+ return NULL;
+ }
- completed_tk = tk->ops->complete(tk, str);
+ ec_completed_tk_add_elt(completed_tk, completed_tk_elt);
return completed_tk;
}
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
+ const struct ec_strvec *strvec)
+{
+ return tk->ops->complete(tk, strvec);
+}
+
/* count the number of identical chars at the beginning of 2 strings */
static size_t strcmp_count(const char *s1, const char *s2)
{
TAILQ_INSERT_TAIL(&completed_tk->elts, elt, next);
completed_tk->count++;
+ if (elt->add != NULL)
+ completed_tk->count_match++;
if (elt->add != NULL) {
if (completed_tk->smallest_start == NULL) {
completed_tk->smallest_start = ec_strdup(elt->add);
void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt)
{
ec_free(elt->add);
- ec_free(elt->full);
ec_free(elt);
}
return;
}
- fprintf(out, "completion: count=%u smallest_start=<%s>\n",
- completed_tk->count, completed_tk->smallest_start);
+ fprintf(out, "completion: count=%u match=%u smallest_start=<%s>\n",
+ completed_tk->count, completed_tk->count_match,
+ completed_tk->smallest_start);
TAILQ_FOREACH(elt, &completed_tk->elts, next) {
- fprintf(out, "add=<%s>, full=<%s>, tk=%p\n",
- elt->add, elt->full, elt->tk);
+ fprintf(out, "add=<%s>, tk=%p, tk_type=%s\n",
+ elt->add, elt->tk, elt->tk->ops->typename);
}
}
return completed_tk->smallest_start;
}
-unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk)
+unsigned int ec_completed_tk_count_match(
+ const struct ec_completed_tk *completed_tk)
{
if (completed_tk == NULL)
return 0;
- return completed_tk->count;
+ return completed_tk->count_match;
}
-void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk)
+struct ec_completed_tk_iter *
+ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
+ enum ec_completed_tk_filter_flags flags)
{
- if (completed_tk == NULL)
- return;
+ struct ec_completed_tk_iter *iter;
+
+ iter = ec_calloc(1, sizeof(*iter));
+ if (iter == NULL)
+ return NULL;
- completed_tk->cur = NULL;
+ iter->completed_tk = completed_tk;
+ iter->flags = flags;
+ iter->cur = NULL;
+
+ return iter;
}
const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
- struct ec_completed_tk *completed_tk)
+ struct ec_completed_tk_iter *iter)
{
- if (completed_tk == NULL)
+ if (iter->completed_tk == NULL)
return NULL;
- if (completed_tk->cur == NULL) {
- completed_tk->cur = TAILQ_FIRST(&completed_tk->elts);
- } else {
- completed_tk->cur = TAILQ_NEXT(completed_tk->cur, next);
- }
+ do {
+ if (iter->cur == NULL) {
+ iter->cur = TAILQ_FIRST(&iter->completed_tk->elts);
+ } else {
+ iter->cur = TAILQ_NEXT(iter->cur, next);
+ }
+
+ if (iter->cur == NULL)
+ break;
- return completed_tk->cur;
+ if (iter->cur->add == NULL &&
+ (iter->flags & ITER_NO_MATCH))
+ break;
+
+ if (iter->cur->add != NULL &&
+ (iter->flags & ITER_MATCH))
+ break;
+
+ } while (iter->cur != NULL);
+
+ return iter->cur;
+}
+
+void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter)
+{
+ ec_free(iter);
}
#include <sys/queue.h>
#include <sys/types.h>
-
#include <stdio.h>
#define EC_TK_ENDLIST ((void *)1)
struct ec_tk;
struct ec_parsed_tk;
+struct ec_strvec;
typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk,
- const char *str);
+ const struct ec_strvec *strvec);
typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk,
- const char *str);
+ const struct ec_strvec *strvec);
typedef void (*ec_tk_free_priv_t)(struct ec_tk *);
struct ec_tk_ops {
+ const char *typename;
ec_tk_parse_t parse;
ec_tk_complete_t complete;
ec_tk_free_priv_t free_priv;
TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_tk);
+/*
+ tk == NULL + empty children list means "no match"
+*/
struct ec_parsed_tk {
- struct ec_parsed_tk_list children;
TAILQ_ENTRY(ec_parsed_tk) next;
+ struct ec_parsed_tk_list children;
const struct ec_tk *tk;
- char *str;
+ struct ec_strvec *strvec;
};
+struct ec_parsed_tk *ec_parsed_tk_new(void);
+
+void ec_parsed_tk_set_match(struct ec_parsed_tk *parsed_tk,
+ const struct ec_tk *tk, struct ec_strvec *strvec);
+
/* XXX we could use a cache to store possible completions or match: the
* cache would be per-node, and would be reset for each call to parse()
* or complete() ? */
-
-struct ec_parsed_tk *ec_parsed_tk_new(const struct ec_tk *tk);
+/* a NULL return value is an error, with errno set
+ ENOTSUP: no ->parse() operation
+*/
struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *token, const char *str);
+
+/* mostly internal to tokens */
+/* XXX it should not reset cache
+ * ... not sure... it is used by tests */
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *token,
+ const struct ec_strvec *strvec);
+
void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
struct ec_parsed_tk *child);
+void ec_parsed_tk_free_children(struct ec_parsed_tk *parsed_tk);
void ec_parsed_tk_dump(FILE *out, const struct ec_parsed_tk *parsed_tk);
void ec_parsed_tk_free(struct ec_parsed_tk *parsed_tk);
const char *id);
const char *ec_parsed_tk_to_string(const struct ec_parsed_tk *parsed_tk);
+size_t ec_parsed_tk_len(const struct ec_parsed_tk *parsed_tk);
+size_t ec_parsed_tk_matches(const struct ec_parsed_tk *parsed_tk);
struct ec_completed_tk_elt {
TAILQ_ENTRY(ec_completed_tk_elt) next;
const struct ec_tk *tk;
char *add;
- char *full;
};
TAILQ_HEAD(ec_completed_tk_elt_list, ec_completed_tk_elt);
struct ec_completed_tk {
struct ec_completed_tk_elt_list elts;
- const struct ec_completed_tk_elt *cur;
unsigned count;
+ unsigned count_match;
char *smallest_start;
};
*/
struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
const char *str);
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *token,
+ const struct ec_strvec *strvec);
struct ec_completed_tk *ec_completed_tk_new(void);
struct ec_completed_tk_elt *ec_completed_tk_elt_new(const struct ec_tk *tk,
- const char *add, const char *full);
+ const char *add);
void ec_completed_tk_add_elt(struct ec_completed_tk *completed_tk,
struct ec_completed_tk_elt *elt);
void ec_completed_tk_elt_free(struct ec_completed_tk_elt *elt);
void ec_completed_tk_free(struct ec_completed_tk *completed_tk);
void ec_completed_tk_dump(FILE *out,
const struct ec_completed_tk *completed_tk);
+struct ec_completed_tk *ec_tk_default_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec);
/* cannot return NULL */
const char *ec_completed_tk_smallest_start(
const struct ec_completed_tk *completed_tk);
-unsigned int ec_completed_tk_count(const struct ec_completed_tk *completed_tk);
+unsigned int ec_completed_tk_count_match(
+ const struct ec_completed_tk *completed_tk);
+
+enum ec_completed_tk_filter_flags {
+ ITER_MATCH = 1,
+ ITER_NO_MATCH,
+};
+
+struct ec_completed_tk_iter {
+ enum ec_completed_tk_filter_flags flags;
+ const struct ec_completed_tk *completed_tk;
+ const struct ec_completed_tk_elt *cur;
+};
+
+struct ec_completed_tk_iter *
+ec_completed_tk_iter_new(struct ec_completed_tk *completed_tk,
+ enum ec_completed_tk_filter_flags flags);
-void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk);
const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
- struct ec_completed_tk *completed_tk);
+ struct ec_completed_tk_iter *iter);
+void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
#endif
#include <ecoli_malloc.h>
#include <ecoli_log.h>
#include <ecoli_test.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_empty.h>
+struct ec_tk_empty {
+ struct ec_tk gen;
+};
+
static struct ec_parsed_tk *ec_tk_empty_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_parsed_tk *parsed_tk;
+ struct ec_strvec *match_strvec;
+
+ (void)strvec;
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
- (void)str;
- parsed_tk->str = ec_strdup("");
+ match_strvec = ec_strvec_new();
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
}
static struct ec_tk_ops ec_tk_empty_ops = {
+ .typename = "empty",
.parse = ec_tk_empty_parse,
+ .complete = ec_tk_default_complete,
};
struct ec_tk *ec_tk_empty_new(const char *id)
{
- return ec_tk_new(id, &ec_tk_empty_ops, sizeof(struct ec_tk_empty));
+ return ec_tk_new(id, &ec_tk_empty_ops,
+ sizeof(struct ec_tk_empty));
}
static int ec_tk_empty_testcase(void)
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", "");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "foo", "bar", EC_TK_ENDLIST);
ec_tk_free(tk);
/* never completes */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#ifndef ECOLI_TK_EMPTY_
#define ECOLI_TK_EMPTY_
-#include <ecoli_tk.h>
-
-struct ec_tk_empty {
- struct ec_tk gen;
-};
-
struct ec_tk *ec_tk_empty_new(const char *id);
#endif
#include <ecoli_log.h>
#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_int.h>
#include <ecoli_test.h>
-static size_t parse_llint(struct ec_tk_int *tk, const char *str,
+struct ec_tk_int {
+ struct ec_tk gen;
+ long long int min;
+ long long int max;
+ unsigned int base;
+};
+
+static int parse_llint(struct ec_tk_int *tk, const char *str,
long long *val)
{
char *endptr;
errno = 0;
*val = strtoll(str, &endptr, tk->base);
- /* starts with a space */
- if (isspace(str[0]))
- return 0;
-
/* out of range */
if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
(errno != 0 && *val == 0))
- return 0;
+ return -1;
if (*val < tk->min || *val > tk->max)
- return 0;
+ return -1;
+
+ if (*endptr != 0)
+ return -1;
- return endptr - str;
+ return 0;
}
static struct ec_parsed_tk *ec_tk_int_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
struct ec_parsed_tk *parsed_tk;
+ struct ec_strvec *match_strvec;
+ const char *str;
long long val;
- size_t len;
- len = parse_llint(tk, str, &val);
- if (len == 0)
- return NULL;
-
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
+
+ if (ec_strvec_len(strvec) == 0)
+ return parsed_tk;
- parsed_tk->str = ec_strndup(str, len);
+ str = ec_strvec_val(strvec, 0);
+ if (parse_llint(tk, str, &val) < 0)
+ return parsed_tk;
+
+ match_strvec = ec_strvec_ndup(strvec, 1);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
}
static struct ec_tk_ops ec_tk_int_ops = {
+ .typename = "int",
.parse = ec_tk_int_parse,
+ .complete = ec_tk_default_complete,
};
struct ec_tk *ec_tk_int_new(const char *id, long long int min,
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "256", "256");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x100", "0x100");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x101", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " 1", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "256", "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x100", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x101", EC_TK_ENDLIST);
p = ec_tk_parse(tk, "0");
s = ec_parsed_tk_to_string(p);
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "7fffffffffffffff",
- "7fffffffffffffff");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x7fffffffffffffff",
- "0x7fffffffffffffff");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "-2", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "7fffffffffffffff", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0x7fffffffffffffff", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "-2", EC_TK_ENDLIST);
p = ec_tk_parse(tk, "10");
s = ec_parsed_tk_to_string(p);
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "-9223372036854775808",
- "-9223372036854775808");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x0", "0");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "1", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "0", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-1", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "-9223372036854775808",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "0x0", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "1", EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "1", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#include <ecoli_tk.h>
-struct ec_tk_int {
- struct ec_tk gen;
- long long int min;
- long long int max;
- unsigned int base;
-};
-
struct ec_tk *ec_tk_int_new(const char *id, long long int min,
long long int max, unsigned int base);
long long ec_tk_int_getval(struct ec_tk *tk, const char *str);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS AND CONTRIBUTORS 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_strvec.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_many.h>
+
+struct ec_tk_many {
+ struct ec_tk gen;
+ unsigned int min;
+ unsigned int max;
+ struct ec_tk *child;
+};
+
+static struct ec_parsed_tk *ec_tk_many_parse(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+ struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+ struct ec_strvec *match_strvec;
+ struct ec_strvec childvec;
+ size_t off = 0, len, count;
+
+ parsed_tk = ec_parsed_tk_new();
+ if (parsed_tk == NULL)
+ goto fail;
+
+ for (count = 0; tk->max == 0 || count < tk->max; count++) {
+ if (ec_strvec_slice(&childvec, strvec, off) < 0)
+ goto fail;
+
+ child_parsed_tk = ec_tk_parse_tokens(tk->child, &childvec);
+ if (child_parsed_tk == NULL)
+ goto fail;
+
+ if (!ec_parsed_tk_matches(child_parsed_tk)) {
+ ec_parsed_tk_free(child_parsed_tk);
+ break;
+ }
+
+ ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+
+ /* it matches "no token", no need to continue */
+ len = ec_parsed_tk_len(child_parsed_tk);
+ if (len == 0) {
+ ec_parsed_tk_free(child_parsed_tk);
+ break;
+ }
+
+ off += len;
+ }
+
+ if (count < tk->min) {
+ ec_parsed_tk_free_children(parsed_tk);
+ return parsed_tk;
+ }
+
+ match_strvec = ec_strvec_ndup(strvec, off);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
+ return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
+}
+
+#if 0
+static struct ec_completed_tk *ec_tk_many_complete(const struct ec_tk *gen_tk,
+ const struct ec_strvec *strvec)
+{
+ struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+ struct ec_completed_tk *completed_tk, *child_completed_tk;
+ struct ec_strvec childvec;
+ struct ec_parsed_tk *parsed_tk;
+ size_t len = 0;
+ unsigned int i;
+
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ return NULL;
+
+ if (tk->len == 0)
+ return completed_tk;
+
+ for (i = 0; i < tk->len; i++) {
+ if (ec_strvec_slice(&childvec, strvec, len) < 0)
+ return completed_tk; /* XXX fail ? */
+
+ child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+ &childvec);
+ if (child_completed_tk == NULL) {
+ ec_completed_tk_free(completed_tk);
+ return NULL;
+ }
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
+
+ parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
+ if (parsed_tk == NULL)
+ goto fail;
+ if (!ec_parsed_tk_matches(parsed_tk)) {
+ ec_parsed_tk_free(parsed_tk);
+ break;
+ }
+
+ len += ec_strvec_len(parsed_tk->strvec);
+ ec_parsed_tk_free(parsed_tk);
+ }
+
+ return completed_tk;
+
+fail:
+ /* XXX */
+ return NULL;
+}
+#endif
+
+static void ec_tk_many_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_many *tk = (struct ec_tk_many *)gen_tk;
+
+ ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_many_ops = {
+ .typename = "many",
+ .parse = ec_tk_many_parse,
+ .complete = ec_tk_default_complete,
+//XXX .complete = ec_tk_many_complete,
+ .free_priv = ec_tk_many_free_priv,
+};
+
+struct ec_tk *ec_tk_many_new(const char *id, struct ec_tk *child,
+ unsigned int min, unsigned int max)
+{
+ struct ec_tk_many *tk = NULL;
+
+ if (child == NULL)
+ return NULL;
+
+ tk = (struct ec_tk_many *)ec_tk_new(id, &ec_tk_many_ops,
+ sizeof(*tk));
+ if (tk == NULL) {
+ ec_tk_free(child);
+ return NULL;
+ }
+
+ tk->child = child;
+ tk->min = min;
+ tk->max = max;
+
+ return &tk->gen;
+}
+
+static int ec_tk_many_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 0, 0);
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
+ ec_tk_free(tk);
+
+ tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 1, 0);
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, EC_TK_ENDLIST);
+ ec_tk_free(tk);
+
+ tk = ec_tk_many_new(NULL, ec_tk_str_new(NULL, "foo"), 1, 2);
+ if (tk == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create tk\n");
+ return -1;
+ }
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "bar",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "foo", "foo",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, EC_TK_ENDLIST);
+ ec_tk_free(tk);
+
+ /* test completion */
+ /* XXX */
+
+ return ret;
+}
+
+static struct ec_test ec_tk_many_test = {
+ .name = "many",
+ .test = ec_tk_many_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_many_test);
--- /dev/null
+/*
+ * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of the University of California, Berkeley nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS AND CONTRIBUTORS 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.
+ */
+
+#ifndef ECOLI_TK_MANY_
+#define ECOLI_TK_MANY_
+
+/*
+ * if min == max == 0, there is no limit
+ */
+struct ec_tk *ec_tk_many_new(const char *id, struct ec_tk *child,
+ unsigned int min, unsigned int max);
+
+#endif
#include <ecoli_malloc.h>
#include <ecoli_log.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_option.h>
#include <ecoli_tk_str.h>
#include <ecoli_test.h>
+struct ec_tk_option {
+ struct ec_tk gen;
+ struct ec_tk *child;
+};
+
static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
- struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+ struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
+ struct ec_strvec *match_strvec;
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
- child_parsed_tk = ec_tk_parse(tk->child, str);
- if (child_parsed_tk != NULL) {
+ child_parsed_tk = ec_tk_parse_tokens(tk->child, strvec);
+ if (child_parsed_tk == NULL)
+ goto fail;
+
+ if (ec_parsed_tk_matches(child_parsed_tk)) {
ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
- parsed_tk->str = ec_strndup(child_parsed_tk->str,
- strlen(child_parsed_tk->str));
+ match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
} else {
- parsed_tk->str = ec_strdup("");
+ ec_parsed_tk_free(child_parsed_tk);
+ match_strvec = ec_strvec_new();
}
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
+
return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
}
static struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
- return ec_tk_complete(tk->child, str);
+ return ec_tk_complete_tokens(tk->child, strvec);
}
static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
}
static struct ec_tk_ops ec_tk_option_ops = {
+ .typename = "option",
.parse = ec_tk_option_parse,
.complete = ec_tk_option_complete,
.free_priv = ec_tk_option_free_priv,
tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops,
sizeof(*tk));
- if (tk == NULL)
+ if (tk == NULL) {
+ ec_tk_free(child);
return NULL;
+ }
tk->child = child;
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 0, EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
- "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", EC_TK_ENDLIST,
+ "foo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "b", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#include <ecoli_tk.h>
-struct ec_tk_option {
- struct ec_tk gen;
- struct ec_tk *child;
-};
-
struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *tk);
#endif
#include <ecoli_malloc.h>
#include <ecoli_log.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_or.h>
#include <ecoli_tk_str.h>
#include <ecoli_test.h>
+struct ec_tk_or {
+ struct ec_tk gen;
+ struct ec_tk **table;
+ unsigned int len;
+};
+
static struct ec_parsed_tk *ec_tk_or_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
- struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+ struct ec_parsed_tk *parsed_tk, *child_parsed_tk = NULL;
+ struct ec_strvec *match_strvec;
unsigned int i;
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
for (i = 0; i < tk->len; i++) {
- child_parsed_tk = ec_tk_parse(tk->table[i], str);
- if (child_parsed_tk != NULL)
+ child_parsed_tk = ec_tk_parse_tokens(tk->table[i], strvec);
+ if (child_parsed_tk == NULL)
+ goto fail;
+ if (ec_parsed_tk_matches(child_parsed_tk))
break;
+ ec_parsed_tk_free(child_parsed_tk);
+ child_parsed_tk = NULL;
}
- if (child_parsed_tk == NULL)
+ /* no match */
+ if (i == tk->len)
+ return parsed_tk;
+
+ match_strvec = ec_strvec_dup(child_parsed_tk->strvec);
+ if (match_strvec == NULL)
goto fail;
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
- parsed_tk->str = ec_strndup(child_parsed_tk->str,
- strlen(child_parsed_tk->str));
-
return parsed_tk;
fail:
+ ec_parsed_tk_free(child_parsed_tk);
ec_parsed_tk_free(parsed_tk);
return NULL;
}
static struct ec_completed_tk *ec_tk_or_complete(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
struct ec_completed_tk *completed_tk, *child_completed_tk;
return NULL;
for (n = 0; n < tk->len; n++) {
- child_completed_tk = ec_tk_complete(tk->table[n], str);
+ child_completed_tk = ec_tk_complete_tokens(tk->table[n],
+ strvec);
- if (child_completed_tk == NULL)
+ if (child_completed_tk == NULL) // XXX fail instead?
continue;
ec_completed_tk_merge(completed_tk, child_completed_tk);
}
static struct ec_tk_ops ec_tk_or_ops = {
+ .typename = "or",
.parse = ec_tk_or_parse,
.complete = ec_tk_or_complete,
.free_priv = ec_tk_or_free_priv,
struct ec_tk_or *tk = NULL;
struct ec_tk *child;
va_list ap;
+ int fail = 0;
va_start(ap, id);
for (child = va_arg(ap, struct ec_tk *);
child != EC_TK_ENDLIST;
child = va_arg(ap, struct ec_tk *)) {
- if (child == NULL)
- goto fail;
+ /* on error, don't quit the loop to avoid leaks */
- ec_tk_or_add(&tk->gen, child);
+ if (child == NULL || ec_tk_or_add(&tk->gen, child) < 0)
+ fail = 1;
}
+ if (fail == 1)
+ goto fail;
+
va_end(ap);
return &tk->gen;
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "fooxxx", "foo");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "bar");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "oo", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " ", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "toto", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "ar");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "t", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "to", "to");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
- "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "f",
- "oo", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "b",
- "ar", "ar2", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "t",
- "oto", "iti", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", "bar", "bar2", "toto", "titi", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "b", EC_TK_ENDLIST,
+ "ar", "ar2", EC_TK_ENDLIST,
+ "ar");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "bar", EC_TK_ENDLIST,
+ "", "2", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "t", EC_TK_ENDLIST,
+ "oto", "iti", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "to", EC_TK_ENDLIST,
+ "to", EC_TK_ENDLIST,
+ "to");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#ifndef ECOLI_TK_OR_
#define ECOLI_TK_OR_
-#include <sys/queue.h>
-
#include <ecoli_tk.h>
-struct ec_tk_or {
- struct ec_tk gen;
- struct ec_tk **table;
- unsigned int len;
-};
-
struct ec_tk *ec_tk_or_new(const char *id);
/* list must be terminated with EC_TK_ENDLIST */
#include <ecoli_malloc.h>
#include <ecoli_log.h>
#include <ecoli_test.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_str.h>
#include <ecoli_tk_option.h>
#include <ecoli_tk_seq.h>
+struct ec_tk_seq {
+ struct ec_tk gen;
+ struct ec_tk **table;
+ unsigned int len;
+};
+
static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+ struct ec_strvec *match_strvec;
+ struct ec_strvec childvec;
size_t len = 0;
unsigned int i;
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
for (i = 0; i < tk->len; i++) {
- child_parsed_tk = ec_tk_parse(tk->table[i], str + len);
+ if (ec_strvec_slice(&childvec, strvec, len) < 0)
+ goto fail;
+
+ child_parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
if (child_parsed_tk == NULL)
goto fail;
+ if (!ec_parsed_tk_matches(child_parsed_tk)) {
+ ec_parsed_tk_free(child_parsed_tk);
+ ec_parsed_tk_free_children(parsed_tk);
+ return parsed_tk;
+ }
- len += strlen(child_parsed_tk->str);
ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+ len += ec_parsed_tk_len(child_parsed_tk);
}
- parsed_tk->str = ec_strndup(str, len);
+ match_strvec = ec_strvec_ndup(strvec, len);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
}
static struct ec_completed_tk *ec_tk_seq_complete(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
struct ec_completed_tk *completed_tk, *child_completed_tk;
+ struct ec_strvec childvec;
struct ec_parsed_tk *parsed_tk;
size_t len = 0;
unsigned int i;
if (tk->len == 0)
return completed_tk;
- for (i = 0; i < tk->len; i++) {
- child_completed_tk = ec_tk_complete(tk->table[i], str + len);
+ for (i = 0; i < tk->len && len < ec_strvec_len(strvec); i++) {
+ if (ec_strvec_slice(&childvec, strvec, len) < 0)
+ goto fail;
+
+ child_completed_tk = ec_tk_complete_tokens(tk->table[i],
+ &childvec);
if (child_completed_tk == NULL) {
ec_completed_tk_free(completed_tk);
return NULL;
}
ec_completed_tk_merge(completed_tk, child_completed_tk);
- parsed_tk = ec_tk_parse(tk->table[i], str + len);
+ parsed_tk = ec_tk_parse_tokens(tk->table[i], &childvec);
if (parsed_tk == NULL)
+ goto fail;
+ if (!ec_parsed_tk_matches(parsed_tk)) {
+ ec_parsed_tk_free(parsed_tk);
break;
+ }
- len += strlen(parsed_tk->str);
+ len += ec_strvec_len(parsed_tk->strvec);
ec_parsed_tk_free(parsed_tk);
}
return completed_tk;
+
+fail:
+ /* XXX */
+ return NULL;
}
static void ec_tk_seq_free_priv(struct ec_tk *gen_tk)
}
static struct ec_tk_ops ec_tk_seq_ops = {
+ .typename = "seq",
.parse = ec_tk_seq_parse,
.complete = ec_tk_seq_complete,
.free_priv = ec_tk_seq_free_priv,
struct ec_tk_seq *tk = NULL;
struct ec_tk *child;
va_list ap;
+ int fail = 0;
va_start(ap, id);
for (child = va_arg(ap, struct ec_tk *);
child != EC_TK_ENDLIST;
child = va_arg(ap, struct ec_tk *)) {
- if (child == NULL)
- goto fail;
+ /* on error, don't quit the loop to avoid leaks */
- ec_tk_seq_add(&tk->gen, child);
+ if (child == NULL || ec_tk_seq_add(&tk->gen, child) < 0)
+ fail = 1;
}
+ if (fail == 1)
+ goto fail;
+
va_end(ap);
return &tk->gen;
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobar", "foobar");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobarxxx", "foobar");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " foobar", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "foo", "bar", "toto",
+ EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foox", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", "barx", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "bar", "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", "foo", EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "foo",
- "bar", "toto", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foot", "oto");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foob", "ar");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobar", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", EC_TK_ENDLIST,
+ "foo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", EC_TK_ENDLIST,
+ "", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", "", EC_TK_ENDLIST,
+ "bar", "toto", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", "t", EC_TK_ENDLIST,
+ "oto", EC_TK_ENDLIST,
+ "oto");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", "b", EC_TK_ENDLIST,
+ "ar", EC_TK_ENDLIST,
+ "ar");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", "bar", EC_TK_ENDLIST,
+ "", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foobarx", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#ifndef ECOLI_TK_SEQ_
#define ECOLI_TK_SEQ_
-#include <sys/queue.h>
-
#include <ecoli_tk.h>
-struct ec_tk_seq {
- struct ec_tk gen;
- struct ec_tk **table;
- unsigned int len;
-};
-
struct ec_tk *ec_tk_seq_new(const char *id);
/* list must be terminated with EC_TK_ENDLIST */
#include <ecoli_malloc.h>
#include <ecoli_log.h>
#include <ecoli_test.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
+#include <ecoli_tk_seq.h>
#include <ecoli_tk_str.h>
#include <ecoli_tk_option.h>
#include <ecoli_tk_shlex.h>
-static int isend(char c)
+struct ec_tk_shlex {
+ struct ec_tk gen;
+ struct ec_tk *child;
+};
+
+static size_t eat_spaces(const char *str)
{
- if (c == '\0' || c == '#' || c == '\n' || c == '\r')
- return 1;
- return 0;
+ size_t i = 0;
+
+ /* skip spaces */
+ while (isblank(str[i]))
+ i++;
+
+ return i;
}
-/* Remove quotes and stop when we reach the end of token. Return the
- * number of "eaten" bytes from the source buffer, or a negative value
- * on error */
-/* XXX support simple quotes, try to be posix-compatible */
-int get_token(const char *src, char **p_dst)
+/*
+ * Allocate a new string which is a copy of the input string with quotes
+ * removed. If quotes are not closed properly, set missing_quote to the
+ * missing quote char.
+ */
+static char *unquote_str(const char *str, size_t n, int allow_missing_quote,
+ char *missing_quote)
{
- unsigned s = 0, d = 0, dstlen;
- int quoted = 0;
+ unsigned s = 1, d = 0;
+ char quote = str[0];
char *dst;
+ int closed = 0;
- dstlen = strlen(src) + 1;
- dst = ec_malloc(dstlen);
- if (dst == NULL)
- return -ENOMEM;
-
- /* skip spaces */
- while (isblank(src[s]))
- s++;
-
- /* empty token */
- if (isend(src[s])) {
- ec_free(dst);
- return -ENOENT;
+ dst = ec_malloc(n);
+ if (dst == NULL) {
+ errno = ENOMEM;
+ return NULL;
}
/* copy token and remove quotes */
- while (src[s] != '\0') {
- if (d >= dstlen) {
- ec_free(dst);
- return -EMSGSIZE;
- }
-
- if ((isblank(src[s]) || isend(src[s])) && quoted == 0)
- break;
-
- if (src[s] == '\\' && src[s+1] == '"') {
- dst[d++] = '"';
+ while (s < n && d < n && str[s] != '\0') {
+ if (str[s] == '\\' && str[s+1] == quote) {
+ dst[d++] = quote;
s += 2;
continue;
}
- if (src[s] == '\\' && src[s+1] == '\\') {
+ if (str[s] == '\\' && str[s+1] == '\\') {
dst[d++] = '\\';
s += 2;
continue;
}
- if (src[s] == '"') {
+ if (str[s] == quote) {
s++;
- quoted = !quoted;
- continue;
+ closed = 1;
+ break;
}
- dst[d++] = src[s++];
+ dst[d++] = str[s++];
}
- /* not enough room in dst buffer */
- if (d >= dstlen) {
+ /* not enough room in dst buffer (should not happen) */
+ if (d >= n) {
ec_free(dst);
- return -EMSGSIZE;
+ errno = EMSGSIZE;
+ return NULL;
}
- /* end of string during quote */
- if (quoted) {
- ec_free(dst);
- return -EINVAL;
+ /* quote not closed */
+ if (closed == 0) {
+ if (missing_quote != NULL)
+ *missing_quote = str[0];
+ if (allow_missing_quote == 0) {
+ ec_free(dst);
+ errno = EINVAL;
+ return NULL;
+ }
}
-
dst[d++] = '\0';
- *p_dst = dst;
- return s;
+
+ return dst;
}
-static int safe_realloc(void *arg, size_t size)
+static size_t eat_quoted_str(const char *str)
{
- void **pptr = arg;
- void *new_ptr = ec_realloc(*pptr, size);
+ size_t i = 0;
+ char quote = str[0];
- if (new_ptr == NULL)
- return -1;
- *pptr = new_ptr;
- return 0;
+ while (str[i] != '\0') {
+ if (str[i] != '\\' && str[i+1] == quote)
+ return i + 2;
+ i++;
+ }
+
+ /* unclosed quote, will be detected later */
+ return i;
}
-static char **tokenize(const char *str, int add_empty)
+static size_t eat_str(const char *str)
{
- char **table = NULL, *token;
- unsigned i, count = 1, off = 0;
- int ret;
+ size_t i = 0;
- if (safe_realloc(&table, sizeof(char *)) < 0)
- return NULL;
+ /* skip spaces */
+ while (!isblank(str[i]) && str[i] != '\0')
+ i++;
- table[0] = NULL;
+ return i;
+}
- while (1) {
- ret = get_token(str + off, &token);
- if (ret == -ENOENT)
- break;
- else if (ret < 0)
- goto fail;
+static struct ec_strvec *tokenize(const char *str, int completion,
+ int allow_missing_quote, char *missing_quote)
+{
+ struct ec_strvec *strvec = NULL;
+ size_t off = 0, len, suboff, sublen;
+ char *word = NULL, *concat = NULL, *tmp;
+ int last_is_space = 1;
- off += ret;
- count++;
- if (safe_realloc(&table, sizeof(char *) * count) < 0)
- goto fail;
- table[count - 2] = token;
- table[count - 1] = NULL;
- }
+// printf("str=%s\n", str);
- if (add_empty && (off != strlen(str) || strlen(str) == 0)) {
- token = ec_strdup("");
- if (token == NULL)
- goto fail;
+ strvec = ec_strvec_new();
+ if (strvec == NULL)
+ goto fail;
- count++;
- if (safe_realloc(&table, sizeof(char *) * count) < 0)
+ while (str[off] != '\0') {
+ len = eat_spaces(&str[off]);
+ if (len > 0)
+ last_is_space = 1;
+// printf("space=%zd\n", len);
+ off += len;
+
+ len = 0;
+ suboff = off;
+ while (str[suboff] != '\0') {
+ last_is_space = 0;
+ if (str[suboff] == '"' || str[suboff] == '\'') {
+ sublen = eat_quoted_str(&str[suboff]);
+// printf("sublen=%zd\n", sublen);
+ word = unquote_str(&str[suboff], sublen,
+ allow_missing_quote, missing_quote);
+ } else {
+ sublen = eat_str(&str[suboff]);
+// printf("sublen=%zd\n", sublen);
+ if (sublen == 0)
+ break;
+ word = ec_strndup(&str[suboff], sublen);
+ }
+
+ if (word == NULL)
+ goto fail;
+// printf("word=%s\n", word);
+
+ len += sublen;
+ suboff += sublen;
+
+ if (concat == NULL) {
+ concat = word;
+ word = NULL;
+ } else {
+ tmp = ec_realloc(concat, len + 1);
+ if (tmp == NULL)
+ goto fail;
+ concat = tmp;
+ strcat(concat, word);
+ ec_free(word);
+ word = NULL;
+ }
+ }
+
+ if (concat != NULL) {
+ if (ec_strvec_add(strvec, concat) < 0)
+ goto fail;
+ ec_free(concat);
+ concat = NULL;
+ }
+
+// printf("str off=%zd len=%zd\n", off, len);
+ off += len;
+ }
+
+ /* in completion mode, append an empty token if the string ends
+ * with space */
+ if (completion && last_is_space) {
+ if (ec_strvec_add(strvec, "") < 0)
goto fail;
- table[count - 2] = token;
- table[count - 1] = NULL;
}
- return table;
+ return strvec;
fail:
- for (i = 0; i < count; i++)
- ec_free(table[i]);
- ec_free(table);
+ ec_free(word);
+ ec_free(concat);
+ ec_strvec_free(strvec);
return NULL;
}
-/* XXX broken: how to support that:
- shlex(
- str("toto"),
- many(str("titi")),
- )
-
- that would match:
- toto
- toto titi
- toto titi titi ...
-
- --> maybe we should not try to create/match the spaces automatically
-
- it would become:
-
- shlex(
- option(space()), auto?
- str("toto"),
- many(
- space(),
- str("titi coin"),
- ),
- option(space()), auto?
- )
-
- -> the goal of shlex would only be to unquote
- -> the creation of auto-spaces would be in another token shcmd
-
- cmd = shcmd_new()
- shcmd_add_tk("ip", tk_ip_new())
- shcmd_set_syntax("show <ip>")
-
-
- */
static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
- struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
- unsigned int i;
- char **tokens, **t;
+ struct ec_strvec *new_vec = NULL, *match_strvec;
+ struct ec_parsed_tk *parsed_tk = NULL, *child_parsed_tk;
+ const char *str;
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
return NULL;
- tokens = tokenize(str, 0);
- if (tokens == NULL)
- goto fail;
-
- t = &tokens[0];
- for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) {
- if (*t == NULL)
- goto fail;
+ if (ec_strvec_len(strvec) == 0)
+ return parsed_tk;
- child_parsed_tk = ec_tk_parse(tk->table[i], *t);
- if (child_parsed_tk == NULL)
- goto fail;
-
- ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
- if (strlen(child_parsed_tk->str) == 0)
- t--;
- else if (strlen(child_parsed_tk->str) != strlen(*t))
- goto fail;
- }
+ str = ec_strvec_val(strvec, 0);
+ new_vec = tokenize(str, 0, 0, NULL);
+ if (new_vec == NULL)
+ goto fail;
- /* check it was the last token */
- if (*t != NULL)
+ child_parsed_tk = ec_tk_parse_tokens(tk->child, new_vec);
+ if (child_parsed_tk == NULL)
goto fail;
- if (tokens != NULL) {
- for (t = &tokens[0]; *t != NULL; t++)
- ec_free(*t);
- ec_free(tokens);
- tokens = NULL;
+ if (!ec_parsed_tk_matches(child_parsed_tk) ||
+ ec_parsed_tk_len(child_parsed_tk) !=
+ ec_strvec_len(new_vec)) {
+ ec_strvec_free(new_vec);
+ ec_parsed_tk_free(child_parsed_tk);
+ return parsed_tk;
}
+ ec_strvec_free(new_vec);
+ new_vec = NULL;
- parsed_tk->str = ec_strdup(str);
+ ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+ match_strvec = ec_strvec_ndup(strvec, 1);
+ if (match_strvec == NULL)
+ goto fail;
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
fail:
- if (tokens != NULL) {
- for (t = &tokens[0]; *t != NULL; t++)
- ec_free(*t);
- ec_free(tokens);
- }
+ ec_strvec_free(new_vec);
ec_parsed_tk_free(parsed_tk);
return NULL;
}
static struct ec_completed_tk *ec_tk_shlex_complete(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
struct ec_completed_tk *completed_tk, *child_completed_tk = NULL;
- struct ec_parsed_tk *child_parsed_tk;
- unsigned int i;
- char **tokens, **t;
+ struct ec_strvec *new_vec = NULL;
+ const char *str;
+ char missing_quote;
+
+// printf("==================\n");
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ return NULL;
- tokens = tokenize(str, 1);
- if (tokens == NULL)
+ if (ec_strvec_len(strvec) != 1)
+ return completed_tk;
+
+ str = ec_strvec_val(strvec, 0);
+ new_vec = tokenize(str, 1, 1, &missing_quote);
+ if (new_vec == NULL)
goto fail;
- printf("complete <%s>\n", str);
- for (t = &tokens[0]; *t != NULL; t++)
- printf(" token <%s> %p\n", *t, *t);
+// ec_strvec_dump(new_vec, stdout);
- t = &tokens[0];
+ child_completed_tk = ec_tk_complete_tokens(tk->child, new_vec);
+ if (child_completed_tk == NULL)
+ goto fail;
+
+ ec_strvec_free(new_vec);
+ new_vec = NULL;
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
+
+ return completed_tk;
- completed_tk = ec_completed_tk_new();
- if (completed_tk == NULL)
- return NULL;
+#if 0
for (i = 0, t = &tokens[0]; i < tk->len; i++, t++) {
if (*(t + 1) != NULL) {
child_parsed_tk = ec_tk_parse(tk->table[i], *t);
}
ec_completed_tk_dump(stdout, completed_tk);
-
- return completed_tk;
+#endif
fail:
- if (tokens != NULL) {
- for (t = &tokens[0]; *t != NULL; t++)
- ec_free(*t);
- ec_free(tokens);
- }
+ ec_strvec_free(new_vec);
ec_completed_tk_free(completed_tk);
-
return NULL;
}
static void ec_tk_shlex_free_priv(struct ec_tk *gen_tk)
{
struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
- unsigned int i;
- for (i = 0; i < tk->len; i++)
- ec_tk_free(tk->table[i]);
- ec_free(tk->table);
+ ec_tk_free(tk->child);
}
static struct ec_tk_ops ec_tk_shlex_ops = {
+ .typename = "shlex",
.parse = ec_tk_shlex_parse,
.complete = ec_tk_shlex_complete,
.free_priv = ec_tk_shlex_free_priv,
};
-struct ec_tk *ec_tk_shlex_new(const char *id)
+struct ec_tk *ec_tk_shlex_new(const char *id, struct ec_tk *child)
{
struct ec_tk_shlex *tk = NULL;
- tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops, sizeof(*tk));
- if (tk == NULL)
+ if (child == NULL)
return NULL;
- tk->table = NULL;
- tk->len = 0;
-
- return &tk->gen;
-}
-
-struct ec_tk *ec_tk_shlex_new_list(const char *id, ...)
-{
- struct ec_tk_shlex *tk = NULL;
- struct ec_tk *child;
- va_list ap;
-
- va_start(ap, id);
-
- tk = (struct ec_tk_shlex *)ec_tk_shlex_new(id);
- if (tk == NULL)
- goto fail;
-
- for (child = va_arg(ap, struct ec_tk *);
- child != EC_TK_ENDLIST;
- child = va_arg(ap, struct ec_tk *)) {
- if (child == NULL)
- goto fail;
-
- ec_tk_shlex_add(&tk->gen, child);
+ tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops,
+ sizeof(*tk));
+ if (tk == NULL) {
+ ec_tk_free(child);
+ return NULL;
}
- va_end(ap);
- return &tk->gen;
+ tk->child = child;
-fail:
- ec_tk_free(&tk->gen); /* will also free children */
- va_end(ap);
- return NULL;
-}
-
-int ec_tk_shlex_add(struct ec_tk *gen_tk, struct ec_tk *child)
-{
- struct ec_tk_shlex *tk = (struct ec_tk_shlex *)gen_tk;
- struct ec_tk **table;
-
- // XXX check tk type
-
- assert(tk != NULL);
- assert(child != NULL);
-
- table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
- if (table == NULL)
- return -1;
-
- tk->table = table;
- table[tk->len] = child;
- tk->len ++;
-
- return 0;
+ return &tk->gen;
}
static int ec_tk_shlex_testcase(void)
struct ec_tk *tk;
int ret = 0;
- tk = ec_tk_shlex_new_list(NULL,
- ec_tk_str_new(NULL, "foo"),
- ec_tk_option_new(NULL, ec_tk_str_new(NULL, "toto")),
- ec_tk_str_new(NULL, "bar"),
- EC_TK_ENDLIST);
+ tk = ec_tk_shlex_new(NULL,
+ ec_tk_seq_new_list(NULL,
+ ec_tk_str_new(NULL, "foo"),
+ ec_tk_option_new(NULL,
+ ec_tk_str_new(NULL, "toto")
+ ),
+ ec_tk_str_new(NULL, "bar"),
+ EC_TK_ENDLIST
+ )
+ );
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo bar", "foo bar");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " \"foo\" \"bar\"",
- " \"foo\" \"bar\"");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo toto bar", "foo toto bar");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo bar ", " foo bar ");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo bar xxx", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo barxxx", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " \"foo \" \"bar\"", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " foo bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'foo' \"bar\"", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " 'f'oo 'toto' bar",
+ EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
- tk = ec_tk_shlex_new_list(NULL,
- ec_tk_str_new(NULL, "foo"),
- ec_tk_option_new(NULL, ec_tk_str_new(NULL, "toto")),
- ec_tk_str_new(NULL, "bar"),
- ec_tk_str_new(NULL, "titi"),
- EC_TK_ENDLIST);
+ tk = ec_tk_shlex_new(NULL,
+ ec_tk_seq_new_list(NULL,
+ ec_tk_str_new(NULL, "foo"),
+ ec_tk_option_new(NULL,
+ ec_tk_str_new(NULL, "toto")
+ ),
+ ec_tk_str_new(NULL, "bar"),
+ ec_tk_str_new(NULL, "titi"),
+ EC_TK_ENDLIST
+ )
+ );
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, " ", "foo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "foo ",
- "bar", "toto", EC_TK_ENDLIST);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo t", "oto");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo b", "ar");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo bar", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo bar ", "titi");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo toto bar ", "titi");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo barx", "");
- ec_tk_free(tk);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", EC_TK_ENDLIST,
+ "foo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ " ", EC_TK_ENDLIST,
+ "foo", EC_TK_ENDLIST,
+ "foo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", EC_TK_ENDLIST,
+ "", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo ", EC_TK_ENDLIST,
+ "bar", "toto", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo t", EC_TK_ENDLIST,
+ "oto", EC_TK_ENDLIST,
+ "oto");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo b", EC_TK_ENDLIST,
+ "ar", EC_TK_ENDLIST,
+ "ar");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo bar", EC_TK_ENDLIST,
+ "", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo bar ", EC_TK_ENDLIST,
+ "titi", EC_TK_ENDLIST,
+ "titi");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo toto bar ", EC_TK_ENDLIST,
+ "titi", EC_TK_ENDLIST,
+ "titi");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo barx", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ec_tk_free(tk);
return ret;
}
#ifndef ECOLI_TK_SHLEX_
#define ECOLI_TK_SHLEX_
-#include <sys/queue.h>
-
#include <ecoli_tk.h>
-struct ec_tk_shlex {
- struct ec_tk gen;
- struct ec_tk **table;
- unsigned int len;
-};
-
-struct ec_tk *ec_tk_shlex_new(const char *id);
-
-/* list must be terminated with EC_TK_ENDLIST */
-struct ec_tk *ec_tk_shlex_new_list(const char *id, ...);
-
-int ec_tk_shlex_add(struct ec_tk *tk, struct ec_tk *child);
+struct ec_tk *ec_tk_shlex_new(const char *id, struct ec_tk *child);
#endif
#include <ecoli_log.h>
#include <ecoli_test.h>
#include <ecoli_malloc.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_space.h>
+struct ec_tk_space {
+ struct ec_tk gen;
+};
+
static struct ec_parsed_tk *ec_tk_space_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
- struct ec_parsed_tk *parsed_tk;
+ struct ec_parsed_tk *parsed_tk = NULL;
+ struct ec_strvec *match_strvec;
+ const char *str;
size_t len = 0;
- if (!isspace(str[0]))
- return NULL;
-
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
+ if (ec_strvec_len(strvec) == 0)
+ return parsed_tk;
+
+ str = ec_strvec_val(strvec, 0);
while (isspace(str[len]))
len++;
+ if (len == 0 || len != strlen(str))
+ return parsed_tk;
+
+ match_strvec = ec_strvec_ndup(strvec, 1);
+ if (match_strvec == NULL)
+ goto fail;
- parsed_tk->str = ec_strndup(str, len);
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
}
static struct ec_tk_ops ec_tk_space_ops = {
+ .typename = "space",
.parse = ec_tk_space_parse,
+ .complete = ec_tk_default_complete,
};
struct ec_tk *ec_tk_space_new(const char *id)
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " ", " ");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " ", " ");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", " ");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo ", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, " ", "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo ", EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, " ", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ " ", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#include <ecoli_tk.h>
-struct ec_tk_space {
- struct ec_tk gen;
-};
-
struct ec_tk *ec_tk_space_new(const char *id);
#endif
#include <ecoli_log.h>
#include <ecoli_malloc.h>
#include <ecoli_test.h>
+#include <ecoli_strvec.h>
#include <ecoli_tk.h>
#include <ecoli_tk_str.h>
+struct ec_tk_str {
+ struct ec_tk gen;
+ char *string;
+ unsigned len;
+};
+
static struct ec_parsed_tk *ec_tk_str_parse(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
- struct ec_parsed_tk *parsed_tk;
+ struct ec_strvec *match_strvec;
+ struct ec_parsed_tk *parsed_tk = NULL;
+ const char *str;
- if (strncmp(str, tk->string, tk->len) != 0)
- return NULL;
-
- parsed_tk = ec_parsed_tk_new(gen_tk);
+ parsed_tk = ec_parsed_tk_new();
if (parsed_tk == NULL)
- return NULL;
+ goto fail;
+
+ if (ec_strvec_len(strvec) == 0)
+ return parsed_tk;
- parsed_tk->str = ec_strndup(str, tk->len);
+ str = ec_strvec_val(strvec, 0);
+ if (strcmp(str, tk->string) != 0)
+ return parsed_tk;
+
+ match_strvec = ec_strvec_ndup(strvec, 1);
+ if (match_strvec == NULL)
+ goto fail;
+
+ ec_parsed_tk_set_match(parsed_tk, gen_tk, match_strvec);
return parsed_tk;
+
+ fail:
+ ec_parsed_tk_free(parsed_tk);
+ return NULL;
}
static struct ec_completed_tk *ec_tk_str_complete(const struct ec_tk *gen_tk,
- const char *str)
+ const struct ec_strvec *strvec)
{
struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
struct ec_completed_tk *completed_tk;
struct ec_completed_tk_elt *completed_tk_elt;
- size_t n;
+ const char *str, *add;
+ size_t n = 0;
completed_tk = ec_completed_tk_new();
if (completed_tk == NULL)
return NULL;
- /* check the string has the same beginning than the token */
- for (n = 0; n < tk->len; n++) {
- if (str[n] != tk->string[n])
- break;
- }
-
- if (str[n] != '\0')
- return completed_tk;
- if (tk->string[n] == '\0')
+ if (ec_strvec_len(strvec) > 1)
return completed_tk;
- completed_tk_elt = ec_completed_tk_elt_new(gen_tk, tk->string + n,
- tk->string);
+ if (ec_strvec_len(strvec) == 1) {
+ str = ec_strvec_val(strvec, 0);
+ for (n = 0; n < tk->len; n++) {
+ if (str[n] != tk->string[n])
+ break;
+ }
+
+ if (str[n] != '\0')
+ add = NULL;
+ else
+ add = tk->string + n;
+ }
+
+ completed_tk_elt = ec_completed_tk_elt_new(gen_tk, add);
if (completed_tk_elt == NULL) {
ec_completed_tk_free(completed_tk);
return NULL;
ec_free(tk->string);
}
-static struct ec_tk_ops ec_tk_str_ops = {
+static const struct ec_tk_ops ec_tk_str_ops = {
+ .typename = "str",
.parse = ec_tk_str_parse,
.complete = ec_tk_str_complete,
.free_priv = ec_tk_str_free_priv,
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foobar", "foo");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, " foo", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "foo", "bar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foobar", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, " foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
ec_tk_free(tk);
tk = ec_tk_str_new(NULL, "Здравствуйте");
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "Здравствуйте", "Здравствуйте");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "Здравствуйте John!", "Здравствуйте");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", NULL);
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "", NULL);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "Здравствуйте",
+ "John!", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "", EC_TK_ENDLIST);
ec_tk_free(tk);
/* an empty token string always matches */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "", "");
- ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "", "foo", EC_TK_ENDLIST);
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo", EC_TK_ENDLIST);
ec_tk_free(tk);
/* test completion */
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "", EC_TK_ENDLIST,
+ "foo", EC_TK_ENDLIST,
+ "foo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "f", EC_TK_ENDLIST,
+ "oo", EC_TK_ENDLIST,
+ "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "foo", EC_TK_ENDLIST,
+ "", EC_TK_ENDLIST,
+ "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk,
+ "x", EC_TK_ENDLIST,
+ EC_TK_ENDLIST,
+ "");
ec_tk_free(tk);
return ret;
#include <ecoli_tk.h>
-struct ec_tk_str {
- struct ec_tk gen;
- char *string;
- unsigned len;
-};
-
struct ec_tk *ec_tk_str_new(const char *id, const char *str);
#endif
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#define _GNU_SOURCE /* for asprintf */
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <ecoli_tk_seq.h>
#include <ecoli_tk_space.h>
#include <ecoli_tk_or.h>
+#include <ecoli_tk_shlex.h>
+#include <ecoli_tk_int.h>
static struct ec_tk *commands;
-/* Set to a character describing the type of completion being attempted by
- rl_complete_internal; available for use by application completion
- functions. */
-extern int rl_completion_type;
-/* Set to the last key used to invoke one of the completion functions */
-extern int rl_completion_invoking_key;
-
-int my_complete(int x, int y)
-{
- (void)x;
- (void)y;
-
- return 0;
-}
-
-char *my_completion_entry(const char *s, int state)
+static char *my_completion_entry(const char *s, int state)
{
static struct ec_completed_tk *c;
+ static struct ec_completed_tk_iter *iter;
static const struct ec_completed_tk_elt *elt;
+ char *out_string;
- (void)s;
if (state == 0) {
- char *start;
+ char *line;
+
+ ec_completed_tk_free(c);
- if (c != NULL)
- ec_completed_tk_free(c);
+ line = strdup(rl_line_buffer);
+ if (line == NULL)
+ return NULL;
+ line[rl_point] = '\0';
- start = strdup(rl_line_buffer);
- if (start == NULL)
+ c = ec_tk_complete(commands, line);
+ free(line);
+ if (c == NULL)
return NULL;
- start[rl_point] = '\0';
- c = ec_tk_complete(commands, start);
- ec_completed_tk_iter_start(c);
+ ec_completed_tk_iter_free(iter);
+ iter = ec_completed_tk_iter_new(c, ITER_MATCH);
+ if (iter == NULL)
+ return NULL;
}
- elt = ec_completed_tk_iter_next(c);
+ elt = ec_completed_tk_iter_next(iter);
if (elt == NULL)
return NULL;
- return strdup(elt->full);
+ if (asprintf(&out_string, "%s%s", s, elt->add) < 0)
+ return NULL;
+
+ return out_string;
}
-char **my_attempted_completion(const char *text, int start, int end)
+static char **my_attempted_completion(const char *text, int start, int end)
{
(void)start;
(void)end;
- // XXX when it returns NULL, it completes with a file
+
+ /* remove default file completion */
+ rl_attempted_completion_over = 1;
+
return rl_completion_matches(text, my_completion_entry);
}
+
+static int show_help(int ignore, int invoking_key)
+{
+ struct ec_completed_tk *c;
+ char *line;
+
+ (void)ignore;
+ (void)invoking_key;
+
+ printf("\nhelp:\n");
+ line = strdup(rl_line_buffer);
+ if (line == NULL)
+ return 1;
+ line[rl_point] = '\0';
+
+ c = ec_tk_complete(commands, line);
+ free(line);
+ if (c == NULL)
+ return 1;
+ ec_completed_tk_dump(stdout, c);
+ ec_completed_tk_free(c);
+
+ rl_forced_update_display();
+
+ return 0;
+}
+
int main(void)
{
struct ec_parsed_tk *p;
// const char *name;
char *line;
- commands = ec_tk_seq_new_list(NULL,
- ec_tk_str_new(NULL, "hello"),
- ec_tk_space_new(NULL),
- ec_tk_or_new_list("name",
- ec_tk_str_new(NULL, "john"),
- ec_tk_str_new(NULL, "mike"),
- EC_TK_ENDLIST),
- EC_TK_ENDLIST);
+ rl_bind_key('?', show_help);
+
+ commands = ec_tk_shlex_new(NULL,
+ ec_tk_seq_new_list(NULL,
+ ec_tk_str_new(NULL, "hello"),
+ ec_tk_or_new_list("name",
+ ec_tk_str_new(NULL, "john"),
+ ec_tk_str_new(NULL, "johnny"),
+ ec_tk_str_new(NULL, "mike"),
+ ec_tk_int_new(NULL, 0, 10, 10),
+ EC_TK_ENDLIST
+ ),
+ EC_TK_ENDLIST
+ )
+ );
if (commands == NULL) {
printf("cannot create token\n");
return 1;
}
- //rl_bind_key('\t', my_complete);
-
- //rl_completion_entry_function = my_completion_entry;
rl_attempted_completion_function = my_attempted_completion;
while (1) {
if (line == NULL)
break;
- // XXX need a "parse_all"
p = ec_tk_parse(commands, line);
ec_parsed_tk_dump(stdout, p);
add_history(line);
#include <stdio.h>
#include <assert.h>
#include <getopt.h>
+#include <execinfo.h>
+#include <ecoli_log.h>
#include <ecoli_test.h>
#include <ecoli_malloc.h>
+#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
+ ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+
+static int log_level = EC_LOG_INFO;
+static int alloc_fail_proba = 0;
+
static const char ec_short_options[] =
- "h" /* help */
+ "h" /* help */
+ "l:" /* log-level */
+ "r:" /* random-alloc-fail */
;
-enum {
- /* long options */
- EC_OPT_LONG_MIN_NUM = 256,
#define EC_OPT_HELP "help"
- EC_OPT_HELP_NUM,
-};
+#define EC_OPT_LOG_LEVEL "log-level"
+#define EC_OPT_RANDOM_ALLOC_FAIL "random-alloc-fail"
static const struct option ec_long_options[] = {
- {EC_OPT_HELP, 1, NULL, EC_OPT_HELP_NUM},
+ {EC_OPT_HELP, 1, NULL, 'h'},
+ {EC_OPT_LOG_LEVEL, 1, NULL, 'l'},
+ {EC_OPT_RANDOM_ALLOC_FAIL, 1, NULL, 'r'},
{NULL, 0, NULL, 0}
};
static void usage(const char *prgname)
{
printf("%s [options]\n"
- " --"EC_OPT_HELP": show this help\n"
+ " -h\n"
+ " --"EC_OPT_HELP"\n"
+ " Show this help.\n"
+ " -l <level>\n"
+ " --"EC_OPT_LOG_LEVEL"=<level>\n"
+ " Set log level (0 = no log, 6 = verbose).\n"
+ " -r <probability>\n"
+ " --"EC_OPT_RANDOM_ALLOC_FAIL"=<probability>\n"
+ " Cause malloc to fail randomly. This helps to debug\n"
+ " leaks or crashes in error cases. The probability is\n"
+ " between 0 and 100.\n"
, prgname);
}
+static int
+parse_int(const char *s, int min, int max, int *ret, unsigned int base)
+{
+ char *end = NULL;
+ long long n;
+
+ n = strtoll(s, &end, base);
+ if ((s[0] == '\0') || (end == NULL) || (*end != '\0'))
+ return -1;
+ if (n < min)
+ return -1;
+ if (n > max)
+ return -1;
+
+ *ret = n;
+ return 0;
+}
+
static void parse_args(int argc, char **argv)
{
int opt;
switch (opt) {
case 'h': /* help */
- case EC_OPT_HELP_NUM:
usage(argv[0]);
exit(0);
+ case 'l': /* log-level */
+ if (parse_int(optarg, EC_LOG_EMERG,
+ EC_LOG_DEBUG, &log_level, 10) < 0) {
+ printf("Invalid log value\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+
+ case 'r': /* random-alloc-fail */
+ if (parse_int(optarg, 0, 100, &alloc_fail_proba,
+ 10) < 0) {
+ printf("Invalid probability value\n");
+ usage(argv[0]);
+ exit(1);
+ }
+ break;
+
default:
usage(argv[0]);
exit(1);
static struct debug_alloc_hdr_list debug_alloc_hdr_list =
TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
+#define STACK_SZ 16
struct debug_alloc_hdr {
TAILQ_ENTRY(debug_alloc_hdr) next;
const char *file;
unsigned int line;
size_t size;
+ void *stack[STACK_SZ];
+ int stacklen;
unsigned int cookie;
};
size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
void *ret;
- hdr = malloc(new_size);
+
+ if (alloc_fail_proba != 0 && (random() % 100) < alloc_fail_proba)
+ hdr = NULL;
+ else
+ hdr = malloc(new_size);
+
if (hdr == NULL) {
ret = NULL;
} else {
hdr->file = file;
hdr->line = line;
hdr->size = size;
+ hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
hdr->cookie = 0x12345678;
TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
ret = hdr + 1;
ftr = (struct debug_alloc_ftr *)(
(char *)hdr + size + sizeof(*hdr));
- ftr->cookie = 0x12345678;
+ ftr->cookie = 0x87654321;
}
- ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n",
+ ec_log(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p\n",
file, line, size, ret);
return ret;
(void)file;
(void)line;
- ec_log(EC_LOG_INFO, "%s:%d: info: free(%p)\n", file, line, ptr);
+ ec_log(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
if (ptr == NULL)
return;
}
ftr = (ptr + hdr->size);
- if (ftr->cookie != 0x12345678) {
+ if (ftr->cookie != 0x87654321) {
ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
file, line, ptr);
abort();
free(hdr);
}
-void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
+static void *debug_realloc(void *ptr, size_t size, const char *file,
+ unsigned int line)
{
struct debug_alloc_hdr *hdr, *h;
struct debug_alloc_ftr *ftr;
}
ftr = (ptr + hdr->size);
- if (ftr->cookie != 0x12345678) {
+ if (ftr->cookie != 0x87654321) {
ec_log(EC_LOG_ERR,
"%s:%d: error: realloc(%p): bad end cookie\n",
file, line, ptr);
hdr->file = file;
hdr->line = line;
hdr->size = size;
+ hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
hdr->cookie = 0x12345678;
TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
ftr = (struct debug_alloc_ftr *)(
(char *)hdr + size + sizeof(*hdr));
- ftr->cookie = 0x12345678;
+ ftr->cookie = 0x87654321;
}
- ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n",
+ ec_log(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
file, line, ptr, size, ret);
return ret;
}
-void debug_alloc_dump(void)
+static int debug_alloc_dump_leaks(void)
{
struct debug_alloc_hdr *hdr;
+ int i;
+ char **buffer;
+
+ if (TAILQ_EMPTY(&debug_alloc_hdr_list))
+ return 0;
TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
- ec_log(EC_LOG_ERR, "%s:%d: error: memory leak size=%zd ptr=%p\n",
+ ec_log(EC_LOG_ERR,
+ "%s:%d: error: memory leak size=%zd ptr=%p\n",
hdr->file, hdr->line, hdr->size, hdr + 1);
+ buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
+ if (buffer == NULL) {
+ for (i = 0; i < hdr->stacklen; i++)
+ ec_log(EC_LOG_ERR, " %p\n", hdr->stack[i]);
+ } else {
+ for (i = 0; i < hdr->stacklen; i++)
+ ec_log(EC_LOG_ERR, " %s\n",
+ buffer ? buffer[i] : "unknown");
+ }
+ free(buffer);
}
+
+ ec_log(EC_LOG_ERR,
+ " missing static syms, use: addr2line -f -e <prog> <addr>\n");
+
+ return -1;
+}
+
+static int debug_log(unsigned int level, void *opaque, const char *str)
+{
+ (void)opaque;
+
+ if (level > (unsigned int)log_level)
+ return 0;
+
+ return printf("%s", str);
}
int main(int argc, char **argv)
{
- int ret;
+ int ret, leaks;
parse_args(argc, argv);
- TAILQ_INIT(&debug_alloc_hdr_list);
+ ec_log_register(debug_log, NULL);
+
/* register a new malloc to track memleaks */
+ TAILQ_INIT(&debug_alloc_hdr_list);
if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
ec_log(EC_LOG_ERR, "cannot register new malloc\n");
return -1;
ret = ec_test_all();
ec_malloc_unregister();
- debug_alloc_dump();
+ leaks = debug_alloc_dump_leaks();
- if (ret != 0)
+ if (alloc_fail_proba == 0 && ret != 0) {
printf("tests failed\n");
+ return 1;
+ } else if (alloc_fail_proba != 0 && leaks != 0) {
+ printf("tests failed (memory leak)\n");
+ return 1;
+ }
+
+ printf("\ntests ok\n");
return 0;
}