diff options
Diffstat (limited to 'src/lib/core')
| -rw-r--r-- | src/lib/core/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/lib/core/include/strlist.h | 6 | ||||
| -rw-r--r-- | src/lib/core/include/version_compare.h | 20 | ||||
| -rw-r--r-- | src/lib/core/strlist.c | 59 | ||||
| -rw-r--r-- | src/lib/core/utils.c | 39 | ||||
| -rw-r--r-- | src/lib/core/version_compare.c | 185 |
6 files changed, 269 insertions, 41 deletions
diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index 0fb273c..462d7d8 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(stasis_core STATIC envctl.c multiprocessing.c semaphore.c + version_compare.c ) target_include_directories(stasis_core PRIVATE ${core_INCLUDE} diff --git a/src/lib/core/include/strlist.h b/src/lib/core/include/strlist.h index b2d7da7..f44025c 100644 --- a/src/lib/core/include/strlist.h +++ b/src/lib/core/include/strlist.h @@ -45,8 +45,10 @@ int strlist_append_file(struct StrList *pStrList, char *path, ReaderFn *readerFn void strlist_append_strlist(struct StrList *pStrList1, struct StrList *pStrList2); void strlist_append(struct StrList **pStrList, char *str); void strlist_append_array(struct StrList *pStrList, char **arr); -void strlist_append_tokenize(struct StrList *pStrList, char *str, char *delim); -void strlist_append_tokenize_raw(struct StrList *pStrList, char *str, char *delim); + +int strlist_append_tokenize(struct StrList *pStrList, char *str, char *delim); + +int strlist_append_tokenize_raw(struct StrList *pStrList, char *str, char *delim); int strlist_appendf(struct StrList **pStrList, const char *fmt, ...); struct StrList *strlist_copy(struct StrList *pStrList); int strlist_cmp(struct StrList *a, struct StrList *b); diff --git a/src/lib/core/include/version_compare.h b/src/lib/core/include/version_compare.h new file mode 100644 index 0000000..857de47 --- /dev/null +++ b/src/lib/core/include/version_compare.h @@ -0,0 +1,20 @@ +#ifndef STASIS_VERSION_COMPARE_H +#define STASIS_VERSION_COMPARE_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "str.h" + +#define GT 1 << 1 +#define LT 1 << 2 +#define EQ 1 << 3 +#define NOT 1 << 4 +#define EPOCH_MOD 10000 + +int version_sum(const char *str); +int version_parse_operator(const char *str); +int version_compare(int flags, const char *aa, const char *bb); + +#endif //STASIS_VERSION_COMPARE_H
\ No newline at end of file diff --git a/src/lib/core/strlist.c b/src/lib/core/strlist.c index 42d5b85..0d25a66 100644 --- a/src/lib/core/strlist.c +++ b/src/lib/core/strlist.c @@ -218,22 +218,30 @@ void strlist_append_strlist(struct StrList *pStrList1, struct StrList *pStrList2 * @param str * @param delim */ - void strlist_append_tokenize(struct StrList *pStrList, char *str, char *delim) { - if (!str || !delim) { - return; - } +int strlist_append_tokenize(struct StrList *pStrList, char *str, char *delim) { + if (!str || !delim) { + return -1; + } - char *tmp = strdup(str); - char **token = split(tmp, delim, 0); - if (token) { - for (size_t i = 0; token[i] != NULL; i++) { - lstrip(token[i]); - strlist_append(&pStrList, token[i]); - } - guard_array_free(token); - } + char *tmp = strdup(str); + if (!tmp) { + return -1; + } + + char **token = split(tmp, delim, 0); + if (!token) { + guard_free(tmp); + return -1; + } + for (size_t i = 0; token[i] != NULL; i++) { + lstrip(token[i]); + strlist_append(&pStrList, token[i]); + } + guard_array_free(token); guard_free(tmp); - } + + return 0; +} /** * Append the contents of a newline delimited string without @@ -242,20 +250,29 @@ void strlist_append_strlist(struct StrList *pStrList1, struct StrList *pStrList2 * @param str * @param delim */ -void strlist_append_tokenize_raw(struct StrList *pStrList, char *str, char *delim) { +int strlist_append_tokenize_raw(struct StrList *pStrList, char *str, char *delim) { if (!str || !delim) { - return; + return -1; } char *tmp = strdup(str); + if (!tmp) { + return -1; + } + char **token = split(tmp, delim, 0); - if (token) { - for (size_t i = 0; token[i] != NULL; i++) { - strlist_append(&pStrList, token[i]); - } - guard_array_free(token); + if (!token) { + guard_free(tmp); + return -1; } + + for (size_t i = 0; token[i] != NULL; i++) { + strlist_append(&pStrList, token[i]); + } + + guard_array_free(token); guard_free(tmp); + return 0; } /** diff --git a/src/lib/core/utils.c b/src/lib/core/utils.c index 90dac1d..e6a8315 100644 --- a/src/lib/core/utils.c +++ b/src/lib/core/utils.c @@ -742,29 +742,32 @@ int fix_tox_conf(const char *filename, char **result, size_t maxlen) { return 0; } -static size_t count_blanks(char *s) { - // return the number of leading blanks (tab/space) in a string - size_t blank = 0; - for (size_t i = 0; i < strlen(s); i++) { - if (isblank(s[i])) { - blank++; +/** + * Collapse all whitespace in a string (to single spaces) + * @param s address of string to modify + * @return + */ +char *collapse_whitespace(char **s) { + char *dest = NULL; + char *src = NULL; + int in_ws = 1; + + for (src = dest = *s; *src != '\0'; ++src) { + if (isspace(*src)) { + if (!in_ws) { + *dest++ = ' '; + in_ws = 1; + } } else { - break; + *dest++ = *src; + in_ws = 0; } } - return blank; -} -char *collapse_whitespace(char **s) { - char *x = (*s); - size_t len = strlen(x); - for (size_t i = 0; i < len; i++) { - size_t blank = count_blanks(&x[i]); - if (blank > 1) { - memmove(&x[i], &x[i] + blank, strlen(&x[i])); - } + if (dest > *s && *(dest - 1) == ' ') { + --dest; } - + *dest = '\0'; return *s; } diff --git a/src/lib/core/version_compare.c b/src/lib/core/version_compare.c new file mode 100644 index 0000000..4939c8f --- /dev/null +++ b/src/lib/core/version_compare.c @@ -0,0 +1,185 @@ +#include "version_compare.h" + +const struct { + const char *key; + int value; +} WEIGHT[] = { + {.key = "post", 1000}, + {.key = "rc", -1000}, + {.key = "dev", -2000}, +}; + +/** + * Sum each part of a '.'-delimited version string + * @param str version string + * @return sum of each part + * @return -1 on error + */ +int version_sum(const char *str) { + char *end; + + if (!str || isempty((char *) str)) { + return -1; + } + + int result = 0; + int epoch = 0; + char *s = strdup(str); + if (!s) { + return -1; + } + char *ptr = s; + end = ptr; + + // Parsing stops at the first non-alpha, non-'.' character + // Digits are processed until the first invalid character + // I'm torn whether this should be considered an error + int i = 0; + while (end != NULL) { + int tmp_result = 0; + + tmp_result = (int) strtoul(ptr, &end, 10); + + // Circumvent a bug which allows a smaller version to be greater + // than a larger version + // Bug: + // 1.0.3 == 1 + 0 + 3 = 4 + // 2.0.0 == 2 + 0 + 0 = 2 + // Correction: + // ((1 * EPOCH_MOD) + 1).0.3 = 104 + // ((2 * EPOCH_MOD) + 2).0.0 = 202 + if (!i && tmp_result && *end != ':') { + result += tmp_result * EPOCH_MOD; + i++; + } + + ptr = end; + if (*ptr == '.' || *ptr == '-') { + ptr++; + } + else if (!epoch && *ptr == ':') { + epoch = 1; + result += EPOCH_MOD; + ptr++; + } + else if (isalpha(*ptr)) { + int adjusted = 0; + for (size_t w = 0; w < sizeof(WEIGHT) / sizeof(WEIGHT[0]); w++) { + const int has_suffix = strncasecmp(ptr, WEIGHT[w].key, strlen(WEIGHT[w].key)) == 0; + if (has_suffix) { + // skip the suffix + ptr += strlen(WEIGHT[w].key); + // adjust result based on suffix weight + result += WEIGHT[w].value; + adjusted = 1; + break; + } + } + + if (!adjusted) { + result += *ptr - ('a' - 1); + ptr++; + } + } + else { + end = NULL; + } + + if (tmp_result) { + result += tmp_result; + } + } + + free(s); + return result; +} + +/** + * Convert version operator(s) to flags + * @param str input string + * @return operator flags + */ +int version_parse_operator(const char *str) { + const char *valid = "><=!"; + + const char *pos = str; + int result = 0; + + if (isempty((char *) str)) { + return -1; + } + while ((pos = strpbrk(pos, valid)) != NULL) { + switch (*pos) { + case '>': + result |= GT; + break; + case '<': + result |= LT; + break; + case '=': + result |= EQ; + break; + case '!': + result |= NOT; + break; + default: + return -1; + } + pos++; + } + + return result; +} + +static int version_has_epoch(const char *str) { + const char *result = strchr(str, ':'); + return result ? 1 : 0; +} + +/** + * Compare version strings based on flag(s) + * @param flags verison operators + * @param aa version1 + * @param bb version2 + * @return 1 flag operation is true + * @return 0 flag operation is false + */ +int version_compare(const int flags, const char *aa, const char *bb) { + if (!flags || flags < 0) { + return -1; + } + + int result_a = version_sum(aa); + if (result_a < 0) { + return -1; + } + + int result_b = version_sum(bb); + if (result_b < 0) { + return -1; + } + + if (version_has_epoch(aa) && !version_has_epoch(bb)) { + result_a -= EPOCH_MOD; + } + if (!version_has_epoch(aa) && version_has_epoch(bb)) { + result_b -= EPOCH_MOD; + } + + int result = 0; + if (flags & GT && flags & EQ) { + result |= result_a >= result_b; + } else if (flags & LT && flags & EQ) { + result |= result_a <= result_b; + } else if (flags & NOT && flags & EQ) { + result |= result_a != result_b; + } else if (flags & GT) { + result |= result_a > result_b; + } else if (flags & LT) { + result |= result_a < result_b; + } else if (flags & EQ) { + result |= result_a == result_b; + } + + return result; +}
\ No newline at end of file |
