diff options
Diffstat (limited to 'src/choices.c')
-rw-r--r-- | src/choices.c | 140 |
1 files changed, 119 insertions, 21 deletions
diff --git a/src/choices.c b/src/choices.c index fe2f80b..998fb4b 100644 --- a/src/choices.c +++ b/src/choices.c @@ -5,6 +5,7 @@ #include <unistd.h> #include <errno.h> +#include "common.h" #include "options.h" #include "choices.h" #include "match.h" @@ -20,11 +21,10 @@ static int cmpchoice(const void *_idx1, const void *_idx2) { const struct scored_result *b = _idx2; if (a->score == b->score) { - /* To ensure a stable sort, we must also sort by the string - * pointers. We can do this since we know all the strings are - * from a contiguous memory segment (buffer in choices_t). + /* To ensure a stable sort, we must also sort by the line + * indices. */ - if (a->str < b->str) { + if (a->idx < b->idx) { return -1; } else { return 1; @@ -46,6 +46,66 @@ static void *safe_realloc(void *buffer, size_t size) { return buffer; } +static char *line_split(const char *line, const char *delim, + const FieldSelector *fs) +{ + int nb_fields; + struct { + int start; + int len; + } *fields = NULL; + + char *ret = NULL; + size_t offset, ret_len; + const char *p; + + /* split line into fields */ + p = line; + nb_fields = 0; + while (*p) { + p += strspn(p, delim); + if (!*p) + break; + + fields = safe_realloc(fields, (nb_fields + 1) * sizeof(*fields)); + + fields[nb_fields].start = p - line; + fields[nb_fields].len = strcspn(p, delim); + p += fields[nb_fields].len; + nb_fields++; + } + + /* count output length */ + ret_len = offset = 0; + for (size_t i = 0; i < fs->nb_ranges; i++) { + size_t range_len; + + int start = fs->ranges[i].start; + int end = fs->ranges[i].end; + + if (start < 0) + start = MAX(nb_fields + start, 0); + start = MIN(nb_fields - 1, start); + + if (end < 0) + end = MAX(nb_fields + end, 0); + end = MIN(nb_fields - 1, end); + + if (end < start) + continue; + + range_len = fields[end].start + fields[end].len - fields[start].start; + + ret = safe_realloc(ret, ret_len + range_len + 1); + + strncpy(ret + offset, line + fields[start].start, range_len); + offset += range_len; + ret[offset] = 0; + } + + return ret; +} + void choices_fread(choices_t *c, FILE *file, char input_delimiter) { /* Save current position for parsing later */ size_t buffer_start = c->buffer_size; @@ -88,7 +148,11 @@ void choices_fread(choices_t *c, FILE *file, char input_delimiter) { } static void choices_resize(choices_t *c, size_t new_capacity) { - c->strings = safe_realloc(c->strings, new_capacity * sizeof(const char *)); + c->input_items = safe_realloc(c->input_items, + new_capacity * sizeof(*c->input_items)); + memset(c->input_items + c->capacity, 0, + (new_capacity - c->capacity) * sizeof(*c->input_items)); + c->capacity = new_capacity; } @@ -99,13 +163,18 @@ static void choices_reset_search(choices_t *c) { } void choices_init(choices_t *c, options_t *options) { - c->strings = NULL; + c->input_items = NULL; c->results = NULL; c->buffer_size = 0; c->buffer = NULL; c->capacity = c->size = 0; + + c->delimiters = options->delimiters; + c->search_fields = &options->search_fields; + c->output_fields = &options->output_fields; + choices_resize(c, INITIAL_CHOICE_CAPACITY); if (options->workers) { @@ -122,8 +191,11 @@ void choices_destroy(choices_t *c) { c->buffer = NULL; c->buffer_size = 0; - free(c->strings); - c->strings = NULL; + for (size_t i = 0; i < c->size; i++) + free(c->input_items[i].allocated); + + free(c->input_items); + c->input_items = NULL; c->capacity = c->size = 0; free(c->results); @@ -131,14 +203,26 @@ void choices_destroy(choices_t *c) { c->available = c->selection = 0; } -void choices_add(choices_t *c, const char *choice) { - /* Previous search is now invalid */ - choices_reset_search(c); +void choices_add(choices_t *c, const char *line) +{ + InputItem *it; if (c->size == c->capacity) { choices_resize(c, c->capacity * 2); } - c->strings[c->size++] = choice; + it = &c->input_items[c->size++]; + + it->input_line = line; + it->search_buf = it->input_line; + + /* extract the fields to be searched, if requested */ + if (c->search_fields->nb_ranges) { + it->allocated = line_split(line, c->delimiters, c->search_fields); + it->search_buf = it->allocated; + } + + /* Previous search is now invalid */ + choices_reset_search(c); } size_t choices_available(choices_t *c) { @@ -230,9 +314,12 @@ static void *choices_search_worker(void *data) { } for(size_t i = start; i < end; i++) { - if (has_match(job->search, c->strings[i])) { - result->list[result->size].str = c->strings[i]; - result->list[result->size].score = match(job->search, c->strings[i]); + InputItem *it = &c->input_items[i]; + const char *str = it->search_buf; + + if (has_match(job->search, str)) { + result->list[result->size].idx = i; + result->list[result->size].score = match(job->search, str); result->size++; } } @@ -300,12 +387,23 @@ void choices_search(choices_t *c, const char *search) { free(job); } -const char *choices_get(choices_t *c, size_t n) { - if (n < c->available) { - return c->results[n].str; - } else { - return NULL; - } +const char *choices_get_search(choices_t *c, size_t n) { + if (n < c->available) + return c->input_items[c->results[n].idx].search_buf; + return NULL; +} + +char *choices_get_output(choices_t *c, size_t n) +{ + const char *line; + + if (n >= c->available) + return NULL; + + line = c->input_items[c->results[n].idx].input_line; + if (c->output_fields->nb_ranges) + return line_split(line, c->delimiters, c->output_fields); + return strdup(line); } score_t choices_getscore(choices_t *c, size_t n) { |