summaryrefslogtreecommitdiff
path: root/src/choices.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/choices.c')
-rw-r--r--src/choices.c140
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) {