srcs := ecoli_tk.c 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
+srcs += ecoli_malloc.c ecoli_log.c ecoli_tk_option.c
+srcs += ecoli_tk_shlex.c
shlib-y-$(O)libecoli.so := $(srcs)
exe-y-$(O)test = $(srcs) main.c
+ldflags-$(O)readline = -lreadline
+exe-y-$(O)readline = $(srcs) main-readline.c
+
include $(ECOLI)/mk/ecoli-post.mk
all: _ecoli_all
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
#include <ecoli_log.h>
#include <ecoli_malloc.h>
const char *s;
int ret = -1;
+ assert(expected != NULL);
+
c = ec_tk_complete(tk, input);
s = ec_completed_tk_smallest_start(c);
- if (s == NULL && expected == NULL)
- ret = 0;
- else if (s != NULL && expected != NULL &&
- !strcmp(s, expected))
+ if (!strcmp(s, expected))
ret = 0;
- if (expected == NULL && ret != 0)
- ec_log(EC_LOG_ERR,
- "tk should not complete but completes with <%s>\n", s);
- if (expected != NULL && ret != 0)
+ if (ret != 0)
ec_log(EC_LOG_ERR,
- "tk should complete with <%s> but completes with <%s>\n",
+ "should complete with <%s> but completes with <%s>\n",
expected, s);
ec_completed_tk_free(c);
ec_log(EC_LOG_ERR,
"nb_completion (%d) does not match (%d)\n",
count, ec_completed_tk_count(c));
+ ec_completed_tk_dump(stdout, c);
goto out;
}
}
}
-/* XXX todo */
-/* int ec_test_check_tk_complete_list(const struct ec_tk *tk, */
-/* const char *input, ...) */
-
int ec_test_all(void)
{
struct ec_test *test;
TAILQ_INIT(&debug_alloc_hdr_list);
/* register a new malloc to trac memleaks */
- if (0 && ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
+ if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
ec_log(EC_LOG_ERR, "cannot register new malloc\n");
return -1;
}
/* XXX define when to use ec_tk_complete() or tk->complete()
* (same for parse)
* suggestion: tk->op() is internal, user calls the function
+ * other idea: have 2 functions
*/
struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
const char *str)
ec_free(elt);
}
-struct ec_completed_tk *ec_completed_tk_merge(
- struct ec_completed_tk *completed_tk1,
+void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
struct ec_completed_tk *completed_tk2)
{
struct ec_completed_tk_elt *elt;
- if (completed_tk2 == NULL)
- return completed_tk1;
-
- if (completed_tk1 == NULL)
- return completed_tk2;
+ assert(completed_tk1 != NULL);
+ assert(completed_tk2 != NULL);
while (!TAILQ_EMPTY(&completed_tk2->elts)) {
elt = TAILQ_FIRST(&completed_tk2->elts);
}
ec_completed_tk_free(completed_tk2);
-
- return completed_tk1;
}
void ec_completed_tk_free(struct ec_completed_tk *completed_tk)
const char *ec_completed_tk_smallest_start(
const struct ec_completed_tk *completed_tk)
{
- if (completed_tk == NULL)
- return NULL;
+ if (completed_tk == NULL || completed_tk->smallest_start == NULL)
+ return "";
return completed_tk->smallest_start;
}
return completed_tk->count;
}
+
+void ec_completed_tk_iter_start(struct ec_completed_tk *completed_tk)
+{
+ if (completed_tk == NULL)
+ return;
+
+ completed_tk->cur = NULL;
+}
+
+const struct ec_completed_tk_elt *ec_completed_tk_iter_next(
+ struct ec_completed_tk *completed_tk)
+{
+ if (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);
+ }
+
+ return completed_tk->cur;
+}
struct ec_completed_tk {
struct ec_completed_tk_elt_list elts;
+ const struct ec_completed_tk_elt *cur;
unsigned count;
char *smallest_start;
};
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);
-struct ec_completed_tk *ec_completed_tk_merge(
- struct ec_completed_tk *completed_tk1,
+void ec_completed_tk_merge(struct ec_completed_tk *completed_tk1,
struct ec_completed_tk *completed_tk2);
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);
-/* can return NULL */
+/* 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);
+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);
+
#endif
struct ec_tk *tk;
int ret = 0;
- /* all inputs match */
tk = ec_tk_empty_new(NULL);
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", NULL);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", "");
ec_tk_free(tk);
return ret;
ec_log(EC_LOG_ERR, "cannot create tk\n");
return -1;
}
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", NULL);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", NULL);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "1", "");
ec_tk_free(tk);
return ret;
--- /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_tk.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_test.h>
+
+static struct ec_parsed_tk *ec_tk_option_parse(const struct ec_tk *gen_tk,
+ const char *str)
+{
+ struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+ struct ec_parsed_tk *parsed_tk, *child_parsed_tk;
+
+ parsed_tk = ec_parsed_tk_new(gen_tk);
+ if (parsed_tk == NULL)
+ return NULL;
+
+ child_parsed_tk = ec_tk_parse(tk->child, str);
+ if (child_parsed_tk != NULL) {
+ ec_parsed_tk_add_child(parsed_tk, child_parsed_tk);
+ parsed_tk->str = ec_strndup(child_parsed_tk->str,
+ strlen(child_parsed_tk->str));
+ } else {
+ parsed_tk->str = ec_strdup("");
+ }
+
+ return parsed_tk;
+}
+
+static struct ec_completed_tk *ec_tk_option_complete(const struct ec_tk *gen_tk,
+ const char *str)
+{
+ struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+
+ return ec_tk_complete(tk->child, str);
+}
+
+static void ec_tk_option_free_priv(struct ec_tk *gen_tk)
+{
+ struct ec_tk_option *tk = (struct ec_tk_option *)gen_tk;
+
+ ec_tk_free(tk->child);
+}
+
+static struct ec_tk_ops ec_tk_option_ops = {
+ .parse = ec_tk_option_parse,
+ .complete = ec_tk_option_complete,
+ .free_priv = ec_tk_option_free_priv,
+};
+
+struct ec_tk *ec_tk_option_new(const char *id, struct ec_tk *child)
+{
+ struct ec_tk_option *tk = NULL;
+
+ if (child == NULL)
+ return NULL;
+
+ tk = (struct ec_tk_option *)ec_tk_new(id, &ec_tk_option_ops,
+ sizeof(*tk));
+ if (tk == NULL)
+ return NULL;
+
+ tk->child = child;
+
+ return &tk->gen;
+}
+
+static int ec_tk_option_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = ec_tk_option_new(NULL, ec_tk_str_new(NULL, "foo"));
+ if (tk == 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, "foo", "foo");
+ ret |= EC_TEST_CHECK_TK_PARSE(tk, "bar", "");
+ ec_tk_free(tk);
+
+ /* test completion */
+ tk = ec_tk_option_new(NULL, ec_tk_str_new(NULL, "foo"));
+ 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, "f", "oo");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "b", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE_LIST(tk, "",
+ "foo", EC_TK_ENDLIST);
+ ec_tk_free(tk);
+
+ return ret;
+}
+
+static struct ec_test ec_tk_option_test = {
+ .name = "tk_option",
+ .test = ec_tk_option_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_option_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_OPTION_
+#define ECOLI_TK_OPTION_
+
+#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
const char *str)
{
struct ec_tk_or *tk = (struct ec_tk_or *)gen_tk;
- struct ec_completed_tk *completed_tk = NULL, *child_completed_tk;
+ struct ec_completed_tk *completed_tk, *child_completed_tk;
size_t n;
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ return NULL;
+
for (n = 0; n < tk->len; n++) {
child_completed_tk = ec_tk_complete(tk->table[n], str);
if (child_completed_tk == NULL)
continue;
- completed_tk = ec_completed_tk_merge(completed_tk,
- child_completed_tk);
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
}
return completed_tk;
struct ec_tk *tk;
int ret = 0;
- /* all inputs starting with foo should match */
tk = ec_tk_or_new_list(NULL,
ec_tk_str_new(NULL, "foo"),
ec_tk_str_new(NULL, "bar"),
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", NULL);
+ 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",
#include <ecoli_test.h>
#include <ecoli_tk.h>
#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
#include <ecoli_tk_seq.h>
-// XXX to handle the quote, it will be done in tk_shseq
-// it will unquote the string and parse each token separately
static struct ec_parsed_tk *ec_tk_seq_parse(const struct ec_tk *gen_tk,
const char *str)
{
const char *str)
{
struct ec_tk_seq *tk = (struct ec_tk_seq *)gen_tk;
- struct ec_completed_tk *completed_tk;
+ struct ec_completed_tk *completed_tk, *child_completed_tk;
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 ec_completed_tk_new();
+ return completed_tk;
+
+ for (i = 0; i < tk->len; i++) {
+ child_completed_tk = ec_tk_complete(tk->table[i], str + len);
+ if (child_completed_tk == NULL) {
+ ec_completed_tk_free(completed_tk);
+ return NULL;
+ }
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
- /* parse the first tokens */
- for (i = 0; i < tk->len - 1; i++) {
parsed_tk = ec_tk_parse(tk->table[i], str + len);
if (parsed_tk == NULL)
break;
ec_parsed_tk_free(parsed_tk);
}
- completed_tk = ec_tk_complete(tk->table[i], str + len);
-
return completed_tk;
}
struct ec_tk *tk;
int ret = 0;
- /* all inputs starting with foo should match */
tk = ec_tk_seq_new_list(NULL,
ec_tk_str_new(NULL, "foo"),
ec_tk_str_new(NULL, "bar"),
/* test completion */
tk = 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) {
}
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", "bar");
+ 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", NULL);
- ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", NULL);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foobarx", "");
ec_tk_free(tk);
return ret;
--- /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 <ctype.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_tk.h>
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_option.h>
+#include <ecoli_tk_shlex.h>
+
+static int isend(char c)
+{
+ if (c == '\0' || c == '#' || c == '\n' || c == '\r')
+ return 1;
+ return 0;
+}
+
+/* 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)
+{
+ unsigned s = 0, d = 0, dstlen;
+ int quoted = 0;
+ char *dst;
+
+ 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;
+ }
+
+ /* 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++] = '"';
+ s += 2;
+ continue;
+ }
+ if (src[s] == '\\' && src[s+1] == '\\') {
+ dst[d++] = '\\';
+ s += 2;
+ continue;
+ }
+ if (src[s] == '"') {
+ s++;
+ quoted = !quoted;
+ continue;
+ }
+ dst[d++] = src[s++];
+ }
+
+ /* not enough room in dst buffer */
+ if (d >= dstlen) {
+ ec_free(dst);
+ return -EMSGSIZE;
+ }
+
+ /* end of string during quote */
+ if (quoted) {
+ ec_free(dst);
+ return -EINVAL;
+ }
+
+ dst[d++] = '\0';
+ *p_dst = dst;
+ return s;
+}
+
+static int safe_realloc(void *arg, size_t size)
+{
+ void **pptr = arg;
+ void *new_ptr = ec_realloc(*pptr, size);
+
+ if (new_ptr == NULL)
+ return -1;
+ *pptr = new_ptr;
+ return 0;
+}
+
+static char **tokenize(const char *str, int add_empty)
+{
+ char **table = NULL, *token;
+ unsigned i, count = 1, off = 0;
+ int ret;
+
+ if (safe_realloc(&table, sizeof(char *)) < 0)
+ return NULL;
+
+ table[0] = NULL;
+
+ while (1) {
+ ret = get_token(str + off, &token);
+ if (ret == -ENOENT)
+ break;
+ else if (ret < 0)
+ goto fail;
+
+ off += ret;
+ count++;
+ if (safe_realloc(&table, sizeof(char *) * count) < 0)
+ goto fail;
+ table[count - 2] = token;
+ table[count - 1] = NULL;
+ }
+
+ if (add_empty && (off != strlen(str) || strlen(str) == 0)) {
+ token = ec_strdup("");
+ if (token == NULL)
+ goto fail;
+
+ count++;
+ if (safe_realloc(&table, sizeof(char *) * count) < 0)
+ goto fail;
+ table[count - 2] = token;
+ table[count - 1] = NULL;
+ }
+
+ return table;
+
+ fail:
+ for (i = 0; i < count; i++)
+ ec_free(table[i]);
+ ec_free(table);
+ return NULL;
+}
+
+static struct ec_parsed_tk *ec_tk_shlex_parse(const struct ec_tk *gen_tk,
+ const char *str)
+{
+ 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;
+
+ parsed_tk = ec_parsed_tk_new(gen_tk);
+ 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;
+
+ 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;
+ }
+
+ /* check it was the last token */
+ if (*t != NULL)
+ goto fail;
+
+ if (tokens != NULL) {
+ for (t = &tokens[0]; *t != NULL; t++)
+ ec_free(*t);
+ ec_free(tokens);
+ tokens = NULL;
+ }
+
+ parsed_tk->str = ec_strdup(str);
+
+ return parsed_tk;
+
+ fail:
+ if (tokens != NULL) {
+ for (t = &tokens[0]; *t != NULL; t++)
+ ec_free(*t);
+ ec_free(tokens);
+ }
+ 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)
+{
+ 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;
+
+ tokens = tokenize(str, 1);
+ if (tokens == NULL)
+ goto fail;
+
+ printf("complete <%s>\n", str);
+ for (t = &tokens[0]; *t != NULL; t++)
+ printf(" token <%s> %p\n", *t, *t);
+
+ t = &tokens[0];
+
+ completed_tk = ec_completed_tk_new();
+ if (completed_tk == NULL)
+ return NULL;
+
+ 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);
+ if (child_parsed_tk == NULL)
+ goto fail;
+
+ if (strlen(child_parsed_tk->str) == 0)
+ t--;
+ else if (strlen(child_parsed_tk->str) != strlen(*t)) {
+ ec_parsed_tk_free(child_parsed_tk);
+ goto fail;
+ }
+
+ ec_parsed_tk_free(child_parsed_tk);
+ } else {
+ child_completed_tk = ec_tk_complete(tk->table[i], *t);
+ if (child_completed_tk == NULL) {
+ ec_completed_tk_free(completed_tk);
+ return NULL;
+ }
+ ec_completed_tk_merge(completed_tk, child_completed_tk);
+
+ child_parsed_tk = ec_tk_parse(tk->table[i], "");
+ if (child_parsed_tk == NULL)
+ break;
+ ec_parsed_tk_free(child_parsed_tk);
+ t--;
+ }
+ }
+
+ if (tokens != NULL) {
+ for (t = &tokens[0]; *t != NULL; t++)
+ ec_free(*t);
+ ec_free(tokens);
+ tokens = NULL;
+ }
+
+ ec_completed_tk_dump(stdout, completed_tk);
+
+ return completed_tk;
+
+ fail:
+ if (tokens != NULL) {
+ for (t = &tokens[0]; *t != NULL; t++)
+ ec_free(*t);
+ ec_free(tokens);
+ }
+ 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);
+}
+
+static struct ec_tk_ops ec_tk_shlex_ops = {
+ .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_shlex *tk = NULL;
+
+ tk = (struct ec_tk_shlex *)ec_tk_new(id, &ec_tk_shlex_ops, sizeof(*tk));
+ if (tk == 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);
+ }
+
+ va_end(ap);
+ return &tk->gen;
+
+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;
+}
+
+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);
+ 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);
+ 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);
+ 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);
+
+ return ret;
+}
+
+static struct ec_test ec_tk_shlex_test = {
+ .name = "tk_shlex",
+ .test = ec_tk_shlex_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_shlex_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_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);
+
+#endif
#include <string.h>
#include <ctype.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
#include <ecoli_malloc.h>
#include <ecoli_tk.h>
#include <ecoli_tk_space.h>
return ec_tk_new(id, &ec_tk_space_ops, sizeof(struct ec_tk_space));
}
+static int ec_tk_space_testcase(void)
+{
+ struct ec_tk *tk;
+ int ret = 0;
+
+ tk = ec_tk_space_new(NULL);
+ if (tk == 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, " ", " ");
+ 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);
+ ec_tk_free(tk);
+
+ /* test completion */
+ tk = ec_tk_space_new(NULL);
+ if (tk == NULL) {
+ 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", "");
+ ec_tk_free(tk);
+
+ return ret;
+}
+
+static struct ec_test ec_tk_space_test = {
+ .name = "tk_space",
+ .test = ec_tk_space_testcase,
+};
+
+EC_REGISTER_TEST(ec_tk_space_test);
struct ec_completed_tk_elt *completed_tk_elt;
size_t n;
+ 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 NULL;
-
- completed_tk = ec_completed_tk_new();
- if (completed_tk == NULL)
- return NULL;
+ return completed_tk;
+ if (tk->string[n] == '\0')
+ return completed_tk;
completed_tk_elt = ec_completed_tk_elt_new(gen_tk, tk->string + n,
tk->string);
struct ec_tk *tk;
int ret = 0;
- /* all inputs starting with foo should match */
tk = ec_tk_str_new(NULL, "foo");
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
ret |= EC_TEST_CHECK_TK_PARSE(tk, "foo", "foo");
ec_tk_free(tk);
- /* all inputs starting with foo should match */
tk = ec_tk_str_new(NULL, "Здравствуйте");
if (tk == NULL) {
ec_log(EC_LOG_ERR, "cannot create tk\n");
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", NULL);
+ ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", "");
ec_tk_free(tk);
return ret;
--- /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 <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <ecoli_tk_str.h>
+#include <ecoli_tk_seq.h>
+#include <ecoli_tk_space.h>
+#include <ecoli_tk_or.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 struct ec_completed_tk *c;
+ static const struct ec_completed_tk_elt *elt;
+
+ (void)s;
+
+ if (state == 0) {
+ char *start;
+
+ if (c != NULL)
+ ec_completed_tk_free(c);
+
+ start = strdup(rl_line_buffer);
+ if (start == NULL)
+ return NULL;
+ start[rl_point] = '\0';
+
+ c = ec_tk_complete(commands, start);
+ ec_completed_tk_iter_start(c);
+ }
+
+ elt = ec_completed_tk_iter_next(c);
+ if (elt == NULL)
+ return NULL;
+
+ return strdup(elt->full);
+}
+
+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
+ return rl_completion_matches(text, my_completion_entry);
+}
+
+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);
+ 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) {
+ line = readline("> ");
+ if (line == NULL)
+ break;
+
+ // XXX need a "parse_all"
+ p = ec_tk_parse(commands, line);
+ ec_parsed_tk_dump(stdout, p);
+ add_history(line);
+ ec_parsed_tk_free(p);
+ }
+
+
+ ec_tk_free(commands);
+ return 0;
+}