static char *get_node_help(const struct ec_comp_item *item)
{
const struct ec_comp_group *grp;
- const struct ec_pnode *state;
+ const struct ec_pnode *pstate;
const struct ec_node *node;
char *help = NULL;
const char *node_help = NULL;
- const char *node_desc = NULL;
+ char *node_desc = NULL;
grp = ec_comp_item_get_grp(item);
- for (state = ec_comp_group_get_state(grp); state != NULL;
- state = ec_pnode_get_parent(state)) {
- node = ec_pnode_get_node(state);
+ for (pstate = ec_comp_group_get_pstate(grp); pstate != NULL;
+ pstate = ec_pnode_get_parent(pstate)) {
+ node = ec_pnode_get_node(pstate);
if (node_help == NULL)
node_help = ec_dict_get(ec_node_attrs(node), "help");
if (node_desc == NULL)
if (asprintf(&help, "%-20s %s", node_desc, node_help) < 0)
return NULL;
+ ec_free(node_desc);
+
return help;
}
struct ec_comp *c = NULL;
struct ec_pnode *p = NULL;
char *line = NULL;
- unsigned int count;
+ size_t count;
char **helps = NULL;
int match = 0;
int cols;
*
* The library also provides helpers to create a an interactive command
* line based on @ref editline library, and a @ref yaml parser for
- * grammar trees.
+ * grammar graphs.
*/
#ifndef ECOLI_
* @defgroup complete Complete
* @{
*
- * @brief Complete string input using a grammar tree
+ * @brief Complete string input using a grammar graph.
*
- * This file provide helpers to list and manipulate the possible
+ * This file provides helpers to list and manipulate the possible
* completions for a given input.
*
* Use @ec_complete_strvec() to complete a vector of strings when
* the input is already split into several tokens. You can use
* @ec_complete() if you know that the size of the vector is
- * 1. This is common if you grammar tree has a lexer that will tokenize
- * the input.
+ * 1. This is common if your grammar graph includes a lexer that
+ * will tokenize the input.
*
* These 2 functions return a pointer to an @ec_comp structure, that
* lists the possible completions. The completions are grouped into
*/
enum ec_comp_type {
EC_COMP_UNKNOWN = 0x1,
- EC_COMP_FULL = 0x2,
- EC_COMP_PARTIAL = 0x4,
+ EC_COMP_FULL = 0x2, /**< The item is fully completed. */
+ EC_COMP_PARTIAL = 0x4, /**< The item is partially completed. */
EC_COMP_ALL = 0x7,
};
* vector that only contains 1 element, the input string. Using this
* function is often more convenient if you get your input from a
* buffer, because you won't have to create a vector. Usually, it means
- * you have a lexer in your grammar tree that will tokenize the input.
+ * you have a lexer in your grammar graph that will tokenize the input.
*
* See @ec_complete_strvec() for more details.
*
* @param node
- * The grammar tree.
+ * The grammar graph.
* @param str
* The input string.
* @return
* vector should be ["cat", "x"].
*
* To get the completion list, the engine parses the beginning of the
- * input using the grammar tree. The resulting parsing tree is saved and
+ * input using the grammar graph. The resulting parsing tree is saved and
* attached to each completion group.
*
* The result is a @ec_comp structure pointer, which contains several
* groups of completion items.
*
* @param node
- * The grammar tree.
- * @param str
- * The input string.
+ * The grammar graph.
+ * @param strvec
+ * The input string vector.
* @return
* A pointer to the completion list on success, or NULL
* on error (errno is set).
const struct ec_strvec *strvec);
/**
- * Get the list of completions when called from a node completion.
+ * Get the list of completions of a child node.
*
- * This function is to be used by ecoli nodes.
+ * This function is to be used by intermediate ecoli nodes, i.e. nodes
+ * which have children (ex: ec_node_seq, ec_node_or, ...). It fills an
+ * existing @ec_comp structure, passed by the parent node.
*
+ * @param node
+ * The grammar graph.
+ * @param comp
+ * The current completion list to be filled.
+ * @param strvec
+ * The input string vector.
+ * @return
+ * 0 on success, or -1 on error (errno is set).
*/
int ec_complete_child(const struct ec_node *node,
struct ec_comp *comp,
const struct ec_strvec *strvec);
/**
- * Create a completion object (list of completion items).
- *
+ * Create an empty completion object (list of completion items).
*
+ * @return
+ * A pointer to the completion structure on success, or NULL on error
+ * (errno is set).
*/
-struct ec_comp *ec_comp(struct ec_pnode *state);
+struct ec_comp *ec_comp(void);
/**
* Free a completion object and all its items.
*
- *
+ * @param comp
+ * The pointer to the completion structure to free.
*/
void ec_comp_free(struct ec_comp *comp);
/**
+ * Dump the content of a completions list.
*
- *
- *
+ * @param out
+ * The stream where the dump is sent.
+ * @param comp
+ * The pointer to the completion list structure.
*/
-void ec_comp_dump(FILE *out,
- const struct ec_comp *comp);
+void ec_comp_dump(FILE *out, const struct ec_comp *comp);
/**
- * Merge items contained in 'from' into 'to'
+ * Merge items contained in 'from' into 'to'.
*
* The 'from' comp struct is freed.
+ *
+ * @param to
+ * The destination completion list.
+ * @param from
+ * The source completion list.
+ * @return
+ * 0 on success, or -1 on error (errno is set).
*/
-int ec_comp_merge(struct ec_comp *to,
- struct ec_comp *from);
+int ec_comp_merge(struct ec_comp *to, struct ec_comp *from);
/**
- * Get current completion state.
+ * Get current parsing state of completion.
+ *
+ * This function can be called by a node during the completion process.
*
+ * When processing the list of completions for a given input,
+ * an incomplete parsing tree is generated before the completion
+ * callback is invoked. A node may use it if the completions list
+ * depend on what was previously parsed. For instance, the "once"
+ * node checks in the parsing tree if the node is already parsed.
+ * In this case, no completion is issued.
+ *
+ * @param comp
+ * The current completion list.
+ * @return
+ * The current parsing state (cannot be NULL).
*/
-struct ec_pnode *ec_comp_get_state(const struct ec_comp *comp);
+struct ec_pnode *ec_comp_get_cur_pstate(const struct ec_comp *comp);
/**
* Get current completion group.
*
+ * This function can be called by a node during the completion process.
+ *
+ * A completion group is a list of completion items that share the same
+ * parsing state and are issued by the same grammar node. The completion
+ * group is created when the first item is added, thus this function
+ * returns NULL if no item has been added in the group.
+ *
+ * @param comp
+ * The current completion list.
+ * @return
+ * The current completion group (can be NULL).
*/
-struct ec_comp_group *ec_comp_get_group(const struct ec_comp *comp);
+struct ec_comp_group *ec_comp_get_cur_group(const struct ec_comp *comp);
/**
- * Get completion group attributes.
+ * Get completion attributes.
+ *
+ * Arbitrary attributes (stored in a dictionary) can be attached to a
+ * completion state.
*
+ * @param comp
+ * The completion list.
+ * @return
+ * The associated attributes.
*/
struct ec_dict *ec_comp_get_attrs(const struct ec_comp *comp);
-/* shortcut for ec_comp_item() + ec_comp_item_add() */
-int ec_comp_add_item(struct ec_comp *comp,
- const struct ec_node *node,
- struct ec_comp_item **p_item,
- enum ec_comp_type type,
- const char *start, const char *full);
-
/**
+ * Add an item in competion list.
+ *
+ * This function can be called by a node during the completion process,
+ * for each completion item that should be added to the list. This is
+ * typically done in terminal nodes, like ec_node_str or ec_node_file.
+ *
+ * Create a new completion item, and add it into the completion
+ * list. A completion item has a type, which can be:
+ * - EC_COMP_FULL: the item is fully completed (common case, used
+ * for instance in the str node)
+ * - EC_COMP_PARTIAL: the item is only partially completed (this
+ * happens rarely, for instance in the file node, when a completion
+ * goes up to the next slash).
+ * - EC_COMP_UNKNOWN: the node detects a valid token, but does not
+ * how to complete it (ex: the int node).
*
+ * @param comp
+ * The current completion list.
+ * @param node
+ * The node issuing the completion item.
+ * @param type
+ * The type of the item.
+ * @param start
+ * The incomplete string being completed.
+ * @param full
+ * The string fully completed.
+ * @return
+ * The item that was added in the list on success, or NULL
+ * on error. Note: don't free the returned value, as it is referenced
+ * by the completion list. It is returned in case it needs to be
+ * modified, for instance with ec_comp_item_set_display().
*/
-int ec_comp_item_set_str(struct ec_comp_item *item,
- const char *str);
+struct ec_comp_item *ec_comp_add_item(struct ec_comp *comp,
+ const struct ec_node *node, enum ec_comp_type type,
+ const char *start, const char *full);
/**
* Get the string value of a completion item.
*
- *
+ * @param item
+ * The completion item..
+ * @return
+ * The completion string of this item.
*/
const char *
ec_comp_item_get_str(const struct ec_comp_item *item);
/**
* Get the display string value of a completion item.
*
+ * The display string corresponds to what is displayed when
+ * listing the possible completions.
*
+ * @param item
+ * The completion item..
+ * @return
+ * The display string of this item.
*/
const char *
ec_comp_item_get_display(const struct ec_comp_item *item);
/**
* Get the completion string value of a completion item.
*
+ * The completion string corresponds to what should be added to
+ * the incomplete token to complete it.
*
+ * @param item
+ * The completion item.
+ * @return
+ * The completion string of this item.
*/
const char *
ec_comp_item_get_completion(const struct ec_comp_item *item);
/**
* Get the group of a completion item.
*
+ * The completion group corresponds to the list of items that share
+ * the same parsing state and are issued by the same node.
*
+ * @param item
+ * The completion item.
+ * @return
+ * The completion group of this item.
*/
const struct ec_comp_group *
ec_comp_item_get_grp(const struct ec_comp_item *item);
/**
* Get the type of a completion item.
*
- *
+ * @param item
+ * The completion item.
+ * @return
+ * The type of this item (EC_COMP_UNKNOWN, EC_COMP_PARTIAL or
+ * EC_COMP_FULL).
*/
enum ec_comp_type
ec_comp_item_get_type(const struct ec_comp_item *item);
/**
* Get the node associated to a completion item.
*
- *
+ * @param item
+ * The completion item.
+ * @return
+ * The node that issued the completion item.
*/
const struct ec_node *
ec_comp_item_get_node(const struct ec_comp_item *item);
+/**
+ * Set the completion item string.
+ *
+ * Some intermediate nodes like sh_lex modify the completion string of
+ * items generated by their children, for instance to add quotes.
+ *
+ * @param item
+ * The completion item to update.
+ * @param str
+ * The new item string.
+ * @return
+ * 0 on success, or -1 on error (errno is set).
+ */
+int ec_comp_item_set_str(struct ec_comp_item *item, const char *str);
+
/**
* Set the display value of an item.
*
+ * The display string corresponds to what is displayed when listing the
+ * possible completions. Some nodes like ec_node_file change the default
+ * value display the base name instead of the full path.
*
+ * @param item
+ * The completion item to update.
+ * @param str
+ * The new display string.
+ * @return
+ * 0 on success, or -1 on error (errno is set).
*/
int ec_comp_item_set_display(struct ec_comp_item *item,
const char *display);
/**
* Set the completion value of an item.
*
+ * The completion string corresponds to what should be added to
+ * the incomplete token to complete it. Some nodes like sh_lex
+ * modify it in the items generated by their children, for instance
+ * to add a terminating quote.
*
+ * @param item
+ * The completion item to update.
+ * @param str
+ * The new completion string.
+ * @return
+ * 0 on success, or -1 on error (errno is set).
*/
int ec_comp_item_set_completion(struct ec_comp_item *item,
const char *completion);
/**
* Get the completion group node.
*
- *
+ * @param grp
+ * The completion group.
*/
const struct ec_node *
ec_comp_group_get_node(const struct ec_comp_group *grp);
/**
- * Get the completion group parsed state.
+ * Get the completion group parsing state.
*
+ * All items of a completion group are issued by the same node.
+ * This function returns a pointer to this node.
*
+ * @param grp
+ * The completion group.
+ * @return
+ * The node that issued the completion group.
*/
const struct ec_pnode *
-ec_comp_group_get_state(const struct ec_comp_group *grp);
+ec_comp_group_get_pstate(const struct ec_comp_group *grp);
/**
* Get the completion group attributes.
*
+ * The parsing state contains the parsing result of the input data
+ * preceding the completion. All items of a completion group share the
+ * same parsing state.
*
+ * @param grp
+ * The completion group.
+ * @return
+ * The parsing state of the completion group.
*/
const struct ec_dict *
ec_comp_group_get_attrs(const struct ec_comp_group *grp);
/**
+ * Default node completion callback
*
+ * This function is the default completion method for nodes that do
+ * not define one.
*
+ * This helper adds a completion item of type EC_COMP_UNKNOWN if the
+ * input string vector contains one element, to indicate that everything
+ * before the last element of the string vector has been parsed
+ * successfully, but the node doesn't know how to complete the last
+ * element.
*
+ * @param node
+ * The completion node.
+ * @param comp
+ * The completion list to update.
+ * @param strvec
+ * The input string vector.
+ * @return
+ * 0 on succes, or -1 on error (errno is set).
*/
int
-ec_complete_unknown(const struct ec_node *gen_node,
+ec_complete_unknown(const struct ec_node *node,
struct ec_comp *comp,
const struct ec_strvec *strvec);
/**
+ * Get the number of completion items.
*
+ * Return the number of completion items that match a given type in a
+ * completion list.
*
- *
- */
-unsigned int ec_comp_count(
- const struct ec_comp *comp,
- enum ec_comp_type flags);
-
-/**
- *
- *
- *
+ * @param comp
+ * The completion list.
+ * @param type
+ * A logical OR of flags among EC_COMP_UNKNOWN, EC_COMP_PARTIAL and
+ * EC_COMP_FULL, to select the type to match.
+ * @return
+ * The number of matching items.
*/
+size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type);
/**
* Get the first completion item matching the type.
*/
/**
- * @defgroup grammar_tree Grammar Tree
+ * @defgroup grammar_graph Grammar Graph
* @{
*
* @brief Libecoli grammar nodes.
*
* A node can have child nodes. For instance, a sequence node
* ec_node_seq(ec_node_str("foo"), ec_node_str("bar")) will match
- * ["foo", "bar"].
+ * a sequence: ["foo", "bar"].
+ *
+ * Note: at some places in the documentation and the code, we may talk
+ * about the grammar tree, but as loops are allowed, we should instead
+ * talk about grammar graph.
*/
#ifndef ECOLI_NODE_
#include <sys/queue.h>
#include <sys/types.h>
+#include <stdbool.h>
#include <stdio.h>
/**
static void __attribute__((constructor, used)) \
ec_node_init_##t(void) \
{ \
- if (ec_node_type_register(&t) < 0) \
+ if (ec_node_type_register(&t, 0) < 0) \
+ fprintf(stderr, \
+ "cannot register node type %s\n", \
+ t.name); \
+ }
+
+/**
+ * Register a node type at library load, overriding previous registration.
+ *
+ * The node type is registered in a function that has the the
+ * constructor attribute: the function is called at library load.
+ *
+ * Be careful when using this macro, as the last type with a given name
+ * is the one that is actually registered. The call order may be hard to
+ * predict, especially within the same binary.
+ *
+ * @param t
+ * The name of the ec_node_type structure variable.
+ */
+#define EC_NODE_TYPE_REGISTER_OVERRIDE(t) \
+ static void ec_node_init_##t(void); \
+ static void __attribute__((constructor, used)) \
+ ec_node_init_##t(void) \
+ { \
+ if (ec_node_type_register(&t, 1) < 0) \
fprintf(stderr, \
"cannot register node type %s\n", \
t.name); \
* The function pointer is not called directly, the helper
* @ec_node_set_config() should be used instead.
*
+ * The configuration passed to this function pointer is valid,
+ * i.e. @ec_config_validate() returned 0 on it.
+ *
* @param node
* The node to configure.
* @param config
const struct ec_config *config);
/**
- * Parse a string vector using the given grammar tree.
+ * Parse a string vector using the given grammar graph.
*
* The function pointer is not called directly, the helpers @ec_parse(),
* ec_parse_strvec() or ec_parse_child() should be used instead.
*
* The implementation of this method for a node that manages children
- * will call ec_parse_child(child, state, child_strvec).
+ * will call ec_parse_child(child, pstate, child_strvec).
*
* @param node
- * The root node of the grammar tree.
- * @param state
+ * The grammar graph.
+ * @param pstate
* A pointer to the leaf being parsed in the parsing tree. It can be
* used by a node to retrieve information from the current parsing
- * tree. To get the root of the tree, @ec_pnode_get_root(state) should
+ * tree. To get the root of the tree, @ec_pnode_get_root(pstate) should
* be used.
* @param strvec
* The string vector to be parsed.
* vector. On error, a negative value is returned and errno is set.
*/
typedef int (*ec_parse_t)(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec);
/**
- * Get completion items using the given grammar tree.
+ * Get completion items using the given grammar graph.
*
- * The function pointer is not called directly, the helpers @ec_complete(),
- * ec_complete_strvec() or ec_complete_child() should be used instead.
+ * The function pointer should not be called directly, the helpers
+ * @ec_complete(), ec_complete_strvec() or ec_complete_child() should be
+ * used instead.
*
* This function completes the last element of the string vector.
* For instance, node.type->complete(node, comp, ["ls"]) will
* list all commands that starts with "ls", while
* node.type->complete(node, comp, ["ls", ""]) will list all
* possible values for the next argument.
-
- * The implementation of this function is supposed to either:
+ *
+ * The implementation of this function in the node is supposed
+ * to either:
* - call @ec_comp_add_item(node, comp, ...) for each completion item
- * that should be added to the list. This is done in terminal nodes,
- * for example in ec_node_str or ec_node_file.
+ * that should be added to the list. This is typically done in
+ * terminal nodes, for example in ec_node_str or ec_node_file.
* - call @ec_complete_child(child, comp, child_strvec) to let
* the children nodes add their own completion. This is the
* case of ec_node_or which trivially calls @ec_complete_child()
* complete the last element.
*
* @param node
- * The root node of the grammar tree.
+ * The root node of the grammar graph.
* @param comp
* The current list of completion items, to be filled by the
* node.type->complete() method.
const struct ec_strvec *strvec);
/**
+ * Get the short description of a grammar node.
+ *
+ * This function pointer should not be called directly. The
+ * @ec_node_desc() helper should be used instead.
+ *
+ * This callback is typically used when building a help string for a
+ * grammar graph. It is used in ecoli editline interface to generate
+ * contextual help like this (first column):
+ * <int> An integer.
+ * foo The foo string.
+ * bar The bar string.
+ *
+ * If this callback is set to NULL in the node type, the
+ * default behavior is to return the node type name inside <>, for
+ * instance "<int>". The string node type implements this method to
+ * return the string value. An integer node could implement it
+ * to return its range (ex: "1..10").
+ *
+ * The returned value is a pointer that must be freed by
+ * the caller with @ec_free().
+ *
+ * On error, NULL is returned and errno is set.
+ */
+typedef char * (*ec_node_desc_t)(const struct ec_node *);
+
+/**
+ * Initialize the node private area.
+ *
+ * This function pointer should not be called directly. The @ec_node()
+ * and ec_node_from_type() helpers, that allocate new nodes, should be
+ * used instead.
+ *
+ * If not NULL, this function is called when a node is instanciated, to
+ * initialize the private area of a node. In any case, the private area
+ * is first zeroed.
*
+ * On success, 0 is returned. On error, a negative value is returned and
+ * errno is set.
*/
-typedef const char * (*ec_node_desc_t)(const struct ec_node *);
typedef int (*ec_node_init_priv_t)(struct ec_node *);
+
+/**
+ * Free the node private area.
+ *
+ * This function pointer should not be called directly. The
+ * @ec_node_free() helper should be used instead.
+ *
+ * When a node is deleted, this function is called to free the resources
+ * referenced in the node private area.
+ */
typedef void (*ec_node_free_priv_t)(struct ec_node *);
+
+/**
+ * Count the number of node children.
+ *
+ * This function pointer should not be called directly. The
+ * @ec_node_get_children_count() helper should be used instead.
+ *
+ * Some grammar nodes like seq, or, many, (...), reference children
+ * nodes in the grammar graph. This function returns the number of
+ * children.
+ */
typedef size_t (*ec_node_get_children_count_t)(const struct ec_node *);
+
+/**
+ * Count the number of node children.
+ *
+ * This function pointer should not be called directly. The
+ * @ec_node_get_child() helper should be used instead.
+ *
+ * Some grammar nodes like seq, or, many, (...), reference children
+ * nodes in the grammar graph. This function sets the i-th child (with i
+ * lower than the return value of ec_node_get_children_count()) in the
+ * child pointer. It also returns the number of references to the child
+ * owned by the parent. This information is used by the algorithm that
+ * frees a grammar graph taking care of loops.
+ *
+ * On success, 0 is returned. On error, a negative value is returned and
+ * errno is set.
+ */
typedef int (*ec_node_get_child_t)(const struct ec_node *,
size_t i, struct ec_node **child, unsigned int *refs);
/**
- * A structure describing a node type.
+ * A structure describing a grammar node type.
+ *
+ * It is usually defined as a static const structure in the code
+ * defining a new grammar node type. Examples can be found in
+ * ecoli_node_<type>.c files.
*/
struct ec_node_type {
TAILQ_ENTRY(ec_node_type) next; /**< Next in list. */
/** Configuration schema array, must be terminated by a sentinel
* (.type = EC_CONFIG_TYPE_NONE). */
const struct ec_config_schema *schema;
- ec_node_set_config_t set_config; /* validate/ack a config change */
- ec_parse_t parse;
- ec_complete_t complete;
- ec_node_desc_t desc;
- size_t size;
- ec_node_init_priv_t init_priv;
- ec_node_free_priv_t free_priv;
+ size_t size; /**< Size of private area */
+ ec_node_set_config_t set_config; /**< Validate and set configuration. */
+ ec_parse_t parse; /**< Parse a string vector. */
+ ec_complete_t complete; /**< Get completion items. */
+ ec_node_desc_t desc; /**< Get short description. */
+ ec_node_init_priv_t init_priv; /**< Initialize private area. */
+ ec_node_free_priv_t free_priv; /**< Free node resourses. */
+ /** Get children count. */
ec_node_get_children_count_t get_children_count;
- ec_node_get_child_t get_child;
+ ec_node_get_child_t get_child; /**< Get the i-th child. */
};
/**
* Register a node type.
*
+ * The name of the type being registered is a uniq identifier. However,
+ * it is possible to force the registration of a type with an existing
+ * name by setting "override" to true. Note that the initial type is not
+ * removed from the list, instead the new one is added before in the
+ * list.
+ *
* @param type
- * A pointer to a ec_test structure describing the test
- * to be registered.
+ * The node type to be registered.
+ * @param override
+ * Allow the registration of an existing type.
* @return
* 0 on success, negative value on error.
*/
-int ec_node_type_register(struct ec_node_type *type);
+int ec_node_type_register(struct ec_node_type *type, bool override);
/**
- * Lookup node type by name
+ * Lookup node type by name.
*
* @param name
* The name of the node type to search.
* @return
- * The node type if found, or NULL on error.
+ * The (read-only) node type if found, or NULL on error.
*/
const struct ec_node_type *ec_node_type_lookup(const char *name);
/**
- * Dump registered log types
+ * Dump registered log types.
+ *
+ * @param out
+ * The stream where the dump is sent.
*/
void ec_node_type_dump(FILE *out);
/**
* Get the config schema of a node type.
+ *
+ * @param type
+ * The node type.
+ * @return
+ * The (read-only) config schema of the node type, or NULL
+ * if the node type has no config schema.
*/
const struct ec_config_schema *
ec_node_type_schema(const struct ec_node_type *type);
/**
* Get the name of a node type.
+ *
+ * @param type
+ * The node type.
+ * @return
+ * The (read-only) name of the node type.
*/
const char *
ec_node_type_name(const struct ec_node_type *type);
-enum ec_node_free_state {
- EC_NODE_FREE_STATE_NONE,
- EC_NODE_FREE_STATE_TRAVERSED,
- EC_NODE_FREE_STATE_FREEABLE,
- EC_NODE_FREE_STATE_NOT_FREEABLE,
- EC_NODE_FREE_STATE_FREEING,
-};
-
-/* create a new node when the type is known, typically called from the node
+/**
+ * create a new node when the type is known, typically called from the node
* code */
struct ec_node *ec_node_from_type(const struct ec_node_type *type, const char *id);
-/* create a new node */
+/**
+ * Create a new node from type name.
+ */
struct ec_node *ec_node(const char *typename, const char *id);
struct ec_node *ec_node_clone(struct ec_node *node);
const struct ec_node_type *ec_node_type(const struct ec_node *node);
struct ec_dict *ec_node_attrs(const struct ec_node *node);
const char *ec_node_id(const struct ec_node *node);
-const char *ec_node_desc(const struct ec_node *node);
+
+char *ec_node_desc(const struct ec_node *node);
void ec_node_dump(FILE *out, const struct ec_node *node);
struct ec_node *ec_node_find(struct ec_node *node, const char *id);
/* callback invoked by parse() or complete() to build the dynamic node
* the behavior of the node can depend on what is already parsed */
typedef struct ec_node *(*ec_node_dynamic_build_t)(
- struct ec_pnode *state, void *opaque);
+ struct ec_pnode *pstate, void *opaque);
struct ec_node *ec_node_dynamic(const char *id, ec_node_dynamic_build_t build,
void *opaque);
* @defgroup parse Parse
* @{
*
- * @brief Create parse tree from string input and grammar tree
+ * @brief Create parse tree from string input and grammar graph
*
* Node parse API.
*
/* internal: used by nodes
*
- * state is the current parse tree, which is built piece by piece while
+ * pstate is the current parse tree, which is built piece by piece while
* parsing the node tree: ec_parse_child() creates a new child in
* this state parse tree, and calls the parse() method for the child
- * node, with state pointing to this new child. If it does not match,
+ * node, with pstate pointing to this new child. If it does not match,
* the child is removed in the state, else it is kept, with its
* possible descendants.
*
* -1 on error, and errno is set
*/
int ec_parse_child(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec);
/**
* Iterate among parse tree
*
* Use it with:
- * for (iter = state; iter != NULL; iter = EC_PNODE_ITER_NEXT(state, iter, 1))
+ * for (iter = pnode; iter != NULL; iter = EC_PNODE_ITER_NEXT(pnode, iter, 1))
*/
struct ec_pnode *__ec_pnode_iter_next(const struct ec_pnode *root,
struct ec_pnode *pnode, bool iter_children);
edit_dep = dependency('libedit', method: 'pkg-config')
yaml_dep = dependency('yaml-0.1', method: 'pkg-config')
-# XXX if debug
-add_global_arguments('-Werror', language : 'c')
+add_global_arguments('-Wmissing-prototypes', language : 'c')
inc = include_directories('include')
priv_inc = include_directories('src')
TAILQ_ENTRY(ec_comp_item) next;
enum ec_comp_type type;
struct ec_comp_group *grp;
- char *start; /* the initial token */
- char *full; /* the full token after completion */
- char *completion; /* chars that are added, NULL if not applicable */
- char *display; /* what should be displayed by help/completers */
+ char *start; /**< The initial token */
+ char *full; /**< The full token after completion */
+ char *completion; /**< Chars that are added, NULL if not applicable */
+ char *display; /**< What should be displayed by help/completers */
struct ec_dict *attrs;
};
struct ec_comp_group {
/* XXX counts ? */
TAILQ_ENTRY(ec_comp_group) next;
+ const struct ec_comp *comp;
const struct ec_node *node;
struct ec_comp_item_list items;
- struct ec_pnode *state;
+ struct ec_pnode *pstate;
struct ec_dict *attrs;
};
TAILQ_HEAD(ec_comp_group_list, ec_comp_group);
struct ec_comp {
- unsigned count;
- unsigned count_full;
- unsigned count_partial;
- unsigned count_unknown;
- struct ec_pnode *cur_state;
+ size_t count;
+ size_t count_full;
+ size_t count_partial;
+ size_t count_unknown;
+ struct ec_pnode *cur_pstate;
struct ec_comp_group *cur_group;
struct ec_comp_group_list groups;
struct ec_dict *attrs;
};
-struct ec_comp *ec_comp(struct ec_pnode *state)
+struct ec_comp *ec_comp(void)
{
struct ec_comp *comp = NULL;
TAILQ_INIT(&comp->groups);
- comp->cur_state = state;
-
return comp;
fail:
return NULL;
}
-struct ec_pnode *ec_comp_get_state(const struct ec_comp *comp)
+struct ec_pnode *ec_comp_get_cur_pstate(const struct ec_comp *comp)
{
- return comp->cur_state;
+ return comp->cur_pstate;
}
-struct ec_comp_group *ec_comp_get_group(const struct ec_comp *comp)
+struct ec_comp_group *ec_comp_get_cur_group(const struct ec_comp *comp)
{
return comp->cur_group;
}
struct ec_comp *comp,
const struct ec_strvec *strvec)
{
- struct ec_pnode *child_state, *cur_state;
+ struct ec_pnode *child_pstate, *cur_pstate;
struct ec_comp_group *cur_group;
+ ec_complete_t complete_cb;
int ret;
- //XXX call ec_complete_unknown() instead, as
- //described in API doc.
- if (ec_node_type(node)->complete == NULL) {
- errno = ENOTSUP;
- return -1;
- }
+ /* get the complete method, falling back to ec_complete_unknown() */
+ complete_cb = ec_node_type(node)->complete;
+ if (complete_cb == NULL)
+ complete_cb = ec_complete_unknown;
/* save previous parse state, prepare child state */
- cur_state = comp->cur_state;
- child_state = ec_pnode(node);
- if (child_state == NULL)
+ cur_pstate = comp->cur_pstate;
+ child_pstate = ec_pnode(node);
+ if (child_pstate == NULL)
return -1;
- if (cur_state != NULL)
- ec_pnode_link_child(cur_state, child_state);
- comp->cur_state = child_state;
+ if (cur_pstate != NULL)
+ ec_pnode_link_child(cur_pstate, child_pstate);
+ comp->cur_pstate = child_pstate;
cur_group = comp->cur_group;
comp->cur_group = NULL;
/* fill the comp struct with items */
- ret = ec_node_type(node)->complete(node, comp, strvec);
+ ret = complete_cb(node, comp, strvec);
/* restore parent parse state */
- if (cur_state != NULL) {
- ec_pnode_unlink_child(cur_state, child_state);
- assert(!ec_pnode_has_child(child_state));
+ if (cur_pstate != NULL) {
+ ec_pnode_unlink_child(cur_pstate, child_pstate);
+ assert(!ec_pnode_has_child(child_pstate));
}
- ec_pnode_free(child_state);
- comp->cur_state = cur_state;
+ ec_pnode_free(child_pstate);
+ comp->cur_pstate = cur_pstate;
comp->cur_group = cur_group;
if (ret < 0)
struct ec_comp *comp = NULL;
int ret;
- comp = ec_comp(NULL);
+ comp = ec_comp();
if (comp == NULL)
goto fail;
}
static struct ec_comp_group *
-ec_comp_group(const struct ec_node *node, struct ec_pnode *parse)
+ec_comp_group(const struct ec_comp *comp, const struct ec_node *node,
+ struct ec_pnode *parse)
{
struct ec_comp_group *grp = NULL;
if (grp == NULL)
return NULL;
+ grp->comp = comp;
grp->attrs = ec_dict();
if (grp->attrs == NULL)
goto fail;
- grp->state = ec_pnode_dup(parse);
- if (grp->state == NULL)
+ grp->pstate = ec_pnode_dup(parse);
+ if (grp->pstate == NULL)
goto fail;
grp->node = node;
fail:
if (grp != NULL) {
- ec_pnode_free(grp->state);
+ ec_pnode_free(grp->pstate);
ec_dict_free(grp->attrs);
}
ec_free(grp);
if (comp->cur_group == NULL) {
struct ec_comp_group *grp;
- grp = ec_comp_group(node, comp->cur_state);
+ grp = ec_comp_group(comp, node, comp->cur_pstate);
if (grp == NULL)
return -1;
TAILQ_INSERT_TAIL(&comp->groups, grp, next);
ec_free(item);
}
-int ec_comp_add_item(struct ec_comp *comp,
- const struct ec_node *node,
- struct ec_comp_item **p_item,
- enum ec_comp_type type,
- const char *start, const char *full)
+struct ec_comp_item *ec_comp_add_item(struct ec_comp *comp,
+ const struct ec_node *node, enum ec_comp_type type,
+ const char *start, const char *full)
{
struct ec_comp_item *item = NULL;
int ret;
item = ec_comp_item(type, start, full);
if (item == NULL)
- return -1;
+ return NULL;
ret = ec_comp_item_add(comp, node, item);
if (ret < 0)
goto fail;
- if (p_item != NULL)
- *p_item = item;
-
- return 0;
+ return item;
fail:
ec_comp_item_free(item);
-
- return -1;
+ return NULL;
}
-/* XXX move in helpers + rename ? */
/* return a completion item of type "unknown" */
int
ec_complete_unknown(const struct ec_node *gen_node,
struct ec_comp *comp,
const struct ec_strvec *strvec)
{
- int ret;
+ const struct ec_comp_item *item = NULL;
if (ec_strvec_len(strvec) != 1)
return 0;
- ret = ec_comp_add_item(comp, gen_node, NULL,
- EC_COMP_UNKNOWN, NULL, NULL);
- if (ret < 0)
- return ret;
+ item = ec_comp_add_item(comp, gen_node, EC_COMP_UNKNOWN, NULL, NULL);
+ if (item == NULL)
+ return -1;
return 0;
}
TAILQ_REMOVE(&grp->items, item, next);
ec_comp_item_free(item);
}
- ec_pnode_free(ec_pnode_get_root(grp->state));
+ ec_pnode_free(ec_pnode_get_root(grp->pstate));
ec_dict_free(grp->attrs);
ec_free(grp);
}
}
const struct ec_pnode *
-ec_comp_group_get_state(const struct ec_comp_group *grp)
+ec_comp_group_get_pstate(const struct ec_comp_group *grp)
{
- return grp->state;
+ return grp->pstate;
}
const struct ec_dict *
return;
}
- fprintf(out, "completion: count=%u full=%u partial=%u unknown=%u\n",
+ fprintf(out, "completion: count=%zu full=%zu partial=%zu unknown=%zu\n",
comp->count, comp->count_full,
comp->count_partial, comp->count_unknown);
return 0;
}
-unsigned int ec_comp_count(
- const struct ec_comp *comp,
- enum ec_comp_type type)
+size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type)
{
- unsigned int count = 0;
+ size_t count = 0;
if (comp == NULL)
return count;
struct ec_editline_help *help)
{
const struct ec_comp_group *grp;
- const struct ec_pnode *state;
+ const struct ec_pnode *pstate;
const struct ec_node *node;
const char *node_help = NULL;
- const char *node_desc = NULL;
+ char *node_desc = NULL;
help->desc = NULL;
help->help = NULL;
grp = ec_comp_item_get_grp(item);
- for (state = ec_comp_group_get_state(grp); state != NULL;
- state = ec_pnode_get_parent(state)) {
- node = ec_pnode_get_node(state);
+ for (pstate = ec_comp_group_get_pstate(grp); pstate != NULL;
+ pstate = ec_pnode_get_parent(pstate)) {
+ node = ec_pnode_get_node(pstate);
if (node_help == NULL)
node_help = ec_dict_get(ec_node_attrs(node), "help");
- if (node_desc == NULL)
+ if (node_desc == NULL) {
node_desc = ec_node_desc(node);
+ if (node_desc == NULL)
+ goto fail;
+ }
}
- if (node_help == NULL)
- node_help = "";
if (node_desc == NULL)
goto fail;
+ if (node_help == NULL)
+ node_help = "";
- help->desc = ec_strdup(node_desc);
- if (help->desc == NULL)
- goto fail;
-
+ help->desc = node_desc;
help->help = ec_strdup(node_help);
if (help->help == NULL)
goto fail;
return 0;
fail:
+ ec_free(node_desc);
ec_free(help->desc);
ec_free(help->help);
return -1;
EC_LOG_TYPE_REGISTER(node);
+/* These states are used to mark the grammar graph when freeing, to
+ * detect loop. */
+enum ec_node_free_state {
+ EC_NODE_FREE_STATE_NONE,
+ EC_NODE_FREE_STATE_TRAVERSED,
+ EC_NODE_FREE_STATE_FREEABLE,
+ EC_NODE_FREE_STATE_NOT_FREEABLE,
+ EC_NODE_FREE_STATE_FREEING,
+};
+
+/**
+ * The grammar node structure.
+ */
struct ec_node {
- const struct ec_node_type *type;
- struct ec_config *config; /**< Generic configuration. */
- char *id;
- char *desc;
- struct ec_dict *attrs;
- unsigned int refcnt;
+ const struct ec_node_type *type; /**< The node type. */
+ struct ec_config *config; /**< Node configuration. */
+ char *id; /**< Node identifier (EC_NO_ID if none). */
+ struct ec_dict *attrs; /**< Attributes of the node. */
+ unsigned int refcnt; /**< Reference counter. */
struct {
- enum ec_node_free_state state; /**< State of loop detection */
+ enum ec_node_free_state state; /**< State of loop detection. */
unsigned int refcnt; /**< Number of reachable references
- * starting from node beeing freed */
+ * starting from node beeing freed. */
} free; /**< Freeing state: used for loop detection */
};
return NULL;
}
-int ec_node_type_register(struct ec_node_type *type)
+int ec_node_type_register(struct ec_node_type *type, bool override)
{
- if (ec_node_type_lookup(type->name) != NULL) {
+ if (!override && ec_node_type_lookup(type->name) != NULL) {
errno = EEXIST;
return -1;
}
- TAILQ_INSERT_TAIL(&node_type_list, type, next);
+ TAILQ_INSERT_HEAD(&node_type_list, type, next);
return 0;
}
if (node->id == NULL)
goto fail;
- if (ec_asprintf(&node->desc, "<%s>", type->name) < 0)
- goto fail;
-
node->attrs = ec_dict();
if (node->attrs == NULL)
goto fail;
fail:
if (node != NULL) {
ec_dict_free(node->attrs);
- ec_free(node->desc);
ec_free(node->id);
}
ec_free(node);
if (node->type->free_priv != NULL)
node->type->free_priv(node);
ec_free(node->id);
- ec_free(node->desc);
ec_dict_free(node->attrs);
}
EC_LOG(EC_LOG_ERR, "failed to dump node\n");
}
-const char *ec_node_desc(const struct ec_node *node)
+char *ec_node_desc(const struct ec_node *node)
{
+ char *desc = NULL;
+
if (node->type->desc != NULL)
return node->type->desc(node);
- return node->desc;
+ if (ec_asprintf(&desc, "<%s>", node->type->name) < 0)
+ return NULL;
+
+ return desc;
}
int ec_node_check_type(const struct ec_node *node,
unsigned int refs;
FILE *f = NULL;
char *buf = NULL;
+ char *desc = NULL;
size_t buflen = 0;
int testres = 0;
int ret;
free(buf);
buf = NULL;
+ desc = ec_node_desc(node);
testres |= EC_TEST_CHECK(
!strcmp(ec_node_type(node)->name, "seq") &&
!strcmp(ec_node_id(node), EC_NO_ID) &&
- !strcmp(ec_node_desc(node), "<seq>"),
+ !strcmp(desc, "<seq>"),
"bad child 0");
+ ec_free(desc);
+ desc = NULL;
testres |= EC_TEST_CHECK(
ec_node_get_children_count(node) == 2,
"child 2 should be NULL");
child = ec_node_find(node, "id_x");
+ desc = ec_node_desc(child);
testres |= EC_TEST_CHECK(child != NULL &&
!strcmp(ec_node_type(child)->name, "str") &&
!strcmp(ec_node_id(child), "id_x") &&
- !strcmp(ec_node_desc(child), "x"),
+ !strcmp(desc, "x"),
"bad child id_x");
+ ec_free(desc);
+ desc = NULL;
child = ec_node_find(node, "id_dezdex");
testres |= EC_TEST_CHECK(child == NULL,
"child with wrong id should be NULL");
};
static int ec_node_any_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_any *priv = ec_node_priv(node);
const struct ec_dict *attrs;
- (void)state;
+ (void)pstate;
if (ec_strvec_len(strvec) == 0)
return EC_PARSE_NOMATCH;
.schema = ec_node_any_schema,
.set_config = ec_node_any_set_config,
.parse = ec_node_any_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_any),
.free_priv = ec_node_any_free_priv,
};
static int
ec_node_bypass_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_bypass *priv = ec_node_priv(node);
- return ec_parse_child(priv->child, state, strvec);
+ return ec_parse_child(priv->child, pstate, strvec);
}
static int
}
static int
-ec_node_cmd_parse(const struct ec_node *node, struct ec_pnode *state,
+ec_node_cmd_parse(const struct ec_node *node, struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_cmd *priv = ec_node_priv(node);
- return ec_parse_child(priv->cmd, state, strvec);
+ return ec_parse_child(priv->cmd, pstate, strvec);
}
static int
};
typedef struct cond_result *(cond_func_t)(
- const struct ec_pnode *state,
+ const struct ec_pnode *pstate,
struct cond_result **in, size_t in_len);
-void cond_result_free(struct cond_result *res)
+static void
+cond_result_free(struct cond_result *res)
{
if (res == NULL)
return;
ec_free(res);
}
-void cond_result_table_free(struct cond_result **table, size_t len)
+static void
+cond_result_table_free(struct cond_result **table, size_t len)
{
size_t i;
}
static struct cond_result *
-eval_root(const struct ec_pnode *state, struct cond_result **in, size_t in_len)
+eval_root(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
{
struct cond_result *out = NULL;
const struct ec_pnode *root = NULL;
if (out->htable == NULL)
goto fail;
- root = ec_pnode_get_root(state);
+ root = ec_pnode_get_root(pstate);
if (ec_htable_set(out->htable, &root, sizeof(root), NULL, NULL) < 0)
goto fail;
}
static struct cond_result *
-eval_current(const struct ec_pnode *state, struct cond_result **in,
+eval_current(const struct ec_pnode *pstate, struct cond_result **in,
size_t in_len)
{
struct cond_result *out = NULL;
if (out->htable == NULL)
goto fail;
- if (ec_htable_set(out->htable, &state, sizeof(state), NULL, NULL) < 0)
+ if (ec_htable_set(out->htable, &pstate, sizeof(pstate), NULL, NULL) < 0)
goto fail;
cond_result_table_free(in, in_len);
}
static struct cond_result *
-eval_bool(const struct ec_pnode *state, struct cond_result **in, size_t in_len)
+eval_bool(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
{
struct cond_result *out = NULL;
- (void)state;
+ (void)pstate;
if (in_len != 1) {
EC_LOG(LOG_ERR, "bool() takes one argument.\n");
}
static struct cond_result *
-eval_or(const struct ec_pnode *state, struct cond_result **in, size_t in_len)
+eval_or(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
{
struct cond_result *out = NULL;
size_t i;
- (void)state;
+ (void)pstate;
if (in_len < 2) {
EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
}
static struct cond_result *
-eval_and(const struct ec_pnode *state, struct cond_result **in, size_t in_len)
+eval_and(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
{
struct cond_result *out = NULL;
size_t i;
- (void)state;
+ (void)pstate;
if (in_len < 2) {
EC_LOG(LOG_ERR, "or() takes at least two arguments\n");
}
static struct cond_result *
-eval_first_child(const struct ec_pnode *state, struct cond_result **in,
+eval_first_child(const struct ec_pnode *pstate, struct cond_result **in,
size_t in_len)
{
struct cond_result *out = NULL;
const struct ec_pnode * const *pparse;
struct ec_pnode *parse;
- (void)state;
+ (void)pstate;
if (in_len != 1 || in[0]->type != NODESET) {
EC_LOG(LOG_ERR, "first_child() takes one argument of type nodeset.\n");
}
static struct cond_result *
-eval_find(const struct ec_pnode *state, struct cond_result **in,
+eval_find(const struct ec_pnode *pstate, struct cond_result **in,
size_t in_len)
{
struct cond_result *out = NULL;
struct ec_pnode *parse;
const char *id;
- (void)state;
+ (void)pstate;
if (in_len != 2 || in[0]->type != NODESET || in[1]->type != STR) {
EC_LOG(LOG_ERR, "find() takes two arguments (nodeset, str).\n");
}
static struct cond_result *
-eval_cmp(const struct ec_pnode *state, struct cond_result **in,
+eval_cmp(const struct ec_pnode *pstate, struct cond_result **in,
size_t in_len)
{
struct cond_result *out = NULL;
struct ec_htable_elt_ref *iter;
bool eq = false, gt = false;
- (void)state;
+ (void)pstate;
if (in_len != 3 || in[0]->type != STR || in[1]->type != in[2]->type) {
EC_LOG(LOG_ERR, "cmp() takes 3 arguments (str, <type>, <type>).\n");
}
static struct cond_result *
-eval_count(const struct ec_pnode *state, struct cond_result **in, size_t in_len)
+eval_count(const struct ec_pnode *pstate, struct cond_result **in, size_t in_len)
{
struct cond_result *out = NULL;
- (void)state;
+ (void)pstate;
if (in_len != 1 || in[0]->type != NODESET) {
EC_LOG(LOG_ERR, "count() takes one argument of type nodeset.\n");
}
static struct cond_result *
-eval_func(const char *name, const struct ec_pnode *state,
+eval_func(const char *name, const struct ec_pnode *pstate,
struct cond_result **in, size_t in_len)
{
cond_func_t *f;
return NULL;
}
- return f(state, in, in_len);
+ return f(pstate, in, in_len);
}
static struct cond_result *
-eval_condition(const struct ec_pnode *cond, const struct ec_pnode *state)
+eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
{
const struct ec_pnode *iter;
struct cond_result *res = NULL;
iter = ec_pnode_find((void *)arg_list, "id_arg");
while (iter != NULL) {
args = ec_realloc(args, (n_arg + 1) * sizeof(*args));
- args[n_arg] = eval_condition(iter, state);
+ args[n_arg] = eval_condition(iter, pstate);
if (args[n_arg] == NULL)
goto fail;
n_arg++;
}
res = eval_func(ec_strvec_val(ec_pnode_strvec(func_name), 0),
- state, args, n_arg);
+ pstate, args, n_arg);
args = NULL;
return res;
}
}
static int
-validate_condition(const struct ec_pnode *cond, const struct ec_pnode *state)
+validate_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate)
{
struct cond_result *res;
int ret;
- res = eval_condition(cond, state);
+ res = eval_condition(cond, pstate);
if (res == NULL)
return -1;
}
static int
-ec_node_cond_parse(const struct ec_node *node, struct ec_pnode *state,
+ec_node_cond_parse(const struct ec_node *node, struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_cond *priv = ec_node_priv(node);
struct ec_pnode *child;
int ret, valid;
- ret = ec_parse_child(priv->child, state, strvec);
+ ret = ec_parse_child(priv->child, pstate, strvec);
if (ret <= 0)
return ret;
- valid = validate_condition(priv->parsed_cond, state);
+ valid = validate_condition(priv->parsed_cond, pstate);
if (valid < 0)
return valid;
if (valid == 0) {
- child = ec_pnode_get_last_child(state);
- ec_pnode_unlink_child(state, child);
+ child = ec_pnode_get_last_child(pstate);
+ ec_pnode_unlink_child(pstate, child);
ec_pnode_free(child);
return EC_PARSE_NOMATCH;
}
char key[64];
int ret = -1;
- parse = ec_comp_get_state(comp);
+ parse = ec_comp_get_cur_pstate(comp);
child = priv->build(parse, priv->opaque);
if (child == NULL)
goto fail;
};
static int ec_node_empty_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
(void)node;
- (void)state;
+ (void)pstate;
(void)strvec;
return 0;
}
static struct ec_node_type ec_node_empty_type = {
.name = "empty",
.parse = ec_node_empty_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_empty),
};
};
static int ec_node_expr_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_expr *priv = ec_node_priv(node);
return -1;
}
- return ec_parse_child(priv->child, state, strvec);
+ return ec_parse_child(priv->child, pstate, strvec);
}
static int
static int
ec_node_file_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
(void)node;
- (void)state;
+ (void)pstate;
if (ec_strvec_len(strvec) == 0)
return EC_PARSE_NOMATCH;
if (ec_asprintf(&disp_str, "%s", de->d_name) < 0)
goto fail;
}
- if (ec_comp_add_item(comp, node, &item,
- type, input, comp_str) < 0)
+ item = ec_comp_add_item(comp, node, type, input, comp_str);
+ if (item == NULL)
goto out;
/* fix the display string: we don't want to display the full
}
static int ec_node_int_uint_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_int_uint *priv = ec_node_priv(node);
uint64_t u64;
int64_t i64;
- (void)state;
+ (void)pstate;
if (ec_strvec_len(strvec) == 0)
return EC_PARSE_NOMATCH;
.schema = ec_node_int_schema,
.set_config = ec_node_int_set_config,
.parse = ec_node_int_uint_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_int_uint),
.init_priv = ec_node_uint_init_priv,
};
.schema = ec_node_uint_schema,
.set_config = ec_node_uint_set_config,
.parse = ec_node_int_uint_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_int_uint),
};
};
static int ec_node_many_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_many *priv = ec_node_priv(node);
if (childvec == NULL)
goto fail;
- ret = ec_parse_child(priv->child, state, childvec);
+ ret = ec_parse_child(priv->child, pstate, childvec);
if (ret < 0)
goto fail;
/* it matches an empty strvec, no need to continue */
if (ret == 0) {
- child_parse = ec_pnode_get_last_child(state);
- ec_pnode_unlink_child(state, child_parse);
+ child_parse = ec_pnode_get_last_child(pstate);
+ ec_pnode_unlink_child(pstate, child_parse);
ec_pnode_free(child_parse);
break;
}
}
if (count < priv->min) {
- ec_pnode_free_children(state);
+ ec_pnode_free_children(pstate);
return EC_PARSE_NOMATCH;
}
struct ec_comp *comp,
const struct ec_strvec *strvec)
{
- struct ec_pnode *parse = ec_comp_get_state(comp);
+ struct ec_pnode *parse = ec_comp_get_cur_pstate(comp);
struct ec_strvec *childvec = NULL;
unsigned int i;
int ret;
const struct ec_strvec *strvec)
{
struct ec_node_once *priv = ec_node_priv(node);
- struct ec_pnode *parse = ec_comp_get_state(comp);
+ struct ec_pnode *parse = ec_comp_get_cur_pstate(comp);
unsigned int count;
int ret;
static int
ec_node_option_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_option *priv = ec_node_priv(node);
int ret;
- ret = ec_parse_child(priv->child, state, strvec);
+ ret = ec_parse_child(priv->child, pstate, strvec);
if (ret < 0)
return ret;
static int
ec_node_or_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_or *priv = ec_node_priv(node);
int ret;
for (i = 0; i < priv->len; i++) {
- ret = ec_parse_child(priv->table[i], state, strvec);
+ ret = ec_parse_child(priv->table[i], pstate, strvec);
if (ret == EC_PARSE_NOMATCH)
continue;
return ret;
static int
ec_node_re_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_re *priv = ec_node_priv(node);
const char *str;
regmatch_t pos;
- (void)state;
+ (void)pstate;
if (ec_strvec_len(strvec) == 0)
return EC_PARSE_NOMATCH;
.schema = ec_node_re_schema,
.set_config = ec_node_re_set_config,
.parse = ec_node_re_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_re),
.free_priv = ec_node_re_free_priv,
};
static int
ec_node_re_lex_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_re_lex *priv = ec_node_priv(node);
if (new_vec == NULL)
goto fail;
- ret = ec_parse_child(priv->child, state, new_vec);
+ ret = ec_parse_child(priv->child, pstate, new_vec);
if (ret < 0)
goto fail;
if ((unsigned)ret == ec_strvec_len(new_vec)) {
ret = 1;
} else if (ret != EC_PARSE_NOMATCH) {
- child_parse = ec_pnode_get_last_child(state);
- ec_pnode_unlink_child(state, child_parse);
+ child_parse = ec_pnode_get_last_child(pstate);
+ ec_pnode_unlink_child(pstate, child_parse);
ec_pnode_free(child_parse);
ret = EC_PARSE_NOMATCH;
}
.schema = ec_node_re_lex_schema,
.set_config = ec_node_re_lex_set_config,
.parse = ec_node_re_lex_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_re_lex),
.free_priv = ec_node_re_lex_free_priv,
.get_children_count = ec_node_re_lex_get_children_count,
static int
ec_node_seq_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_seq *priv = ec_node_priv(node);
if (childvec == NULL)
goto fail;
- ret = ec_parse_child(priv->table[i], state, childvec);
+ ret = ec_parse_child(priv->table[i], pstate, childvec);
if (ret < 0)
goto fail;
childvec = NULL;
if (ret == EC_PARSE_NOMATCH) {
- ec_pnode_free_children(state);
+ ec_pnode_free_children(pstate);
return EC_PARSE_NOMATCH;
}
struct ec_comp *comp,
const struct ec_strvec *strvec)
{
- struct ec_pnode *parse = ec_comp_get_state(comp);
+ struct ec_pnode *parse = ec_comp_get_cur_pstate(comp);
struct ec_strvec *childvec = NULL;
unsigned int i;
int ret;
static int
ec_node_sh_lex_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_sh_lex *priv = ec_node_priv(node);
if (new_vec == NULL)
goto fail;
- ret = ec_parse_child(priv->child, state, new_vec);
+ ret = ec_parse_child(priv->child, pstate, new_vec);
if (ret < 0)
goto fail;
if ((unsigned)ret == ec_strvec_len(new_vec)) {
ret = 1;
} else if (ret != EC_PARSE_NOMATCH) {
- child_parse = ec_pnode_get_last_child(state);
- ec_pnode_unlink_child(state, child_parse);
+ child_parse = ec_pnode_get_last_child(pstate);
+ ec_pnode_unlink_child(pstate, child_parse);
ec_pnode_free(child_parse);
ret = EC_PARSE_NOMATCH;
}
static int
ec_node_space_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
const char *str;
size_t len = 0;
- (void)state;
+ (void)pstate;
(void)node;
if (ec_strvec_len(strvec) == 0)
static struct ec_node_type ec_node_space_type = {
.name = "space",
.parse = ec_node_space_parse,
- .complete = ec_complete_unknown,
.size = sizeof(struct ec_node_space),
};
static int
ec_node_str_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_str *priv = ec_node_priv(node);
const char *str;
- (void)state;
+ (void)pstate;
if (priv->string == NULL) {
errno = EINVAL;
const struct ec_strvec *strvec)
{
struct ec_node_str *priv = ec_node_priv(node);
+ const struct ec_comp_item *item;
const char *str;
size_t n = 0;
if (str[n] != '\0')
return EC_PARSE_NOMATCH;
- if (ec_comp_add_item(comp, node, NULL, EC_COMP_FULL,
- str, priv->string) < 0)
+ item = ec_comp_add_item(comp, node, EC_COMP_FULL, str, priv->string);
+ if (item == NULL)
return -1;
return 0;
}
-static const char *ec_node_str_desc(const struct ec_node *node)
+static char *ec_node_str_desc(const struct ec_node *node)
{
struct ec_node_str *priv = ec_node_priv(node);
- return priv->string;
+ return ec_strdup(priv->string);
}
static void ec_node_str_free_priv(struct ec_node *node)
{
struct ec_node *node;
int testres = 0;
+ char *desc = NULL;
node = ec_node_str(EC_NO_ID, "foo");
if (node == NULL) {
EC_LOG(EC_LOG_ERR, "cannot create node\n");
return -1;
}
- testres |= EC_TEST_CHECK(!strcmp(ec_node_desc(node), "foo"),
+ desc = ec_node_desc(node);
+ testres |= EC_TEST_CHECK(!strcmp(desc, "foo"),
"Invalid node description.");
+ ec_free(desc);
+ desc = NULL;
testres |= EC_TEST_CHECK_PARSE(node, 1, "foo");
testres |= EC_TEST_CHECK_PARSE(node, 1, "foo", "bar");
testres |= EC_TEST_CHECK_PARSE(node, -1, "foobar");
* updated accordingly. */
static int
__ec_node_subset_parse(struct parse_result *out, struct ec_node **table,
- size_t table_len, struct ec_pnode *state,
+ size_t table_len, struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node **child_table;
for (i = 0; i < table_len; i++) {
/* try to parse elt i */
- ret = ec_parse_child(table[i], state, strvec);
+ ret = ec_parse_child(table[i], pstate, strvec);
if (ret < 0)
goto fail;
memset(&result, 0, sizeof(result));
ret = __ec_node_subset_parse(&result, child_table,
- table_len - 1, state, childvec);
+ table_len - 1, pstate, childvec);
ec_strvec_free(childvec);
childvec = NULL;
if (ret < 0)
/* if result is not the best, ignore */
if (result.parse_len < best_result.parse_len) {
memset(&result, 0, sizeof(result));
- ec_pnode_del_last_child(state);
+ ec_pnode_del_last_child(pstate);
continue;
}
/* replace the previous best result */
ec_pnode_free(best_parse);
- best_parse = ec_pnode_get_last_child(state);
- ec_pnode_unlink_child(state, best_parse);
+ best_parse = ec_pnode_get_last_child(pstate);
+ ec_pnode_unlink_child(pstate, best_parse);
best_result.parse_len = result.parse_len + 1;
best_result.len = len + result.len;
*out = best_result;
ec_free(child_table);
if (best_parse != NULL)
- ec_pnode_link_child(state, best_parse);
+ ec_pnode_link_child(pstate, best_parse);
return 0;
static int
ec_node_subset_parse(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
struct ec_node_subset *priv = ec_node_priv(node);
memset(&result, 0, sizeof(result));
ret = __ec_node_subset_parse(&result, priv->table,
- priv->len, state, strvec);
+ priv->len, pstate, strvec);
if (ret < 0)
goto fail;
struct ec_comp *comp,
const struct ec_strvec *strvec)
{
- struct ec_pnode *parse = ec_comp_get_state(comp);
+ struct ec_pnode *parse = ec_comp_get_cur_pstate(comp);
struct ec_strvec *childvec = NULL;
struct ec_node *save;
size_t i, len;
};
static int __ec_parse_child(const struct ec_node *node,
- struct ec_pnode *state,
+ struct ec_pnode *pstate,
bool is_root, const struct ec_strvec *strvec)
{
struct ec_strvec *match_strvec;
if (child == NULL)
return -1;
- ec_pnode_link_child(state, child);
+ ec_pnode_link_child(pstate, child);
} else {
- child = state;
+ child = pstate;
}
ret = ec_node_type(node)->parse(node, child, strvec);
if (ret < 0)
if (ret == EC_PARSE_NOMATCH) {
if (!is_root) {
- ec_pnode_unlink_child(state, child);
+ ec_pnode_unlink_child(pstate, child);
ec_pnode_free(child);
}
return ret;
fail:
if (!is_root) {
- ec_pnode_unlink_child(state, child);
+ ec_pnode_unlink_child(pstate, child);
ec_pnode_free(child);
}
return -1;
}
-int ec_parse_child(const struct ec_node *node, struct ec_pnode *state,
+int ec_parse_child(const struct ec_node *node, struct ec_pnode *pstate,
const struct ec_strvec *strvec)
{
- assert(state != NULL);
- return __ec_parse_child(node, state, false, strvec);
+ assert(pstate != NULL);
+ return __ec_parse_child(node, pstate, false, strvec);
}
// XXX what is returned if no match ??
struct ec_strvec *vec = NULL;
const char *s;
int ret = 0;
- unsigned int count = 0;
+ size_t count = 0;
va_list ap;
va_start(ap, type);
/* check if we have more completions (or less) than expected */
if (count != ec_comp_count(c, type)) {
EC_LOG(EC_LOG_ERR,
- "nb_completion (%d) does not match (%d)\n",
+ "nb_completion (%zu) does not match (%zu)\n",
count, ec_comp_count(c, type));
ec_comp_dump(stdout, c);
ret = -1;