diff --git a/common/sort-utils.c b/common/sort-utils.c index d5b15a1a..bae2cd16 100644 --- a/common/sort-utils.c +++ b/common/sort-utils.c @@ -16,7 +16,9 @@ #include #include +#include #include "common/sort-utils.h" +#include "common/messages.h" int compare_init(struct compare *comp, const struct sortdef *sortdef) { @@ -55,3 +57,157 @@ int compare_add_sort_key(struct compare *comp, const char *key) } return 0; } + +/* + * Append given sort by its @id from associated sortdef. + * + * Return: 0 if id is valid + * -1 if id not in sortdef + */ +int compare_add_sort_id(struct compare *comp, int id) +{ + int i; + + if (!comp->sortdef) + return -1; + + if (id < 0) + return -1; + + for (i = 0; i < SORT_MAX_KEYS; i++) { + if (comp->sortdef[i].name == NULL) + return -1; + if (comp->sortdef[i].id == id) { + comp->comp[comp->count] = comp->sortdef[i].comp; + comp->count++; + break; + } + } + return 0; +} + +/* + * Consume word-like list of key names (coma separated) and return its id if + * found in sortdef. The @next pointer is advanced to the next expected key start. + * Empty and NULL @next is accepted. + * + * Key lookup is case insensitive. + * + * Retrun: id from sortdef if a matching + * -1 on error + * -2 end of buffer + */ +int compare_parse_key_to_id(const struct compare *comp, const char **next) +{ + const char *tmp = *next, *start = *next; + + if (!comp->sortdef) + return -1; + + /* No sort string (use defaults), or last. */ + if (!*next || !**next) + return -2; + + do { + /* End of word. */ + if (*tmp == ',' || *tmp == 0) { + /* Look up in sortdef. */ + for (int i = 0; comp->sortdef[i].name; i++) { + int len = strlen(comp->sortdef[i].name); + + if (strncasecmp(start, comp->sortdef[i].name, len) == 0) { + /* Point to last NUL. */ + *next = tmp; + /* Or the next valid char. */ + if (*tmp) + (*next)++; + return comp->sortdef[i].id; + } + } + /* Not found, report which one. */ + *next = start; + return -1; + } + /* Invalid char found. */ + if (!isalnum(*tmp)) { + *next = tmp; + return -1; + } + tmp++; + } while(1); + + /* Not found. */ + *next = start; + return -1; +} + +/* Read id of its associated sort @key. Key lookup is case insensitive. */ +int compare_key_id(const struct compare *comp, const char *key) +{ + if (!comp->sortdef) + return -1; + + for (int i = 0; comp->sortdef[i].name; i++) + if (strcasecmp(comp->sortdef[i].name, key) == 0) + return comp->sortdef[i].id; + return -1; +} + +/* Read sort key name associated to @id. */ +const char *compare_id_name(const struct compare *comp, int id) +{ + if (!comp->sortdef) + return NULL; + + for (int i = 0; comp->sortdef[i].name; i++) + if (comp->sortdef[i].id == id) + return comp->sortdef[i].name; + return NULL; +} + +/* + * Check if the given @id (must exist in the associated sortdef) enabled in + * @comp. + */ +bool compare_has_id(const struct compare *comp, int id) +{ + int idx; + + if (!comp->sortdef) + return false; + + idx = -1; + for (int i = 0; comp->sortdef[i].name; i++) + if (comp->sortdef[i].id == id) + idx = i; + + if (idx < 0) + return false; + + for (int i = 0; i < comp->count; i++) + if (comp->comp[i] == comp->sortdef[idx].comp) + return true; + return false; +} + +/* + * Set up compare structure with associated sortdef from a user specified list + * of keys. + */ +int compare_setup_sort(struct compare *comp, const struct sortdef *sdef, const char *def) +{ + const char *tmp; + int id; + + tmp = def; + do { + id = compare_parse_key_to_id(comp, &tmp); + if (id == -1) { + error("unknown sort key: %s", tmp); + return -1; + } + compare_add_sort_id(comp, id); + } while (id >= 0); + + return 0; +} diff --git a/common/sort-utils.h b/common/sort-utils.h index e2a355b3..b92a8e72 100644 --- a/common/sort-utils.h +++ b/common/sort-utils.h @@ -17,6 +17,8 @@ #ifndef __COMMON_SORT_UTILS_H__ #define __COMMON_SORT_UTILS_H__ +#include + /* * Example: @@ -48,7 +50,7 @@ void test() { .desc = "sort by id" }, { .name = "size", .comp = (sort_cmp_t)cmp_entry_size, .desc = "sort by entry size" }, - { .name = NULL, .comp = NULL } + SORTDEF_END }; // List of keys to use for sort (e.g. from command line options) const char *sortby[] = { "size", "id" }; @@ -66,6 +68,9 @@ void test() { } */ +#define SORTDEF_END { .name = NULL, .comp = NULL } +#define SORT_MAX_KEYS 32 + typedef int (*sort_cmp_t)(const void *a, const void *b); typedef int (*sort_r_cmp_t)(const void *a, const void *b, void *data); @@ -73,10 +78,12 @@ struct sortdef { const char *name; const char *desc; sort_cmp_t comp; + /* User defined identifier of this sort key. */ + int id; }; struct compare { - sort_cmp_t comp[32]; + sort_cmp_t comp[SORT_MAX_KEYS]; unsigned long invert_map; int count; const struct sortdef *sortdef; @@ -85,5 +92,11 @@ struct compare { int compare_init(struct compare *comp, const struct sortdef *sortdef); int compare_cmp_multi(const void *a, const void *b, const struct compare *comp); int compare_add_sort_key(struct compare *comp, const char *key); +int compare_parse_key_to_id(const struct compare *comp, const char **next); +int compare_add_sort_id(struct compare *comp, int id); +int compare_key_id(const struct compare *comp, const char *key); +const char *compare_id_name(const struct compare *comp, int id); +bool compare_has_id(const struct compare *comp, int id); +int compare_setup_sort(struct compare *comp, const struct sortdef *sdef, const char *def); #endif