From 472ea706c65a52f5579a22d706230fd69c0a6bfa Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Sat, 11 Apr 2020 10:49:07 +0200 Subject: [PATCH] Add keywordset module Add a utility module which can store a set of keywords. The set can be created and update from a string and can be serialized into a string. The string representation is a space-separated list of keywordis. Strings created by this utility contain the keywords stored in the set sorted in lexical order separated by a single space character. Input strings used to create or update a keyword set contain keywords separated by whitespace. Words from the string are added to the set unless a word starts with '-' in which case it will be removed from the Usage example: struct keywordset *kws = keywordset_new("xxx yyy") keywordset_update(kws, "-yyy aaaa") char *s = keywordset_get(kws): // s now "aaaaa xxx" free(s); // caller must free() if (keywordset_ismember(kws,"xxx")) ... // true keywordset_free(kws); --- keywordset.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ keywordset.h | 10 ++++ test_keywordset.c | 39 ++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 keywordset.c create mode 100644 keywordset.h create mode 100644 test_keywordset.c diff --git a/keywordset.c b/keywordset.c new file mode 100644 index 00000000..589acf65 --- /dev/null +++ b/keywordset.c @@ -0,0 +1,133 @@ +#include "keywordset.h" +#include +#include +#include +#include +#include "xmalloc.h" + +#define KEYWORDSET_INITIAL_SLOTS (4-2) + +struct keywordset { + int nr_slots; + int used; + char **names; +}; + +static int find_name(struct keywordset *kws, char *name, size_t len) { + int i; + int j; + for ( i = 0; i < kws->used ; i++ ) { + j = 0; + while(1) { + if (kws->names[i][j] == 0) + break; + if (kws->names[i][j] != name[j]) + break; + j++; + if (j==len) + return i; + } + } + return -1; +} + +static void expand(struct keywordset *kws) { + int new_slots=(kws->nr_slots+2)*2-2; + kws->names=realloc(kws->names,new_slots*sizeof(*kws->names)); + kws->nr_slots=new_slots; +} + +static void add_name(struct keywordset *kws, char *name, size_t len) { + int i=find_name(kws, name, len); + if (i>=0) { + free(kws->names[i]); + kws->names[i] = xstrndup(name, len); + } else { + if (kws->used == kws->nr_slots) + expand(kws); + kws->names[kws->used++] = xstrndup(name, len); + } +} + +static void remove_name(struct keywordset *kws, char *name, size_t len) { + int i=find_name(kws, name, len); + if (i>=0) { + free(kws->names[i]); + memmove(&(kws->names[i]), &(kws->names[i+1]), (kws->used-i-1)*sizeof(*kws->names)); + kws->used--; + } +} + +void keywordset_update(struct keywordset *kws, char *input) { + char *c=input; + char *name_start; + int add; + while (*c) { + while (*c && isspace(*c)) + c++; + if (*c == '-') { + add = 0; + c++; + } else + add = 1; + if (*c) { + name_start=c++; + while (*c && !isspace(*c)) + c++; + if (add) + add_name(kws, name_start, c-name_start); + else + remove_name(kws, name_start, c-name_start); + } + } +} + +struct keywordset *keywordset_new(char *input) { + struct keywordset *kws = xmalloc(sizeof(*kws)); + kws->nr_slots = KEYWORDSET_INITIAL_SLOTS; + kws->used = 0; + kws->names = xmalloc(KEYWORDSET_INITIAL_SLOTS*sizeof(*kws->names)); + if (input) + keywordset_update(kws, input); + return kws; +} + +static int cmp(const void *a, const void *b) { + return strcmp(*(char **)a, *(char **)b); +} + +char *keywordset_get(struct keywordset *kws) { + char **names=xmalloc(kws->used * sizeof(*names)); + memcpy(names, kws->names, kws->used * sizeof(*names)); + qsort(names, kws->used, sizeof(*names), cmp); + size_t len = 0; + int i; + for (i=0; iused; i++) { + len += strlen(names[i]); + } + size_t outlen = len + (kws->used >= 2 ? kws->used-1 : 0); + char *out=xmalloc(outlen + 1 ); + char *p=out; + for ( i = 0 ; i < kws->used ; i++) { + p=stpcpy(p, names[i]); + *p++ = ' '; + } + out[outlen] = 0; + free(names); + return(out); +} + +int keywordset_ismember(struct keywordset *kws, char *name) { + if (find_name(kws, name, strlen(name)) >= 0) + return 1; + else + return 0; +} + +void keywordset_free(struct keywordset *kws) { + int i; + for ( i = 0 ; i < kws->used ; i++) + free(kws->names[i]); + free(kws->names); + free(kws); +} diff --git a/keywordset.h b/keywordset.h new file mode 100644 index 00000000..f9964739 --- /dev/null +++ b/keywordset.h @@ -0,0 +1,10 @@ +#ifndef _KEYWORDSET_H +#define _KEYWORDSET_H + +struct keywordset *keywordset_new(char *input); +void keywordset_update(struct keywordset *kws, char *input); +char *keywordset_get(struct keywordset *kws); +int keywordset_ismember(struct keywordset *kwd, char *name); +void keywordset_free(struct keywordset *kws); + +#endif diff --git a/test_keywordset.c b/test_keywordset.c new file mode 100644 index 00000000..a7b7ded3 --- /dev/null +++ b/test_keywordset.c @@ -0,0 +1,39 @@ +#include +#include +#include "keywordset.h" +#include + +int main() { + struct keywordset *kws = keywordset_new("avaritia theinternet"); + char *s; + + keywordset_update(kws, "-avaritia deadbird null void xx XX"); + + s=keywordset_get(kws); + assert(strcmp(s, "XX deadbird null theinternet void xx")==0); + free(s); + + assert(keywordset_ismember(kws, "avaritia") == 0); + assert(keywordset_ismember(kws, "deadpool") == 0); + assert(keywordset_ismember(kws, "deadbird") == 1); + assert(keywordset_ismember(kws, "theinternet") == 1); + assert(keywordset_ismember(kws, "DEADBIRD") == 0); + assert(keywordset_ismember(kws, "xx") == 1); + assert(keywordset_ismember(kws, "XX") == 1); + + keywordset_free(kws); + + kws=keywordset_new(NULL); + s=keywordset_get(kws); + assert(strcmp(s, "")==0); + free(s); + keywordset_free(kws); + + kws=keywordset_new(""); + s=keywordset_get(kws); + assert(strcmp(s, "")==0); + free(s); + keywordset_free(kws); + + +}