From 61218df5d5c07fb3d76b8943f09b36e741954359 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Mon, 7 Nov 2022 13:08:02 +0100 Subject: [PATCH] tokens: Add low-level function to exlclude, prepend lists These functions are needed for openssh -,^ features. Signed-off-by: Norbert Pocs Reviewed-by: Jakub Jelen --- include/libssh/token.h | 5 ++ src/token.c | 145 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 144 insertions(+), 6 deletions(-) diff --git a/include/libssh/token.h b/include/libssh/token.h index 2cd7b20f..550ad792 100644 --- a/include/libssh/token.h +++ b/include/libssh/token.h @@ -49,6 +49,11 @@ char *ssh_remove_duplicates(const char *list); char *ssh_append_without_duplicates(const char *list, const char *appended_list); +char *ssh_prefix_without_duplicates(const char *list, + const char *prefixed_list); +char *ssh_remove_all_matching(const char *list, + const char *remove_list); + #ifdef __cplusplus } #endif diff --git a/src/token.c b/src/token.c index 0924d3bd..2e26c562 100644 --- a/src/token.c +++ b/src/token.c @@ -376,6 +376,7 @@ char *ssh_append_without_duplicates(const char *list, { size_t concat_len = 0; char *ret = NULL, *concat = NULL; + int rc = 0; if (list != NULL) { concat_len = strlen(list); @@ -396,12 +397,144 @@ char *ssh_append_without_duplicates(const char *list, return NULL; } - if (list != NULL) { - strcpy(concat, list); - strncat(concat, ",", concat_len - strlen(concat) - 1); - } - if (appended_list != NULL) { - strncat(concat, appended_list, concat_len - strlen(concat) - 1); + rc = snprintf(concat, concat_len, "%s%s%s", + list == NULL ? "" : list, + list == NULL ? "" : ",", + appended_list == NULL ? "" : appended_list); + if (rc < 0) { + SAFE_FREE(concat); + return NULL; + } + + ret = ssh_remove_duplicates(concat); + + SAFE_FREE(concat); + + return ret; +} + +/** + * @internal + * + * @brief Given two strings containing lists of tokens, return a newly + * allocated string containing the elements of the first list without the + * elements of the second list. The order of the elements will be preserved. + * + * @param[in] list The first list + * @param[in] remove_list The list to be removed + * + * @return A newly allocated copy list containing elements of the + * list without the elements of remove_list; NULL in case of error. + */ +char *ssh_remove_all_matching(const char *list, + const char *remove_list) +{ + struct ssh_tokens_st *l_tok = NULL, *r_tok = NULL; + int i, j, cmp; + char *ret = NULL; + size_t len, pos = 0; + bool exclude; + + if ((list == NULL)) { + return NULL; + } + if (remove_list == NULL) { + return strdup (list); + } + + l_tok = ssh_tokenize(list, ','); + if (l_tok == NULL) { + goto out; + } + + r_tok = ssh_tokenize(remove_list, ','); + if (r_tok == NULL) { + goto out; + } + + ret = calloc(1, strlen(list) + 1); + if (ret == NULL) { + return NULL; + } + + for (i = 0; l_tok->tokens[i]; i++) { + exclude = false; + for (j = 0; r_tok->tokens[j]; j++) { + cmp = strcmp(l_tok->tokens[i], r_tok->tokens[j]); + if (cmp == 0) { + exclude = true; + break; + } + } + if (exclude == false) { + if (pos != 0) { + ret[pos] = ','; + pos++; + } + + len = strlen(l_tok->tokens[i]); + memcpy(&ret[pos], l_tok->tokens[i], len); + pos += len; + } + } + + if (ret[0] == '\0') { + SAFE_FREE(ret); + } + +out: + ssh_tokens_free(l_tok); + ssh_tokens_free(r_tok); + return ret; +} + +/** + * @internal + * + * @brief Given two strings containing lists of tokens, return a newly + * allocated string containing all the elements of the first list prefixed at + * the beginning of the second list, without duplicates. + * + * @param[in] list The first list + * @param[in] prefixed_list The list to use as a prefix + * + * @return A newly allocated list containing all the elements + * of the list prefixed with the elements of the prefixed_list without + * duplicates; NULL in case of error. + */ +char *ssh_prefix_without_duplicates(const char *list, + const char *prefixed_list) +{ + size_t concat_len = 0; + char *ret = NULL, *concat = NULL; + int rc = 0; + + if (list != NULL) { + concat_len = strlen(list); + } + + if (prefixed_list != NULL) { + concat_len += strlen(prefixed_list); + } + + if (concat_len == 0) { + return NULL; + } + + /* Add room for ending '\0' and for middle ',' */ + concat_len += 2; + concat = calloc(concat_len, 1); + if (concat == NULL) { + return NULL; + } + + rc = snprintf(concat, concat_len, "%s%s%s", + prefixed_list == NULL ? "" : prefixed_list, + prefixed_list == NULL ? "" : ",", + list == NULL ? "" : list); + if (rc < 0) { + SAFE_FREE(concat); + return NULL; } ret = ssh_remove_duplicates(concat);