CFLAGS += -I.
srcs := ecoli_tk.c ecoli_malloc.c ecoli_log.c ecoli_strvec.c
+srcs += ecoli_keyval.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
ldflags-$(O)test = -rdynamic
exe-y-$(O)test = $(srcs) main.c
-ldflags-$(O)readline = -lreadline
+ldflags-$(O)readline = -lreadline -ltermcap
exe-y-$(O)readline = $(srcs) main-readline.c
include $(ECOLI)/mk/ecoli-post.mk
--- /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 <assert.h>
+#include <errno.h>
+
+#include <ecoli_malloc.h>
+#include <ecoli_log.h>
+#include <ecoli_test.h>
+#include <ecoli_keyval.h>
+
+struct ec_keyval {
+ size_t len;
+ struct ec_keyval_elt *vec;
+};
+
+struct ec_keyval_elt {
+ char *key;
+ void *val;
+ ec_keyval_elt_free_t free;
+};
+
+struct ec_keyval *ec_keyval_new(void)
+{
+ struct ec_keyval *keyval;
+
+ keyval = ec_calloc(1, sizeof(*keyval));
+ if (keyval == NULL)
+ return NULL;
+
+ return keyval;
+}
+
+static struct ec_keyval_elt *ec_keyval_lookup(const struct ec_keyval *keyval,
+ const char *key)
+{
+ struct ec_keyval_elt *elt;
+ size_t i;
+
+ if (keyval == NULL || keyval->vec == NULL)
+ return NULL;
+
+ for (i = 0; i < ec_keyval_len(keyval); i++) {
+ elt = &keyval->vec[i];
+ if (strcmp(elt->key, key) == 0)
+ return elt;
+ }
+
+ return NULL;
+}
+
+static void ec_keyval_elt_free(struct ec_keyval_elt *elt)
+{
+ if (elt == NULL)
+ return;
+
+ ec_free(elt->key);
+ if (elt->free != NULL)
+ elt->free(elt->val);
+}
+
+void *ec_keyval_get(const struct ec_keyval *keyval, const char *key)
+{
+ struct ec_keyval_elt *elt;
+
+ elt = ec_keyval_lookup(keyval, key);
+ if (elt == NULL)
+ return NULL;
+
+ return elt->val;
+}
+
+int ec_keyval_del(struct ec_keyval *keyval, const char *key)
+{
+ struct ec_keyval_elt *elt;
+ struct ec_keyval_elt *last = &keyval->vec[keyval->len - 1];
+
+ elt = ec_keyval_lookup(keyval, key);
+ if (elt == NULL)
+ return -ENOENT;
+
+ ec_keyval_elt_free(elt);
+
+ if (elt != last)
+ memcpy(elt, last, sizeof(*elt));
+
+ keyval->len--;
+
+ return 0;
+}
+
+int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
+ ec_keyval_elt_free_t free_cb)
+{
+ struct ec_keyval_elt *new_vec;
+ struct ec_keyval_elt *elt;
+
+ assert(keyval != NULL);
+ assert(key != NULL);
+
+ ec_keyval_del(keyval, key);
+
+ new_vec = ec_realloc(keyval->vec,
+ sizeof(*keyval->vec) * (keyval->len + 1));
+ if (new_vec == NULL)
+ return -ENOMEM;
+
+ keyval->vec = new_vec;
+
+ elt = &new_vec[keyval->len];
+ elt->key = ec_strdup(key);
+ if (elt->key == NULL)
+ return -ENOMEM;
+
+ elt->val = val;
+ elt->free = free_cb;
+ keyval->len++;
+
+ return 0;
+}
+
+void ec_keyval_free(struct ec_keyval *keyval)
+{
+ struct ec_keyval_elt *elt;
+ size_t i;
+
+ if (keyval == NULL)
+ return;
+
+ for (i = 0; i < ec_keyval_len(keyval); i++) {
+ elt = &keyval->vec[i];
+ ec_keyval_elt_free(elt);
+ }
+ ec_free(keyval->vec);
+ ec_free(keyval);
+}
+
+size_t ec_keyval_len(const struct ec_keyval *keyval)
+{
+ return keyval->len;
+}
+
+void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out)
+{
+ size_t i;
+
+ if (keyval == NULL) {
+ fprintf(out, "empty keyval\n");
+ return;
+ }
+
+ fprintf(out, "keyval:\n");
+ for (i = 0; i < ec_keyval_len(keyval); i++) {
+ fprintf(out, " %s: %p\n",
+ keyval->vec[i].key,
+ keyval->vec[i].val);
+ }
+}
+
+static int ec_keyval_testcase(void)
+{
+ struct ec_keyval *keyval;
+ char *val;
+
+ keyval = ec_keyval_new();
+ if (keyval == NULL) {
+ ec_log(EC_LOG_ERR, "cannot create keyval\n");
+ return -1;
+ }
+
+ EC_TEST_ASSERT(ec_keyval_len(keyval) == 0);
+ ec_keyval_set(keyval, "key1", "val1", NULL);
+ ec_keyval_set(keyval, "key2", ec_strdup("val2"), ec_free2);
+ EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
+
+ val = ec_keyval_get(keyval, "key1");
+ EC_TEST_ASSERT(val != NULL && !strcmp(val, "val1"));
+ val = ec_keyval_get(keyval, "key2");
+ EC_TEST_ASSERT(val != NULL && !strcmp(val, "val2"));
+ val = ec_keyval_get(keyval, "key3");
+ EC_TEST_ASSERT(val == NULL);
+
+ ec_keyval_set(keyval, "key1", "another_val1", NULL);
+ ec_keyval_set(keyval, "key2", ec_strdup("another_val2"), ec_free2);
+ EC_TEST_ASSERT(ec_keyval_len(keyval) == 2);
+
+ val = ec_keyval_get(keyval, "key1");
+ EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val1"));
+ val = ec_keyval_get(keyval, "key2");
+ EC_TEST_ASSERT(val != NULL && !strcmp(val, "another_val2"));
+
+ ec_keyval_del(keyval, "key1");
+ EC_TEST_ASSERT(ec_keyval_len(keyval) == 1);
+
+ ec_keyval_free(keyval);
+
+ return 0;
+}
+
+static struct ec_test ec_keyval_test = {
+ .name = "keyval",
+ .test = ec_keyval_testcase,
+};
+
+EC_REGISTER_TEST(ec_keyval_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_KEYVAL_
+#define ECOLI_KEYVAL_
+
+#include <stdio.h>
+
+typedef void (*ec_keyval_elt_free_t)(void *);
+
+struct ec_keyval;
+
+struct ec_keyval *ec_keyval_new(void);
+
+void *ec_keyval_get(const struct ec_keyval *keyval, const char *key);
+int ec_keyval_del(struct ec_keyval *keyval, const char *key);
+int ec_keyval_set(struct ec_keyval *keyval, const char *key, void *val,
+ ec_keyval_elt_free_t free_cb);
+
+void ec_keyval_free(struct ec_keyval *keyval);
+size_t ec_keyval_len(const struct ec_keyval *keyval);
+void ec_keyval_dump(const struct ec_keyval *keyval, FILE *out);
+
+#endif
return ec_malloc_handler.malloc(size, file, line);
}
+void *ec_malloc2(size_t size)
+{
+ return __ec_malloc(size, __FILE__, __LINE__);
+}
+
void __ec_free(void *ptr, const char *file, unsigned int line)
{
ec_malloc_handler.free(ptr, file, line);
}
+void ec_free2(void *ptr)
+{
+ __ec_free(ptr, __FILE__, __LINE__);
+}
+
void *__ec_calloc(size_t nmemb, size_t size, const char *file,
unsigned int line)
{
char *__ec_strndup(const char *s, size_t n, const char *file,
unsigned int line);
+/* XXX rename into ec_malloc, and change macro to uppercase */
+void *ec_malloc2(size_t size);
+void ec_free2(void *ptr);
+
/* we use macros here to ensure the file/line stay correct when
* debugging the standard malloc with valgrind */
for (i = 0; i < ec_strvec_len(strvec); i++)
fprintf(out, " %zd: %s\n", i, strvec->vec[i]);
}
+
+/* XXX test case */
}
}
- if (count != ec_completed_tk_count_match(c)) {
+ if (count != ec_completed_tk_count(c, EC_MATCH)) {
ec_log(EC_LOG_ERR,
"nb_completion (%d) does not match (%d)\n",
- count, ec_completed_tk_count_match(c));
+ count, ec_completed_tk_count(c, EC_MATCH));
ec_completed_tk_dump(stdout, c);
ret = -1;
}
/* 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_ERR(fmt, ...) \
+ ec_log(EC_LOG_ERR, "%s:%d: error: " fmt "\n", \
+ __FILE__, __LINE__, ##__VA_ARGS__); \
+
+#define EC_TEST_ASSERT(cond, args...) \
+ do { \
+ if (!(cond)) \
+ EC_TEST_ERR("assertion failure: " #cond); \
+ } while (0)
#define EC_TEST_CHECK_TK_PARSE(tk, input, expected...) ({ \
int ret_ = ec_test_check_tk_parse(tk, input, expected); \
if (ret_) \
- TEST_ERR(); \
+ EC_TEST_ERR("parse test failed"); \
ret_; \
})
#define EC_TEST_CHECK_TK_COMPLETE(tk, args...) ({ \
int ret_ = ec_test_check_tk_complete(tk, args); \
if (ret_) \
- TEST_ERR(); \
+ EC_TEST_ERR("complete test failed"); \
ret_; \
})
#include <ecoli_malloc.h>
#include <ecoli_strvec.h>
+#include <ecoli_keyval.h>
#include <ecoli_tk.h>
struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
size_t size)
{
struct ec_tk *tk = NULL;
+ char buf[256]; // XXX
assert(size >= sizeof(*tk));
if (tk == NULL)
goto fail;
+ TAILQ_INIT(&tk->children);
+ tk->ops = ops;
+
if (id != NULL) {
tk->id = ec_strdup(id);
if (tk->id == NULL)
goto fail;
}
- tk->ops = ops;
+ snprintf(buf, sizeof(buf), "<%s>", ops->typename);
+ tk->desc = ec_strdup(buf); // XXX ec_asprintf ?
+ if (tk->desc == NULL)
+ goto fail;
+
+ tk->attrs = ec_keyval_new();
+ if (tk->attrs == NULL)
+ goto fail;
return tk;
fail:
+ ec_free(tk->attrs);
+ ec_free(tk->desc);
+ ec_free(tk->id);
ec_tk_free(tk);
return NULL;
}
if (tk->ops != NULL && tk->ops->free_priv != NULL)
tk->ops->free_priv(tk);
ec_free(tk->id);
+ ec_free(tk->desc);
+ ec_free(tk->attrs);
ec_free(tk);
}
+struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id)
+{
+ struct ec_tk *child, *ret;
+ const char *tk_id = ec_tk_id(tk);
+
+ if (id != NULL && tk_id != NULL && !strcmp(tk_id, id))
+ return tk;
+
+ TAILQ_FOREACH(child, &tk->children, next) {
+ ret = ec_tk_find(child, id);
+ if (ret != NULL)
+ return ret;
+ }
+
+ return NULL;
+}
+
+struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk)
+{
+ return tk->attrs;
+}
+
+const char *ec_tk_id(const struct ec_tk *tk)
+{
+ return tk->id;
+}
+
+struct ec_tk *ec_tk_parent(const struct ec_tk *tk)
+{
+ return tk->parent;
+}
+
struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, const char *str)
{
struct ec_strvec *strvec = NULL;
typename = parsed_tk->tk->ops->typename;
}
+ /* XXX we only display the first token */
fprintf(out, "tk_type=%s, id=%s, s=<%s>\n", typename, id, s);
TAILQ_FOREACH(child, &parsed_tk->children, next)
return completed_tk->smallest_start;
}
-unsigned int ec_completed_tk_count_match(
- const struct ec_completed_tk *completed_tk)
+unsigned int ec_completed_tk_count(
+ const struct ec_completed_tk *completed_tk,
+ enum ec_completed_tk_filter_flags flags)
{
+ unsigned int count = 0;
+
if (completed_tk == NULL)
- return 0;
+ return count;
+
+ if (flags & EC_MATCH)
+ count += completed_tk->count_match;
+ if (flags & EC_NO_MATCH)
+ count += (completed_tk->count - completed_tk->count_match); //XXX
- return completed_tk->count_match;
+ return count;
}
struct ec_completed_tk_iter *
break;
if (iter->cur->add == NULL &&
- (iter->flags & ITER_NO_MATCH))
+ (iter->flags & EC_NO_MATCH))
break;
if (iter->cur->add != NULL &&
- (iter->flags & ITER_MATCH))
+ (iter->flags & EC_MATCH))
break;
} while (iter->cur != NULL);
{
ec_free(iter);
}
+
+const char *ec_tk_desc(const struct ec_tk *tk)
+{
+ if (tk->ops->desc != NULL)
+ return tk->ops->desc(tk);
+
+ return tk->desc;
+}
struct ec_tk;
struct ec_parsed_tk;
struct ec_strvec;
+struct ec_keyval;
typedef struct ec_parsed_tk *(*ec_tk_parse_t)(const struct ec_tk *tk,
const struct ec_strvec *strvec);
typedef struct ec_completed_tk *(*ec_tk_complete_t)(const struct ec_tk *tk,
const struct ec_strvec *strvec);
+typedef const char * (*ec_tk_desc_t)(const struct ec_tk *);
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_desc_t desc;
ec_tk_free_priv_t free_priv;
};
+TAILQ_HEAD(ec_tk_list, ec_tk);
+
struct ec_tk {
const struct ec_tk_ops *ops;
char *id;
+ char *desc;
+ struct ec_keyval *attrs;
+ /* XXX ensure parent and child are properly set in all nodes */
+ struct ec_tk *parent;
+
+ TAILQ_ENTRY(ec_tk) next;
+ struct ec_tk_list children;
};
struct ec_tk *ec_tk_new(const char *id, const struct ec_tk_ops *ops,
size_t priv_size);
void ec_tk_free(struct ec_tk *tk);
+/* XXX add more accessors */
+struct ec_keyval *ec_tk_attrs(const struct ec_tk *tk);
+struct ec_tk *ec_tk_parent(const struct ec_tk *tk);
+const char *ec_tk_id(const struct ec_tk *tk);
+
+struct ec_tk *ec_tk_find(struct ec_tk *tk, const char *id);
+
+/* XXX split this file ? */
TAILQ_HEAD(ec_parsed_tk_list, ec_parsed_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);
+struct ec_parsed_tk *ec_tk_parse(const struct ec_tk *tk, 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,
+struct ec_parsed_tk *ec_tk_parse_tokens(const struct ec_tk *tk,
const struct ec_strvec *strvec);
void ec_parsed_tk_add_child(struct ec_parsed_tk *parsed_tk,
* return a completed_tk object filled with elts
* return NULL on error (nomem?)
*/
-struct ec_completed_tk *ec_tk_complete(const struct ec_tk *token,
+struct ec_completed_tk *ec_tk_complete(const struct ec_tk *tk,
const char *str);
-struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *token,
+struct ec_completed_tk *ec_tk_complete_tokens(const struct ec_tk *tk,
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 *ec_completed_tk_smallest_start(
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,
+ EC_MATCH = 1,
+ EC_NO_MATCH = 2,
};
+unsigned int ec_completed_tk_count(
+ const struct ec_completed_tk *completed_tk,
+ enum ec_completed_tk_filter_flags flags);
+
struct ec_completed_tk_iter {
enum ec_completed_tk_filter_flags flags;
const struct ec_completed_tk *completed_tk;
struct ec_completed_tk_iter *iter);
void ec_completed_tk_iter_free(struct ec_completed_tk_iter *iter);
+
+
+const char *ec_tk_desc(const struct ec_tk *tk);
+
#endif
p = ec_tk_parse(tk, "0");
s = ec_parsed_tk_to_string(p);
- if (s == NULL) {
- TEST_ERR();
- } else {
- if (ec_tk_int_getval(tk, s) != 0)
- TEST_ERR();
- }
+ EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 0);
ec_parsed_tk_free(p);
p = ec_tk_parse(tk, "10");
s = ec_parsed_tk_to_string(p);
- if (s == NULL) {
- TEST_ERR();
- } else {
- if (ec_tk_int_getval(tk, s) != 10)
- TEST_ERR();
- }
+ EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 10);
ec_parsed_tk_free(p);
ec_tk_free(tk);
p = ec_tk_parse(tk, "10");
s = ec_parsed_tk_to_string(p);
- if (s == NULL) {
- TEST_ERR();
- } else {
- if (ec_tk_int_getval(tk, s) != 16)
- TEST_ERR();
- }
+ EC_TEST_ASSERT(s != NULL && ec_tk_int_getval(tk, s) == 16);
ec_parsed_tk_free(p);
ec_tk_free(tk);
table[tk->len] = child;
tk->len ++;
+ child->parent = gen_tk;
+ TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
return 0;
}
table[tk->len] = child;
tk->len ++;
+ child->parent = gen_tk;
+ TAILQ_INSERT_TAIL(&gen_tk->children, child, next);
+
return 0;
}
return completed_tk;
}
+static const char *ec_tk_str_desc(const struct ec_tk *gen_tk)
+{
+ struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
+
+ return tk->string;
+}
+
static void ec_tk_str_free_priv(struct ec_tk *gen_tk)
{
struct ec_tk_str *tk = (struct ec_tk_str *)gen_tk;
.typename = "str",
.parse = ec_tk_str_parse,
.complete = ec_tk_str_complete,
+ .desc = ec_tk_str_desc,
.free_priv = ec_tk_str_free_priv,
};
#include <readline/readline.h>
#include <readline/history.h>
+#include <ecoli_tk.h>
+#include <ecoli_keyval.h>
+
#include <ecoli_tk_str.h>
#include <ecoli_tk_seq.h>
#include <ecoli_tk_space.h>
return NULL;
ec_completed_tk_iter_free(iter);
- iter = ec_completed_tk_iter_new(c, ITER_MATCH);
+ iter = ec_completed_tk_iter_new(c, EC_MATCH);
if (iter == NULL)
return NULL;
}
return rl_completion_matches(text, my_completion_entry);
}
+/* this function builds the help string */
+static char *get_tk_help(const struct ec_tk *tk)
+{
+ const struct ec_tk *tk2;
+ char *help = NULL;
+ char *tk_help = NULL;
+
+ for (tk2 = tk; tk2 != NULL && tk_help == NULL; tk2 = ec_tk_parent(tk2))
+ tk_help = ec_keyval_get(ec_tk_attrs(tk2), "help");
+
+ if (tk_help == NULL)
+ tk_help = "";
+
+ if (asprintf(&help, "%-20s %s", ec_tk_desc(tk), tk_help) < 0)
+ return NULL;
+
+ return help;
+}
static int show_help(int ignore, int invoking_key)
{
+ const struct ec_completed_tk_elt *elt;
+ struct ec_completed_tk_iter *iter;
struct ec_completed_tk *c;
char *line;
+ unsigned int count, i;
+ char **helps = NULL;
(void)ignore;
(void)invoking_key;
- printf("\nhelp:\n");
line = strdup(rl_line_buffer);
if (line == NULL)
return 1;
free(line);
if (c == NULL)
return 1;
- ec_completed_tk_dump(stdout, c);
+ //ec_completed_tk_dump(stdout, c);
+
+ count = ec_completed_tk_count(c, EC_MATCH | EC_NO_MATCH);
+ helps = calloc(count + 1, sizeof(char *));
+ if (helps == NULL)
+ return 1;
+
+ iter = ec_completed_tk_iter_new(c, EC_MATCH | EC_NO_MATCH);
+ if (iter == NULL)
+ goto fail;
+
+ /* strangely, rl_display_match_list() expects first index at 1 */
+ for (i = 1, elt = ec_completed_tk_iter_next(iter);
+ i <= count && elt != NULL;
+ i++, elt = ec_completed_tk_iter_next(iter)) {
+ helps[i] = get_tk_help(elt->tk);
+ }
+
ec_completed_tk_free(c);
+ rl_display_match_list(helps, count, 1000);
+
rl_forced_update_display();
return 0;
+
+fail:
+ free(helps);
+ // free helps[n] XXX
+ return 1;
}
-int main(void)
+static int create_commands(void)
{
- struct ec_parsed_tk *p;
-// const char *name;
- char *line;
+ struct ec_tk *cmdlist = NULL, *cmd = NULL;
- rl_bind_key('?', show_help);
+ cmdlist = ec_tk_or_new(NULL);
+ if (cmdlist == NULL)
+ goto fail;
- commands = ec_tk_shlex_new(NULL,
- ec_tk_seq_new_list(NULL,
- ec_tk_str_new(NULL, "hello"),
+ cmd = ec_tk_seq_new_list(NULL,
+ ec_tk_str_new(NULL, "hello"),
+ ec_tk_or_new_list(NULL,
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_int_new("int", 0, 10, 10),
EC_TK_ENDLIST
- )
+ ),
+ EC_TK_ENDLIST
+ );
+ ec_keyval_set(ec_tk_attrs(cmd), "help", "say hello to someone", NULL);
+ ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "name")),
+ "help", "the name of the person", NULL);
+ ec_keyval_set(ec_tk_attrs(ec_tk_find(cmd, "int")),
+ "help", "an integer", NULL);
+ if (ec_tk_or_add(cmdlist, cmd) < 0)
+ goto fail;
+
+ cmd = ec_tk_seq_new_list(NULL,
+ ec_tk_str_new(NULL, "bye"),
+ EC_TK_ENDLIST
);
- if (commands == NULL) {
- printf("cannot create token\n");
+ ec_keyval_set(ec_tk_attrs(cmd), "help", "say bye to someone", NULL);
+ if (ec_tk_or_add(cmdlist, cmd) < 0)
+ goto fail;
+
+ commands = ec_tk_shlex_new(NULL, cmdlist);
+ if (commands == NULL)
+ goto fail;
+
+ return 0;
+
+ fail:
+ fprintf(stderr, "cannot initialize tokens\n");
+ ec_tk_free(cmd);
+ ec_tk_free(cmdlist);
+ return -1;
+}
+
+int main(void)
+{
+ struct ec_parsed_tk *p;
+// const char *name;
+ char *line;
+
+
+ if (create_commands() < 0)
return 1;
- }
+ rl_bind_key('?', show_help);
rl_attempted_completion_function = my_attempted_completion;
while (1) {
ec_tk_free(commands);
return 0;
+
}