lib: add string utilities

I see lots of the same code being copy-pasted and slightly tweaked for
string processing all over the codebase. Time to start aggregating these
pieces into something consistent and correct.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2018-05-11 15:32:06 -04:00
parent a86dc99646
commit fe011935cd
8 changed files with 286 additions and 84 deletions

View File

@ -25,17 +25,17 @@
*/ */
#include <zebra.h> #include <zebra.h>
#include <lib/version.h>
#include "command.h"
#include "frrstr.h"
#include "memory.h" #include "memory.h"
#include "log.h" #include "log.h"
#include "log_int.h" #include "log_int.h"
#include <lib/version.h>
#include "thread.h" #include "thread.h"
#include "vector.h" #include "vector.h"
#include "linklist.h" #include "linklist.h"
#include "vty.h" #include "vty.h"
#include "command.h"
#include "workqueue.h" #include "workqueue.h"
#include "vrf.h" #include "vrf.h"
#include "command_match.h" #include "command_match.h"
@ -46,7 +46,6 @@
#include "jhash.h" #include "jhash.h"
DEFINE_MTYPE(LIB, HOST, "Host config") DEFINE_MTYPE(LIB, HOST, "Host config")
DEFINE_MTYPE(LIB, STRVEC, "String vector")
DEFINE_MTYPE(LIB, COMPLETION, "Completion item") DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
#define item(x) \ #define item(x) \
@ -259,30 +258,46 @@ void print_version(const char *progname)
printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS); printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
} }
/* Utility function to concatenate argv argument into a single string
with inserting ' ' character between each argument. */
char *argv_concat(struct cmd_token **argv, int argc, int shift) char *argv_concat(struct cmd_token **argv, int argc, int shift)
{ {
int i; int cnt = argc - shift;
size_t len; const char *argstr[cnt];
char *str;
char *p;
len = 0; for (int i = 0; i < cnt; i++)
for (i = shift; i < argc; i++) argstr[i] = argv[i + shift]->arg;
len += strlen(argv[i]->arg) + 1;
if (!len) return frrstr_join(argstr, cnt, " ");
}
vector cmd_make_strvec(const char *string)
{
if (!string)
return NULL; return NULL;
p = str = XMALLOC(MTYPE_TMP, len);
for (i = shift; i < argc; i++) { const char *copy = string;
size_t arglen;
memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg))); /* skip leading whitespace */
p += arglen; while (isspace((int)*copy) && *copy != '\0')
*p++ = ' '; copy++;
/* if the entire string was whitespace or a comment, return */
if (*copy == '\0' || *copy == '!' || *copy == '#')
return NULL;
vector result = frrstr_split_vec(copy, " \n\r\t");
for (unsigned int i = 0; i < vector_active(result); i++) {
if (strlen(vector_slot(result, i)) == 0) {
XFREE(MTYPE_TMP, vector_slot(result, i));
vector_unset(result, i);
}
} }
*(p - 1) = '\0'; return result;
return str; }
void cmd_free_strvec(vector v)
{
frrstr_strvec_free(v);
} }
/** /**
@ -332,61 +347,6 @@ void install_node(struct cmd_node *node, int (*func)(struct vty *))
"Command Hash"); "Command Hash");
} }
/**
* Tokenizes a string, storing tokens in a vector.
* Whitespace is ignored.
*
* Delimiter string = " \n\r\t".
*
* @param string to tokenize
* @return tokenized string
*/
vector cmd_make_strvec(const char *string)
{
if (!string)
return NULL;
char *copy, *copystart;
copystart = copy = XSTRDUP(MTYPE_TMP, string);
// skip leading whitespace
while (isspace((int)*copy) && *copy != '\0')
copy++;
// if the entire string was whitespace or a comment, return
if (*copy == '\0' || *copy == '!' || *copy == '#') {
XFREE(MTYPE_TMP, copystart);
return NULL;
}
vector strvec = vector_init(VECTOR_MIN_SIZE);
const char *delim = " \n\r\t", *tok = NULL;
while (copy) {
tok = strsep(&copy, delim);
if (*tok != '\0')
vector_set(strvec, XSTRDUP(MTYPE_STRVEC, tok));
}
XFREE(MTYPE_TMP, copystart);
return strvec;
}
/* Free allocated string vector. */
void cmd_free_strvec(vector v)
{
unsigned int i;
char *cp;
if (!v)
return;
for (i = 0; i < vector_active(v); i++)
if ((cp = vector_slot(v, i)) != NULL)
XFREE(MTYPE_STRVEC, cp);
vector_free(v);
}
/* Return prompt character of specified node. */ /* Return prompt character of specified node. */
const char *cmd_prompt(enum node_type node) const char *cmd_prompt(enum node_type node)
{ {

View File

@ -33,9 +33,6 @@
DECLARE_MTYPE(HOST) DECLARE_MTYPE(HOST)
DECLARE_MTYPE(COMPLETION) DECLARE_MTYPE(COMPLETION)
/* for test-commands.c */
DECLARE_MTYPE(STRVEC)
/* Host configuration variable */ /* Host configuration variable */
struct host { struct host {
/* Host name of this router. */ /* Host name of this router. */

141
lib/frrstr.c Normal file
View File

@ -0,0 +1,141 @@
/*
* FRR string processing utilities.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include "frrstr.h"
#include "memory.h"
#include "vector.h"
void frrstr_split(const char *string, const char *delimiter, char ***result,
int *argc)
{
if (!string)
return;
unsigned int sz = 4, idx = 0;
char *copy, *copystart;
*result = XCALLOC(MTYPE_TMP, sizeof(char *) * sz);
copystart = copy = XSTRDUP(MTYPE_TMP, string);
*argc = 0;
const char *tok = NULL;
while (copy) {
tok = strsep(&copy, delimiter);
(*result)[idx] = XSTRDUP(MTYPE_TMP, tok);
if (++idx == sz)
*result = XREALLOC(MTYPE_TMP, *result,
(sz *= 2) * sizeof(char *));
(*argc)++;
}
XFREE(MTYPE_TMP, copystart);
return;
}
vector frrstr_split_vec(const char *string, const char *delimiter)
{
char **result;
int argc;
frrstr_split(string, delimiter, &result, &argc);
vector v = array_to_vector((void **)result, argc);
XFREE(MTYPE_TMP, result);
return v;
}
char *frrstr_join(const char **parts, int argc, const char *join)
{
int i;
char *str;
char *p;
size_t len = 0;
size_t joinlen = join ? strlen(join) : 0;
for (i = 0; i < argc; i++)
len += strlen(parts[i]);
len += argc * joinlen + 1;
if (!len)
return NULL;
p = str = XMALLOC(MTYPE_TMP, len);
for (i = 0; i < argc; i++) {
size_t arglen = strlen(parts[i]);
memcpy(p, parts[i], arglen);
p += arglen;
if (i + 1 != argc) {
memcpy(p, join, joinlen);
p += joinlen;
}
}
*p = '\0';
return str;
}
char *frrstr_join_vec(vector v, const char *join)
{
char **argv;
int argc;
vector_to_array(v, (void ***)&argv, &argc);
char *ret = frrstr_join((const char **)argv, argc, join);
XFREE(MTYPE_TMP, argv);
return ret;
}
void frrstr_filter_vec(vector v, regex_t *filter)
{
regmatch_t ignored[1];
for (unsigned int i = 0; i < vector_active(v); i++) {
if (regexec(filter, vector_slot(v, i), 0, ignored, 0)) {
XFREE(MTYPE_TMP, vector_slot(v, i));
vector_unset(v, i);
}
}
}
void frrstr_strvec_free(vector v)
{
unsigned int i;
char *cp;
if (!v)
return;
for (i = 0; i < vector_active(v); i++) {
cp = vector_slot(v, i);
XFREE(MTYPE_TMP, cp);
}
vector_free(v);
}

86
lib/frrstr.h Normal file
View File

@ -0,0 +1,86 @@
/*
* FRR string processing utilities.
* Copyright (C) 2018 Cumulus Networks, Inc.
* Quentin Young
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _FRRSTR_H_
#define _FRRSTR_H_
#include <sys/types.h>
#include <regex.h>
#include "vector.h"
/*
* Tokenizes a string, storing tokens in a vector. Whitespace is ignored.
* Delimiter characters are not included.
*
* string
* The string to split
*
* delimiter
* Delimiter string, as used in strsep()
*
* Returns:
* The split string. Each token is allocated with MTYPE_TMP.
*/
void frrstr_split(const char *string, const char *delimiter, char ***result,
int *argc);
vector frrstr_split_vec(const char *string, const char *delimiter);
/*
* Concatenate string array into a single string.
*
* argv
* array of string pointers to concatenate
*
* argc
* array length
*
* join
* string to insert between each part, or NULL for nothing
*
* Returns:
* the joined string, allocated with MTYPE_TMP
*/
char *frrstr_join(const char **parts, int argc, const char *join);
char *frrstr_join_vec(vector v, const char *join);
/*
* Filter string vector.
* Removes lines that do not contain a match for the provided regex.
*
* v
* The vector to filter.
*
* filter
* Regex to filter with.
*/
void frrstr_filter_vec(vector v, regex_t *filter);
/*
* Free allocated string vector.
* Assumes each item is allocated with MTYPE_TMP.
*
* v
* the vector to free
*/
void frrstr_strvec_free(vector v);
#endif /* _FRRSTR_H_ */

View File

@ -21,6 +21,7 @@ lib_libfrr_la_SOURCES = \
lib/ferr.c \ lib/ferr.c \
lib/filter.c \ lib/filter.c \
lib/frr_pthread.c \ lib/frr_pthread.c \
lib/frrstr.c \
lib/getopt.c \ lib/getopt.c \
lib/getopt1.c \ lib/getopt1.c \
lib/grammar_sandbox.c \ lib/grammar_sandbox.c \
@ -105,6 +106,7 @@ pkginclude_HEADERS += \
lib/freebsd-queue.h \ lib/freebsd-queue.h \
lib/frr_pthread.h \ lib/frr_pthread.h \
lib/frratomic.h \ lib/frratomic.h \
lib/frrstr.h \
lib/getopt.h \ lib/getopt.h \
lib/graph.h \ lib/graph.h \
lib/hash.h \ lib/hash.h \

View File

@ -181,3 +181,18 @@ unsigned int vector_count(vector v)
return count; return count;
} }
void vector_to_array(vector v, void ***dest, int *argc)
{
*dest = XCALLOC(MTYPE_TMP, sizeof(void *) * v->active);
memcpy(*dest, v->index, sizeof(void *) * v->active);
*argc = v->active;
}
vector array_to_vector(void **src, int argc)
{
vector v = vector_init(VECTOR_MIN_SIZE);
for (int i = 0; i < argc; i++)
vector_set_index(v, i, src[i]);
return v;
}

View File

@ -59,5 +59,6 @@ extern vector vector_copy(vector v);
extern void *vector_lookup(vector, unsigned int); extern void *vector_lookup(vector, unsigned int);
extern void *vector_lookup_ensure(vector, unsigned int); extern void *vector_lookup_ensure(vector, unsigned int);
extern void vector_to_array(vector v, void ***dest, int *argc);
extern vector array_to_vector(void **src, int argc);
#endif /* _ZEBRA_VECTOR_H */ #endif /* _ZEBRA_VECTOR_H */

View File

@ -130,7 +130,7 @@ static void test_load(void)
line[strlen(line) - 1] = '\0'; line[strlen(line) - 1] = '\0';
if (line[0] == '#') if (line[0] == '#')
continue; continue;
vector_set(test_cmds, XSTRDUP(MTYPE_STRVEC, line)); vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line));
} }
} }
@ -181,7 +181,7 @@ static void test_terminate(void)
vty_terminate(); vty_terminate();
for (i = 0; i < vector_active(test_cmds); i++) for (i = 0; i < vector_active(test_cmds); i++)
XFREE(MTYPE_STRVEC, vector_slot(test_cmds, i)); XFREE(MTYPE_TMP, vector_slot(test_cmds, i));
vector_free(test_cmds); vector_free(test_cmds);
cmd_terminate(); cmd_terminate();
} }