#include "list.h" #define _GNU_SOURCE #include "history.h" #include "util.h" #include "grapheme.h" #include #include static struct inputln *history_list_prev( struct inputln *cur, const char *search); static struct inputln *history_list_next( struct inputln *cur, const char *search); struct inputln * history_list_prev(struct inputln *cur, const char *search) { struct list_link *iter; struct inputln *ln; for (iter = cur->link.prev; iter && iter->prev; iter = iter->prev) { ln = LIST_UPCAST(iter, struct inputln, link); if (!search || !*search || strcasestr(ln->buf, search)) return ln; } return cur; } struct inputln * history_list_next(struct inputln *cur, const char *search) { struct list_link *iter; struct inputln *ln; iter = cur->link.next; while (LIST_INNER(iter)) { ln = LIST_UPCAST(iter, struct inputln, link); if (!search || !*search || strcasestr(ln->buf, search)) return ln; iter = iter->next; } return cur; } void history_init(struct history *history) { list_init(&history->list); history->input = inputln_alloc(); history->sel = history->input; } void history_deinit(struct history *history) { struct list_link *link; struct inputln *ln; link = list_link_pop(&history->input->link); ln = LIST_UPCAST(link, struct inputln, link); inputln_free(ln); history->input = NULL; list_free_items(&history->list, (list_item_free_fn) inputln_free, LIST_OFFSET(struct inputln, link)); history->sel = NULL; } void history_submit(struct history *history) { /* if chose from history free input */ if (history->sel != history->input) { list_link_pop(&history->input->link); inputln_free(history->input); } /* pop first in case already in history */ list_link_pop(&history->sel->link); history_add(history, history->sel); /* create new input buf and add to hist */ history->input = inputln_alloc(); history->sel = history->input; history_add(history, history->sel); } void history_prev(struct history *history) { history->sel = history_list_prev(history->sel, history->input->buf); } void history_next(struct history *history) { history->sel = history_list_next(history->sel, history->input->buf); } void history_add(struct history *history, struct inputln *line) { struct inputln *ln; struct list_link *back; if (list_len(&history->list) == HISTORY_MAX) { /* pop last item to make space */ back = list_pop_back(&history->list); ln = LIST_UPCAST(back, struct inputln, link); inputln_free(ln); } list_insert_front(&history->list, &line->link); } void inputln_init(struct inputln *ln) { ln->buf = NULL; ln->len = 0; ln->cap = 0; ln->cur = 0; ln->curpos = 0; ln->link = LIST_LINK_INIT; inputln_resize(ln, 128); } void inputln_deinit(struct inputln *ln) { free(ln->buf); } struct inputln * inputln_alloc(void) { struct inputln *ln; ln = malloc(sizeof(struct inputln)); if (!ln) ERROR(SYSTEM, "malloc"); inputln_init(ln); return ln; } void inputln_free(struct inputln *ln) { inputln_deinit(ln); free(ln); } void inputln_resize(struct inputln *ln, size_t size) { ASSERT(size != 0); ln->cap = size; ln->buf = realloc(ln->buf, ln->cap * sizeof(char)); if (!ln->buf) ERROR(SYSTEM, "realloc"); ln->len = MIN(ln->len, ln->cap-1); ln->buf[ln->len] = '\0'; } void inputln_left(struct inputln *ln) { ln->cur = utf8_next_break_left(ln->buf, ln->cur); ln->curpos = text_width(ln->buf, ln->cur); } void inputln_right(struct inputln *ln) { ln->cur = utf8_next_break_right(ln->buf, ln->cur); ln->curpos = text_width(ln->buf, ln->cur); } void inputln_addch(struct inputln *ln, char c) { int i; if (ln->len + 1 >= ln->cap) { ln->cap *= 2; ln->buf = realloc(ln->buf, ln->cap); } for (i = ln->len; i > ln->cur; i--) ln->buf[i] = ln->buf[i-1]; ln->buf[ln->cur] = c; ln->len++; ln->cur++; ln->buf[ln->len] = '\0'; ln->curpos = text_width(ln->buf, ln->cur); } void inputln_del(struct inputln *ln, int n) { size_t next; int i; if (!ln->cur) return; next = utf8_next_break_left(ln->buf, ln->cur); n = ln->cur - next; for (i = ln->cur; i <= ln->len; i++) ln->buf[i-n] = ln->buf[i]; ln->len -= n; ln->cur -= n; ln->buf[ln->len] = '\0'; ln->curpos = text_width(ln->buf, ln->cur); } void inputln_copy(struct inputln *dst, struct inputln *src) { if (dst->buf) { free(dst->buf); dst->buf = NULL; } dst->len = src->len; dst->buf = astrdup(src->buf); dst->cap = src->len + 1; dst->cur = dst->len; dst->curpos = text_width(dst->buf, dst->len); } void inputln_replace(struct inputln *ln, const char *str) { ln->len = strlen(str); if (ln->cap <= ln->len) inputln_resize(ln, ln->len + 1); strncpy(ln->buf, str, ln->len + 1); ln->cur = ln->len; ln->curpos = text_width(ln->buf, ln->len); }