Loading...
Searching...
No Matches
pool-editline/main.c

Dynamic completion from runtime data structures.

Dynamic completion from runtime data structures.Demonstrates using ec_node_dynlist() to generate completions from runtime data. The example manages IP address pools and provides completion for pool names and addresses.

Example session:

pool> pool list
No pool
pool> pool add servers
pool> pool add clients
pool> pool list
servers
clients
pool> addr pool servers add 192.168.1.1
pool> addr pool servers add 192.168.1.2
pool> addr pool servers list
192.168.1.1
192.168.1.2
pool> addr pool servers del 192.168.1.1
pool> pool del clients
pool> exit
Exit !
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright 2025, Olivier MATZ <zer0@droids-corp.org>
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ecoli.h>
#include "ip_pool.h"
#define POOL_REGEXP "[A-Za-z][-_a-zA-Z0-9]+"
#define IP_REGEXP \
"((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-" \
"9]|[1-9][0-9]|[0-9])"
#define ID_POOL_NAME "id_pool_name"
#define ID_ADDR "id_addr"
/* stop program when true */
static bool done;
static struct ec_node *with_help(struct ec_node *node, const char *help)
{
if (node == NULL)
return NULL;
if (ec_interact_set_help(node, help) < 0) {
ec_node_free(node);
return NULL;
}
return node;
}
static struct ec_node *with_cb(struct ec_node *node, ec_interact_command_cb_t cb)
{
if (node == NULL)
return NULL;
if (ec_interact_set_callback(node, cb) < 0) {
ec_node_free(node);
return NULL;
}
return node;
}
static struct ec_node *with_desc(struct ec_node *node, const char *desc)
{
if (node == NULL)
return NULL;
if (ec_interact_set_desc(node, desc) < 0) {
ec_node_free(node);
return NULL;
}
return node;
}
static int pool_list_cb(const struct ec_pnode *parse)
{
struct ec_strvec *names = NULL;
size_t len;
size_t i;
(void)parse;
names = ip_pool_list();
if (names == NULL) {
fprintf(stderr, "Failed to list pools\n");
return -1;
}
len = ec_strvec_len(names);
if (len == 0) {
printf("No pool\n");
} else {
for (i = 0; i < len; i++)
printf("%s\n", ec_strvec_val(names, i));
}
ec_strvec_free(names);
return 0;
}
static int pool_add_cb(const struct ec_pnode *parse)
{
const char *pool_name;
pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0);
if (ip_pool(pool_name) == NULL) {
fprintf(stderr, "Failed to add pool\n");
return -1;
}
return 0;
}
static int pool_del_cb(const struct ec_pnode *parse)
{
const char *pool_name;
pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0);
ip_pool_free(pool_name);
return 0;
}
static int addr_list_cb(const struct ec_pnode *parse)
{
struct ec_strvec *addrs = NULL;
const struct ip_pool *pool;
const char *pool_name;
size_t len;
size_t i;
pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0);
pool = ip_pool_lookup(pool_name);
addrs = ip_pool_addr_list(pool);
if (addrs == NULL) {
fprintf(stderr, "Failed to list pool addresses\n");
return -1;
}
len = ec_strvec_len(addrs);
if (len == 0) {
printf("No address\n");
} else {
for (i = 0; i < len; i++)
printf("%s\n", ec_strvec_val(addrs, i));
}
ec_strvec_free(addrs);
return 0;
}
static int addr_add_cb(const struct ec_pnode *parse)
{
const char *pool_name;
struct ip_pool *pool;
const char *addr;
pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0);
pool = ip_pool_lookup(pool_name);
addr = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_ADDR)), 0);
if (ip_pool_addr_add(pool, addr) < 0) {
fprintf(stderr, "Failed to add address to pool\n");
return -1;
}
return 0;
}
static int addr_del_cb(const struct ec_pnode *parse)
{
const char *pool_name;
struct ip_pool *pool;
const char *addr;
pool_name = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_POOL_NAME)), 0);
pool = ip_pool_lookup(pool_name);
addr = ec_strvec_val(ec_pnode_get_strvec(ec_pnode_find(parse, ID_ADDR)), 0);
if (ip_pool_addr_del(pool, addr) < 0) {
fprintf(stderr, "Failed to delete address from pool\n");
return -1;
}
return 0;
}
static int exit_cb(const struct ec_pnode *parse)
{
(void)parse;
printf("Exit !\n");
done = true;
return 0;
}
static int check_exit(void *opaque)
{
(void)opaque;
return done;
}
static struct ec_strvec *list_pools(struct ec_pnode *pstate, void *opaque)
{
(void)pstate;
(void)opaque;
return ip_pool_list();
}
static struct ec_strvec *list_addrs(struct ec_pnode *pstate, void *opaque)
{
const char *pool_name;
struct ip_pool *pool;
(void)opaque;
pool_name = ec_strvec_val(
ec_pnode_get_strvec(ec_pnode_find(ec_pnode_get_root(pstate), ID_POOL_NAME)), 0
);
if (pool_name == NULL)
return ec_strvec();
pool = ip_pool_lookup(pool_name);
if (pool == NULL)
return ec_strvec();
return ip_pool_addr_list(pool);
}
static struct ec_node *create_pool_commands(void)
{
struct ec_node *cmdlist = NULL;
/* the list of pool subcommands */
cmdlist = EC_NODE_OR(
EC_NO_ID,
with_cb(with_help(ec_node_str(EC_NO_ID, "list"), "Display the list of IP pools"),
pool_list_cb),
with_cb(EC_NODE_SEQ(
EC_NO_ID,
with_help(ec_node_str(EC_NO_ID, "add"), "Create an IP pool"),
with_help(
with_desc(
ec_node_dynlist(
ID_POOL_NAME,
list_pools,
NULL,
POOL_REGEXP,
DYNLIST_MATCH_REGEXP | DYNLIST_EXCLUDE_LIST
),
"<pool-name>"
),
"The name of the pool to create"
)
),
pool_add_cb),
with_cb(EC_NODE_SEQ(
EC_NO_ID,
with_help(ec_node_str(EC_NO_ID, "del"), "Delete an IP pool"),
with_help(
with_desc(
ec_node_dynlist(
ID_POOL_NAME,
list_pools,
NULL,
POOL_REGEXP,
DYNLIST_MATCH_LIST
),
"<pool-name>"
),
"The name of the pool to delete"
)
),
pool_del_cb)
);
/* the pool command */
return EC_NODE_SEQ(
EC_NO_ID,
with_help(ec_node_str(EC_NO_ID, "pool"), "Add, delete, or list pools"),
cmdlist
);
}
static struct ec_node *create_addr_commands(void)
{
struct ec_node *cmdlist = NULL;
/* the list of addr subcommands */
cmdlist = EC_NODE_OR(
EC_NO_ID,
with_cb(with_help(
EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "list")),
"Display the list of IP addresses in a pool"
),
addr_list_cb),
EC_NODE_SEQ(
EC_NO_ID,
with_cb(with_help(
ec_node_str(EC_NO_ID, "add"),
"Add an IP address into a pool"
),
addr_add_cb),
with_help(
with_desc(
ec_node_dynlist(
ID_ADDR,
list_addrs,
NULL,
IP_REGEXP,
DYNLIST_MATCH_REGEXP | DYNLIST_EXCLUDE_LIST
),
"<a.b.c.d>"
),
"The IP to add"
)
),
EC_NODE_SEQ(
EC_NO_ID,
with_cb(with_help(
ec_node_str(EC_NO_ID, "del"),
"Delete an IP address from a pool"
),
addr_del_cb),
with_help(
with_desc(
ec_node_dynlist(
ID_ADDR,
list_addrs,
NULL,
IP_REGEXP,
DYNLIST_MATCH_LIST
),
"<a.b.c.d>"
),
"The existing IP to delete"
)
)
);
return EC_NODE_SEQ(
EC_NO_ID,
with_help(ec_node_str(EC_NO_ID, "addr"), "Add, delete, list addresses in pool"),
with_help(ec_node_str(EC_NO_ID, "pool"), "Specify the pool for this operation"),
with_help(
with_desc(
ec_node_dynlist(
ID_POOL_NAME,
list_pools,
NULL,
POOL_REGEXP,
DYNLIST_MATCH_LIST
),
"<pool-name>"
),
"The name of the pool (must exist)"
),
cmdlist
);
}
static struct ec_node *create_commands(void)
{
struct ec_node *cmdlist = NULL;
struct ec_node *cmd = NULL;
/* the top node containing the list of commands */
cmdlist = ec_node("or", EC_NO_ID);
if (cmdlist == NULL)
goto fail;
/* the exit command */
cmd = ec_node_str(EC_NO_ID, "exit");
if (ec_interact_set_callback(cmd, exit_cb) < 0)
goto fail;
if (ec_interact_set_help(cmd, "Exit program") < 0)
goto fail;
if (ec_node_or_add(cmdlist, cmd) < 0)
goto fail;
/* the pool commands */
if (ec_node_or_add(cmdlist, create_pool_commands()) < 0)
goto fail;
/* the addr commands */
if (ec_node_or_add(cmdlist, create_addr_commands()) < 0)
goto fail;
/* the lexer, added above the command list */
cmdlist = ec_node_sh_lex(EC_NO_ID, cmdlist);
if (cmdlist == NULL)
goto fail;
return cmdlist;
fail:
fprintf(stderr, "cannot initialize nodes\n");
ec_node_free(cmdlist);
return NULL;
}
int main(void)
{
struct ec_editline *editline = NULL;
struct ec_node *node = NULL;
if (ip_pool_init() < 0) {
fprintf(stderr, "cannot init IP pools: %s\n", strerror(errno));
return 1;
}
if (ec_init() < 0) {
fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
return 1;
}
node = create_commands();
if (node == NULL) {
fprintf(stderr, "failed to create commands: %s\n", strerror(errno));
goto fail;
}
editline = ec_editline("pool-editline", stdin, stdout, stderr, 0);
if (editline == NULL) {
fprintf(stderr, "Failed to initialize editline\n");
goto fail;
}
if (ec_editline_set_prompt(editline, "pool> ") < 0) {
fprintf(stderr, "Failed to set prompt\n");
goto fail;
}
ec_editline_set_node(editline, node);
if (ec_editline_interact(editline, check_exit, NULL) < 0)
goto fail;
ec_editline_free(editline);
ec_node_free(node);
ip_pool_exit();
return 0;
fail:
ec_editline_free(editline);
ec_node_free(node);
ip_pool_exit();
return 1;
}
ec_editline_set_prompt
int ec_editline_set_prompt(struct ec_editline *editline, const char *prompt)
Set editline prompt.
ec_editline
struct ec_editline * ec_editline(const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags)
Create an editline instance with default behavior.
ec_editline_free
void ec_editline_free(struct ec_editline *editline)
Free an editline structure allocated with ec_editline().
ec_editline_interact
int ec_editline_interact(struct ec_editline *editline, ec_editline_check_exit_cb_t check_exit_cb, void *opaque)
Interact with the user until exit.
ec_editline_set_node
int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node)
Attach an ecoli node to the editline structure.
ec_init
int ec_init(void)
Initialize ecoli library.
ec_interact_command_cb_t
int(* ec_interact_command_cb_t)(const struct ec_pnode *)
Type of callback attached with EC_INTERACT_CB_ATTR attribute.
Definition interact.h:49
ec_interact_set_help
int ec_interact_set_help(struct ec_node *node, const char *help)
Set help on a grammar node.
ec_interact_set_desc
int ec_interact_set_desc(struct ec_node *node, const char *desc)
Set short description of a grammar node.
ec_interact_set_callback
int ec_interact_set_callback(struct ec_node *node, ec_interact_command_cb_t cb)
Set callback function on a grammar node.
ec_node_dynlist
struct ec_node * ec_node_dynlist(const char *id, ec_node_dynlist_get_t get, void *opaque, const char *re_str, enum ec_node_dynlist_flags flags)
Create a dynlist node.
DYNLIST_MATCH_REGEXP
@ DYNLIST_MATCH_REGEXP
Match names from regexp pattern.
Definition node_dynlist.h:51
DYNLIST_MATCH_LIST
@ DYNLIST_MATCH_LIST
Match names returned by the user callback.
Definition node_dynlist.h:46
DYNLIST_EXCLUDE_LIST
@ DYNLIST_EXCLUDE_LIST
Don't match names returned by the user callback, even if it matches the regexp.
Definition node_dynlist.h:57
ec_node_or_add
int ec_node_or_add(struct ec_node *node, struct ec_node *child)
Add a child to an "or" node.
EC_NODE_OR
#define EC_NODE_OR(args...)
Create a new "or" node from an arbitrary list of child nodes.
Definition node_or.h:23
EC_NODE_SEQ
#define EC_NODE_SEQ(args...)
Create a sequence node from a list of child nodes.
Definition node_seq.h:29
ec_node_sh_lex
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
Create a shell lexer node.
ec_node_str
struct ec_node * ec_node_str(const char *id, const char *str)
Create a string node that matches a specific string.
ec_node
struct ec_node * ec_node(const char *typename, const char *id)
Create a new node from its type name.
EC_NO_ID
#define EC_NO_ID
Node has no identifier.
Definition node.h:64
ec_node_free
void ec_node_free(struct ec_node *node)
Decrement node reference counter and free the node if it is the last reference.
ec_pnode_get_strvec
const struct ec_strvec * ec_pnode_get_strvec(const struct ec_pnode *pnode)
Get the string vector associated with a parsing node.
ec_pnode_find
const struct ec_pnode * ec_pnode_find(const struct ec_pnode *root, const char *id)
Find a node from its identifier.
ec_pnode_get_root
struct ec_pnode * ec_pnode_get_root(struct ec_pnode *pnode)
Get the root of the parsing tree.
ec_pnode
struct ec_pnode * ec_pnode(const struct ec_node *node)
Create an empty parsing tree.
ec_strvec_free
void ec_strvec_free(struct ec_strvec *strvec)
Free a string vector.
ec_strvec_val
const char * ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
Get a string element from a vector.
ec_strvec
struct ec_strvec * ec_strvec(void)
Allocate a new empty string vector.
ec_strvec_len
size_t ec_strvec_len(const struct ec_strvec *strvec)
Get the length of a string vector.