Skip to content

Commit

Permalink
Remove auto-escaping of expanded strings
Browse files Browse the repository at this point in the history
Remove co_value field
  • Loading branch information
olofhagsand committed Nov 8, 2024
1 parent 22a2ecb commit ff6a394
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 150 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Cligen Changelog

* [7.3.0](#730) Expected: January 2025
* [7.2.0](#720) 28 October 2024
* [7.1.0](#710) 3 July 2024
* [7.0.0](#770) 8 March 2024
Expand All @@ -10,6 +11,15 @@
* [6.1.0](#610) 19 Feb 2023
* [6.0.0](#600) 29 Nov 2022

## 7.3.0
Expected: January 2025

### Corrected Bugs

* Fixed: [Expansion removes the double quote](https://github.com/clicon/clixon/issues/524)
* Remove auto-escaping of expanded nodes
* (Internal:) remove co_value and storing non-escaped command

## 7.2.0
28 October 2024

Expand Down
166 changes: 87 additions & 79 deletions cligen_expand.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,61 +411,90 @@ co_expand_treeref_copy_shallow(cligen_handle h,
return retval;
}

/*! Escape special characters in a string for its usage as CLI keyword.
/*! Count length of escaped string
*
* If no escaping is required, return original string.
* Otherwise, allocate a new string for escaped result.
* @param[in] s Original string.
* @retval e Escaped string, malloced, must be freed
* @retval NULL Error
* @param[in] s Original string.
* @retval len Length of string after escaping
*/
static int
cligen_escape_nr(const char *s)
{
int nr = 0;
const char *sp;

sp = s;
while ((sp = strpbrk(sp, "?\\ \t"))) {
if (nr == 0)
nr = 2; /* escapes */
if (!isspace(*sp)) {
nr++;
}
sp++;
}
return nr;
}

/*! Check if string needs escaping
*
**/
static const char*
cligen_escape(const char* s)
* @param[in] s Original string.
* @retval 1 Yes needs escaping
* @retval 0 No does not need escaping
* @retval -1 Error
*/
int
cligen_escape_need(const char *s)
{
char *copy;
size_t len;
int chars_to_escape = 0;
const char *spec;
int i, j;
int nr = 0;

if (s == NULL){
errno = EINVAL;
return NULL;
}
spec = s;
// while ((spec = strpbrk(spec, "?\\"))) {
while ((spec = strpbrk(spec, "?\\ \t"))) {
if (chars_to_escape == 0) {
chars_to_escape = 2; /* escapes */
}
if (!isspace(*spec)) {
chars_to_escape++;
}
spec++;
}

if (!chars_to_escape) {
return s;
return -1;
}

nr = cligen_escape_nr(s);
if (nr == 0)
return 0;
len = strlen(s);
if (len > 1 && s[0] == '"' && s[len-1] == '"')
return 0;
return 1;
}

/*! Escape string
*
* @param[in] s0 Original string.
* @retval s1 New malloced string
* @retval NULL Error does not need escaping
*/
char *
cligen_escape_do(const char *s0)
{
char *s1 = NULL;
size_t len;
int nr = 0;
int i;
int j;

if ((copy = (char*)malloc(len + 1 + chars_to_escape)) == NULL){
if (s0 == NULL){
errno = EINVAL;
return NULL;
}

copy[0] = '"';
for (i = 0, j = 1; i < len; i++, j++) {
if ((s[i] == '?') || (s[i] == '\\')) {
copy[j++] = '\\';
len = strlen(s0);
nr = cligen_escape_nr(s0);
if ((s1 = (char*)malloc(len + 1 + nr)) == NULL){
return NULL;
}
j = 0;
s1[j++] = '"';
for (i = 0; i < len; i++, j++) {
if ((s0[i] == '?') || (s0[i] == '\\')) {
s1[j++] = '\\';
}
copy[j] = s[i];
s1[j] = s0[i];
}
copy[j++] = '"';
copy[j] = '\0';

return copy;
s1[j++] = '"';
s1[j] = '\0';
return s1;
}

/*! Call expand callback and insert expanded commands in place of variable
Expand All @@ -485,14 +514,14 @@ cligen_escape(const char* s)
* @see cligen_eval where cligen callbacks are invoked
*/
static int
pt_expand_fnv(cligen_handle h,
cg_obj *co,
cg_obj *co_parent,
cvec *cvv_var,
cvec *cvv_filter,
cg_callback *callbacks,
int transient,
parse_tree *ptn)
pt_expand_fn(cligen_handle h,
cg_obj *co,
cg_obj *co_parent,
cvec *cvv_var,
cvec *cvv_filter,
cg_callback *callbacks,
int transient,
parse_tree *ptn)
{
int retval = -1;
cvec *commands = NULL;
Expand Down Expand Up @@ -522,6 +551,7 @@ pt_expand_fnv(cligen_handle h,
if (callbacks && callbacks->cc_cvec){
cligen_callback_arguments_set(h, callbacks->cc_cvec);
}
cligen_co_match_set(h, co); /* For eventual use in callback */
if ((*co->co_expandv_fn)(cligen_userhandle(h)?cligen_userhandle(h):h,
co->co_expand_fn_str,
cvv1,
Expand Down Expand Up @@ -550,21 +580,14 @@ pt_expand_fnv(cligen_handle h,
if (co_filter_set(con, cvv_filter) == NULL)
goto done;
value = cv_string_get(cv);
if ((cmd = cligen_escape(value)) == NULL)
if ((cmd = strdup(value)) == NULL)
goto done;
if (cmd == value) {
if ((cmd = strdup(cmd)) == NULL)
goto done;
}
/* 'cmd' always points to mutable string, helpstr is consumed */
if (transform_var_to_cmd(con, (char*)cmd, helpstr) < 0){
helpstr = NULL;
goto done;
}
helpstr = NULL;
/* Save the unescaped string */
if (cmd != value)
co_value_set(con, (char*)value);
}
retval = 0;
done:
Expand Down Expand Up @@ -676,8 +699,6 @@ pt_expand1_co(cligen_handle h,
cg_obj *con = NULL;
char *label;

if (co_value_set(co, NULL) < 0)
goto done;
if (hide && co_flags_get(co, CO_FLAGS_HIDE))
goto ok;
/* Loop labels from object itself and see if any of the elements are filtered, if so skip it
Expand Down Expand Up @@ -714,12 +735,12 @@ pt_expand1_co(cligen_handle h,
* this iteration and if not add it?
*/
if (expandvar){
if (pt_expand_fnv(h, co, NULL,
cvv_var,
cvv_filter,
callbacks,
transient,
ptn) < 0)
if (pt_expand_fn(h, co, NULL,
cvv_var,
cvv_filter,
callbacks,
transient,
ptn) < 0)
goto done;
}
}
Expand Down Expand Up @@ -947,7 +968,6 @@ pt_expand(cligen_handle h,

/*! Go through tree and clean & delete all extra memory from pt_expand and pt_expand_treeref
*
* More specifically, delete all co_values and co_pt_exp and expanded subtrees co_ref
* @param[in] h CLIgen handle
* @param[in] pt Parsetree
* @retval 0 OK
Expand All @@ -958,19 +978,7 @@ int
pt_expand_cleanup(cligen_handle h,
parse_tree *pt)
{
int retval = -1;
int i;
cg_obj *co;

for (i=0; i<pt_len_get(pt); i++){
if ((co = pt_vec_i_get(pt, i)) != NULL){
if (co_value_set(co, NULL) < 0)
goto done;
}
}
retval = 0;
done:
return retval;
return 0;
}

/*! Return object in original tree from object in a referenced tree
Expand Down
2 changes: 2 additions & 0 deletions cligen_expand.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
/*
* Prototypes
*/
int cligen_escape_need(const char *s);
char *cligen_escape_do(const char *s);
int co_isfilter(cvec *cvv_filter, char *label);
char *pt_local_pipe(parse_tree *pt);
int pt_expand(cligen_handle h, cg_obj *co, parse_tree *pt, cvec *cvt, cvec *cvv,
Expand Down
8 changes: 5 additions & 3 deletions cligen_handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,8 @@ cligen_treename_keyword_set(cligen_handle h,

/*! Return CLIgen object that matched in the current callback.
*
* After an evaluation when calling a callback, a node has been matched in the
* current parse-tree. This matching node is returned (and set) here.
* After an evaluation when calling a callback, a node has been matched in the
* current parse-tree. This matching node is returned here.
* @param[in] h CLIgen handle
*/
cg_obj *
Expand All @@ -413,7 +413,9 @@ cligen_co_match(cligen_handle h)
return ch->ch_co_match;
}

/*!
/*! Set CLIgen object to use in the current callback.
*
* It can be useful for callbacks to know which cligen-object is matched
* @param[in] h CLIgen handle
*/
int
Expand Down
34 changes: 6 additions & 28 deletions cligen_match.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,16 +500,13 @@ match_bindvars(cligen_handle h,
/* co_orig is original object in case of expansion */
if ((co_orig = co->co_treeref_orig) == NULL)
co_orig = co->co_ref;
if (co->co_type == CO_VARIABLE){
/* Once so translate only is done once */
if ((cv = add_cov_to_cvec(h, co, token, cvv)) == NULL)
if (co->co_type == CO_VARIABLE){
if ((cv = add_cov_to_cvec(h, co, token, cvv)) == NULL) // <-- 1
goto done;
}
else if (co->co_type == CO_COMMAND && co_orig->co_type == CO_VARIABLE){
/* Once so translate only is done once */
if ((cv = add_cov_to_cvec(h, co_orig,
co->co_value?co->co_value:co->co_command,
cvv)) == NULL)
if ((cv = add_cov_to_cvec(h, co_orig, token, cvv)) == NULL)
goto done;
}
else{
Expand Down Expand Up @@ -560,7 +557,6 @@ match_pattern_sets_local(cligen_handle h,
{
int retval = -1;
cg_obj *co_match = NULL;
cg_obj *co_orig = NULL;
int lasttoken = 0;
char *token;
char *resttokens;
Expand Down Expand Up @@ -619,8 +615,6 @@ match_pattern_sets_local(cligen_handle h,
}
/* Get the single match object */
co_match = mr_pt_i_get(mr0, 0);
/* co_orig is original object in case of expansion */
co_orig = co_match->co_ref?co_match->co_ref: co_match;

/* Already matched (sets functionality) */
if (co_flags_get(co_match, CO_FLAGS_MATCH)){ /* XXX: orig?? */
Expand Down Expand Up @@ -650,21 +644,6 @@ match_pattern_sets_local(cligen_handle h,
mr_last_set(mr0); /* dont go to children */
}
ok:
/* mr0:local or mrc:child
* if mrc has result, take that, otherwise take mr0
*/
switch (mr_pt_len_get(mr0)) {
case 0:
break;
case 1:
if (co_match->co_type == CO_COMMAND &&
co_orig && co_orig->co_type == CO_VARIABLE)
if (co_value_set(co_orig, co_match->co_command) < 0)
goto done;
break;
default:
break;
} /* matches */
*mrp = mr0;
mr0 = NULL;
retval = 0;
Expand Down Expand Up @@ -844,8 +823,7 @@ match_pattern_sets(cligen_handle h,
pt_print(stderr, pt);
#endif
/* Match the current token */
if (match_pattern_sets_local(h, cvt, cvr, pt, level, best,
cvv, &mr0) < 0)
if (match_pattern_sets_local(h, cvt, cvr, pt, level, best, cvv, &mr0) < 0)
goto done;
#ifdef _DEBUG_SETS
fprintf(stderr, "%s %*s matchnr:%d\n", __FUNCTION__, level*3,"", mr_pt_len_get(mr0));
Expand Down Expand Up @@ -1347,15 +1325,15 @@ match_complete(cligen_handle h,
if (co->co_type != CO_COMMAND)
continue;
}
command = co->co_value?co->co_value:co->co_command;
command = co->co_command;
if (co1 == NULL){
slen = strlen(mr_token_get(mr));
minmatch = strlen(command);
co1 = co;
command1 = command;
}
else{
command1 = co1->co_value?co1->co_value:co1->co_command;
command1 = co1->co_command;
if (!cligen_caseignore_get(h) && strcmp(command1, command)==0)
; /* equal */
else if (cligen_caseignore_get(h) && strcasecmp(command1, command)==0)
Expand Down
Loading

0 comments on commit ff6a394

Please sign in to comment.