#include #include #include #include "choices.h" #include "match.h" /* Initial size of buffer for storing input in memory */ #define INITIAL_BUFFER_SIZE 4096 /* Initial size of choices array */ #define INITIAL_CHOICE_CAPACITY 128 static int cmpchoice(const void *_idx1, const void *_idx2) { const struct scored_result *a = _idx1; const struct scored_result *b = _idx2; if (a->score == b->score) return 0; else if (a->score < b->score) return 1; else return -1; } static void *safe_realloc(void *buffer, size_t size) { buffer = realloc(buffer, size); if (!buffer) { fprintf(stderr, "Error: Can't allocate memory (%zu bytes)\n", size); abort(); } return buffer; } void choices_fread(choices_t *c, FILE *file) { size_t bufsize = INITIAL_BUFFER_SIZE, pos = 0; char *buf = safe_realloc(NULL, bufsize); /* Continue reading until we get a "short" read, indicating EOF */ while ((pos += fread(buf + pos, 1, bufsize - pos, file)) == bufsize) { bufsize *= 2; buf = safe_realloc(buf, bufsize); } buf[pos] = 0; /* Truncate buffer to used size, (maybe) freeing some memory for * future allocations. */ buf = safe_realloc(buf, pos + 1); /* Tokenize input and add to choices */ char *line = buf; do { char *nl = strchr(line, '\n'); if (nl) *nl++ = '\0'; /* Skip empty lines */ if (*line) choices_add(c, line); line = nl; } while (line); } static void choices_resize(choices_t *c, size_t new_capacity) { c->strings = safe_realloc(c->strings, new_capacity * sizeof(const char *)); c->capacity = new_capacity; } static void choices_reset_search(choices_t *c) { free(c->results); c->selection = c->available = 0; c->results = NULL; } void choices_init(choices_t *c) { c->strings = NULL; c->results = NULL; c->capacity = c->size = 0; choices_reset_search(c); choices_resize(c, INITIAL_CHOICE_CAPACITY); } void choices_free(choices_t *c) { free(c->strings); free(c->results); } void choices_add(choices_t *c, const char *choice) { /* Previous search is now invalid */ choices_reset_search(c); if (c->size == c->capacity) { choices_resize(c, c->capacity * 2); } c->strings[c->size++] = choice; } size_t choices_available(choices_t *c) { return c->available; } void choices_search(choices_t *c, const char *search) { choices_reset_search(c); c->results = malloc(c->size * sizeof(struct scored_result)); if (!c->results) { fprintf(stderr, "Error: Can't allocate memory\n"); abort(); } for (size_t i = 0; i < c->size; i++) { if (has_match(search, c->strings[i])) { c->results[c->available].str = c->strings[i]; c->results[c->available].score = match(search, c->strings[i]); c->available++; } } qsort(c->results, c->available, sizeof(struct scored_result), cmpchoice); } const char *choices_get(choices_t *c, size_t n) { if (n < c->available) { return c->results[n].str; } else { return NULL; } } double choices_getscore(choices_t *c, size_t n) { return c->results[n].score; } void choices_prev(choices_t *c) { if (c->available) c->selection = (c->selection + c->available - 1) % c->available; } void choices_next(choices_t *c) { if (c->available) c->selection = (c->selection + 1) % c->available; }