From 3c4e00bb661e42c9f0e27574cc01424af4bec9bc Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Thu, 15 Jan 2026 16:54:40 +0200 Subject: [PATCH 1/2] Add dynamic library support for CLIgen extensions Introduced mechanisms to dynamically load and inject custom match result handling via shared libraries using `dlopen` and `dlsym`. Updated relevant structures and functions to support this functionality. --- Makefile.in | 2 +- cligen_handle.c | 44 ++++++++++++++++++++++ cligen_handle.h | 14 +++++++ cligen_handle_internal.h | 1 + cligen_read.c | 80 ++++++++++++++++++++++++++++------------ 5 files changed, 116 insertions(+), 25 deletions(-) diff --git a/Makefile.in b/Makefile.in index 1af0484f..724ba5ac 100644 --- a/Makefile.in +++ b/Makefile.in @@ -57,7 +57,7 @@ ifneq ($(LINKAGE),dynamic) # -rdynamic for using -ldl LDFLAGS += -rdynamic endif -LIBS = @LIBS@ +LIBS = @LIBS@ -ldl INCLUDES = -I. -I@srcdir@ @INCLUDES@ HOST_VENDOR = @host_vendor@ diff --git a/cligen_handle.c b/cligen_handle.c index 93bffbfc..8615eceb 100644 --- a/cligen_handle.c +++ b/cligen_handle.c @@ -75,6 +75,8 @@ */ #define TREENAME_KEYWORD_DEFAULT "treename" +#include + /* forward */ static int terminal_rows_set1(int rows); @@ -153,6 +155,7 @@ cligen_init(void) struct cligen_handle *ch; cligen_handle h = NULL; struct sigaction sigh; + void *lib_handle; if ((ch = malloc(sizeof(*ch))) == NULL){ fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno)); @@ -185,6 +188,14 @@ cligen_init(void) cligen_buf_init(h); /* getline cant function without some history */ (void)cligen_hist_init(h, CLIGEN_HISTSIZE_DEFAULT); + + lib_handle = dlopen("libcligen_ext.so", RTLD_LAZY); + if (lib_handle) { + cligen_ext_lib_handle_set(h, lib_handle); + }else { + cligen_ext_lib_handle_set(h, NULL); + } + done: return h; } @@ -1405,3 +1416,36 @@ cligen_tree_resolve_wrapper_get(cligen_handle h, *arg = ch->ch_tree_resolve_wrapper_arg; return 0; } + + +/*! Get extension library handle + * + * This handle is typically used to store a pointer to a dynamically loaded + * library (via dlopen) that provides additional functionality. + * @param[in] h CLIgen handle + * @retval ptr Pointer to the library handle or NULL if not set + */ +void * +cligen_ext_lib_handle_get(cligen_handle h) +{ + struct cligen_handle *ch = handle(h); + + return ch->ch_ext_lib_handle; +} + +/*! Set extension library handle + * + * Store a handle to a loaded shared object in the CLIgen handle. + * @param[in] h CLIgen handle + * @param[in] data Pointer to store (e.g., dlopen handle) + * @retval 0 OK + */ +int +cligen_ext_lib_handle_set(cligen_handle h, + void *data) +{ + struct cligen_handle *ch = handle(h); + + ch->ch_ext_lib_handle = data; + return 0; +} diff --git a/cligen_handle.h b/cligen_handle.h index a77338ce..665a01cb 100644 --- a/cligen_handle.h +++ b/cligen_handle.h @@ -57,6 +57,8 @@ */ #define CLIGEN_TABMODE_STEPS 0x04 +#include "cligen_result.h" + /* * Types */ @@ -103,6 +105,15 @@ typedef int (cligen_tree_resolve_wrapper_fn)(cligen_handle h, char *name, cvec * */ typedef int (cligen_hist_fn)(cligen_handle h, char *cmd, void *arg); +/*! Function type for injecting custom data into match results. + * @param[in] mr Match results to be modified + * @param[in] cvt Tokenized string vector providing context + * @param[in] string Original input string + * @retval 0 OK + * @retval -1 Error + */ +typedef int (cligen_match_inject_fn)(match_result *mr, cvec *cvt, const char *string); + /* * Prototypes */ @@ -213,4 +224,7 @@ int cligen_eval_wrap_fn_get(cligen_handle h, cligen_eval_wrap_fn **fn, void ** int cligen_tree_resolve_wrapper_set(cligen_handle h, cligen_tree_resolve_wrapper_fn *fn, void *arg); int cligen_tree_resolve_wrapper_get(cligen_handle h, cligen_tree_resolve_wrapper_fn **fn, void **arg); +void *cligen_ext_lib_handle_get(cligen_handle h); +int cligen_ext_lib_handle_set(cligen_handle h, void *data); + #endif /* _CLIGEN_HANDLE_H_ */ diff --git a/cligen_handle_internal.h b/cligen_handle_internal.h index 6ffff146..55340a71 100644 --- a/cligen_handle_internal.h +++ b/cligen_handle_internal.h @@ -116,6 +116,7 @@ struct cligen_handle{ void *ch_eval_wrap_arg; /* Argument to eval wrap function */ cligen_tree_resolve_wrapper_fn *ch_tree_resolve_wrapper_fn; /* Wrap function for treeref lookup */ void *ch_tree_resolve_wrapper_arg; /* Argument to treeref wrap function */ + void *ch_ext_lib_handle; /* Handle to dynamically loaded extension library (dlopen) */ }; #endif /* _CLIGEN_HANDLE_INTERNAL_H_ */ diff --git a/cligen_read.c b/cligen_read.c index 12cfb8d0..ead22ecd 100644 --- a/cligen_read.c +++ b/cligen_read.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef WIN32 #include #else @@ -281,22 +282,24 @@ show_help_columns(cligen_handle h, parse_tree *pt, cvec *cvv) { - int retval = -1; - int level; - int i; - int nrcmd = 0; - struct cligen_help *chvec = NULL; - struct cligen_help *ch; - cg_obj *co; - cbuf *cb = NULL; - char *cmd; - int maxlen = 0; - int column_width; - int column_nr; - int rest; - cvec *cvt = NULL; /* Tokenized string: vector of tokens */ - cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */ - match_result *mr = NULL; + int retval = -1; + int level; + int i; + int nrcmd = 0; + struct cligen_help *chvec = NULL; + struct cligen_help *ch; + cg_obj *co; + cbuf *cb = NULL; + char *cmd; + int maxlen = 0; + int column_width; + int column_nr; + int rest; + cvec *cvt = NULL; /* Tokenized string: vector of tokens */ + cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */ + match_result *mr = NULL; + void *lib_handle = NULL; + cligen_match_inject_fn *inject_func = NULL; if (string == NULL){ errno = EINVAL; @@ -315,6 +318,20 @@ show_help_columns(cligen_handle h, cvv, &mr) < 0) goto done; + + lib_handle = cligen_ext_lib_handle_get(h); + if (lib_handle) { + dlerror(); + inject_func = (cligen_match_inject_fn*)dlsym(lib_handle, "cligen_ext_match_inject"); + if (inject_func){ + if (inject_func(mr, cvt, string) < 0) { + goto done; + } + }else { + fprintf(stderr, "CLIgen extension error (dlsym): %s\n", dlerror()); + } + } + if ((level = cligen_cvv_levels(cvt)) < 0) goto done; if (mr_pt_len_get(mr) > 0){ /* min, max only defined if matchlen > 0 */ @@ -421,14 +438,16 @@ show_help_line(cligen_handle h, parse_tree *pt, cvec *cvv) { - int retval = -1; - int level; - cvec *cvt = NULL; /* Tokenized string: vector of tokens */ - cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */ - cg_var *cvlastt; /* Last element in cvt */ - cg_var *cvlastr; /* Last element in cvr */ - cligen_result result; - match_result *mr = NULL; + int retval = -1; + int level; + cvec *cvt = NULL; /* Tokenized string: vector of tokens */ + cvec *cvr = NULL; /* Rest variant, eg remaining string in each step */ + cg_var *cvlastt; /* Last element in cvt */ + cg_var *cvlastr; /* Last element in cvr */ + cligen_result result; + match_result *mr = NULL; + void *lib_handle = NULL; + cligen_match_inject_fn *inject_func = NULL; if (string == NULL){ errno = EINVAL; @@ -444,6 +463,19 @@ show_help_line(cligen_handle h, cvv, &mr) < 0) goto done; + + lib_handle = cligen_ext_lib_handle_get(h); + if (lib_handle) { + inject_func = (cligen_match_inject_fn*)dlsym(lib_handle, "cligen_ext_match_inject"); + if (inject_func){ + if (inject_func(mr, cvt, string) < 0) { + goto done; + } + }else { + fprintf(stderr, "CLIgen extension error (dlsym): %s\n", dlerror()); + } + } + if ((level = cligen_cvv_levels(cvt)) < 0) goto done; From 088f4242d8ceb3e24312ccdefbcf34e640535d70 Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Thu, 15 Jan 2026 17:08:48 +0200 Subject: [PATCH 2/2] Reorder includes in `cligen_handle.h` to follow coding conventions --- cligen_handle.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cligen_handle.h b/cligen_handle.h index ab3aefcd..5955dfd3 100644 --- a/cligen_handle.h +++ b/cligen_handle.h @@ -57,12 +57,13 @@ */ #define CLIGEN_TABMODE_STEPS 0x04 -#include "cligen_result.h" /* Dont show alternatives after completion * 0: Dont 1: show alternatives / help after completion */ #define CLIGEN_TABMODE_SHOW 0x08 +#include "cligen_result.h" + /* * Types */