#define INCLUDE_DEFS
#include "config.h"
+static void xkeypress(XConf* x, XEvent* e);
+static void xbtnpress(XConf* x, XEvent* e);
+static void redraw(XConf* x);
+static void xinit(XConf* x);
+
typedef struct {
float score;
char* string;
size_t ChoiceIdx = 0;
size_t Offset = 0;
-static char* rdline(FILE* fin) {
+static int peekc(FILE* in) {
+ int c = fgetc(in);
+ ungetc(c, in);
+ return c;
+}
+
+static char* rdline(FILE* fin, size_t* len) {
if (feof(fin) || ferror(fin))
return NULL;
size_t size = 256;
size_t index = 0;
- char* str = (char*)malloc(size);
- while (true) {
- char ch = fgetc(fin);
- if (ch == EOF) break;
- str[index++] = ch;
- str[index] = '\0';
- if (index+1 >= size) {
+ char* str = (char*)calloc(size,1);
+ while (!feof(stdin)) {
+ if (index+2 >= size) {
size = size << 1;
str = realloc(str, size);
}
- if (ch == '\n') break;
+ char ch = fgetc(fin);
+ if (ch == '\r') continue;
+ if (ch == EOF || ch == '\n') break;
+ str[index++] = ch;
+ str[index] = '\0';
}
+ if (len) *len = index;
return str;
}
else if (ca->score > cb->score)
return -1;
else
- return strcmp(ca->string, cb->string);
+ return 0;
}
-static void load_choices(void) {
+static void load_choices(XConf* x) {
+ size_t choice_len;
char* choice_text;
+ bool shown = false;
Choice choice = {0};
vec_init(&Choices, sizeof(Choice));
- while ((choice_text = rdline(stdin)) != NULL) {
- choice_text[strlen(choice_text)-1] = '\0';
- if (strlen(choice_text) > 0) {
+ while ((choice_text = rdline(stdin, &choice_len)) != NULL) {
+ if (choice_len > 0) {
choice.string = choice_text;
choice.length = strlen(choice_text);
choice.score = 1.0;
+ if (!shown && peekc(stdin) != EOF) {
+ x11_show(x);
+ redraw(x);
+ shown = true;
+ }
vec_push_back(&Choices, &choice);
+ } else {
+ free(choice_text);
}
}
- vec_sort(&Choices, by_score);
}
-static char* find_match_start(char *str, int ch) {
+static char* find_char(char* str, int ch) {
for (; *str; str++)
if (tolower(*str) == tolower(ch))
return str;
return NULL;
}
-static bool match(char *string, size_t offset, size_t *start, size_t *end) {
- char* q = Query;
- char* s = find_match_start(&string[offset], *q);
- char* e = s;
- /* bail if no match for first char */
- if (s == NULL) return 0;
+static inline bool find_match(char *string, char* query, size_t offset, char **start, char **end) {
+ char *s, *e;
+ /* find first match char or bail */
+ s = e = find_char(&string[offset], *query);
+ if (s == NULL) return false;
+
/* find the end of the match */
- for (; *q; q++)
- if ((e = find_match_start(e, *q)) == NULL)
+ for (query++; *query; query++)
+ if ((e = find_char(e+1, *query)) == NULL)
return false;
- /* make note of the matching range */
- *start = s - string;
- *end = e - string;
- /* Less than or equal is used in order to obtain the left-most match. */
- if (match(string, offset + 1, start, end) && (size_t)(e - s) <= *end - *start) {
- *start = s - string;
- *end = e - string;
- }
+
+ /* if we made it this far, we found a match */
+ *start = s, *end = e;
+ return true;
+}
+
+static bool match(char *string, size_t offset, size_t *start, size_t *end) {
+ char *s1 = 0, *e1 = 0, *s2, *e2;
+ /* first check if we match at all */
+ if (!find_match(string, Query, offset, &s1, &e1))
+ return false;
+ s2 = s1, e2 = e1; // Init s2 and e2 before use below
+
+ /* next find the longest match. If multiple, take the left most one */
+ while (find_match(string, Query, ++offset, &s1, &e1) && ((e1-s1) <= (e2-s2)))
+ s1 = s2, e1 = e2;
+
+ /* return the best match */
+ *start = s1 - string;
+ *end = e1 - string;
return true;
}
static void score(void) {
- for (unsigned int i = 0; i < vec_size(&Choices); i++) {
+ unsigned int nchoices = vec_size(&Choices);
+ for (unsigned int i = 0; i < nchoices; i++) {
Choice* choice = (Choice*)vec_at(&Choices, i);
float qlen = (float)QueryIdx;
if (match(choice->string, 0, &choice->match_start, &choice->match_end)) {
- float clen = (float)(choice->match_end - choice->match_start);
+ float clen = (float)(choice->match_end - choice->match_start) + 1;
choice->score = qlen / clen / (float)(choice->length);
} else {
choice->match_start = 0;
vec_sort(&Choices, by_score);
}
+int main(int argc, char** argv) {
+ XConf x = {0};
+ if (argc >= 2) {
+ size_t sz = min(strlen(argv[1]), sizeof(Query)-1);
+ strncpy(Query, argv[1], sz);
+ QueryIdx = sz;
+ }
+ xinit(&x);
+ load_choices(&x);
+ score();
+ if (vec_size(&Choices) > 1)
+ x11_event_loop(&x, redraw);
+ Choice* choice = (Choice*)vec_at(&Choices, ChoiceIdx);
+ if (vec_size(&Choices) && ChoiceIdx != SIZE_MAX)
+ printf("%s\n", choice->string);
+ return 0;
+}
+
static void xkeypress(XConf* x, XEvent* e) {
KeySym key = x11_getkey(x, e);
if (key == XK_Return) {
} else if (key == XK_BackSpace) {
if (QueryIdx > 0)
Query[--QueryIdx] = '\0';
+ score();
} else if (key >= 0x20 && key <= 0x7F) {
if (QueryIdx < sizeof(Query)-1)
Query[QueryIdx++] = key;
Offset = ChoiceIdx = 0;
+ score();
}
- score();
}
static void xbtnpress(XConf* x, XEvent* e) {
perror("unable to load base font");
exit(EXIT_FAILURE);
}
- x11_show(x);
x->eventfns[KeyPress] = xkeypress;
x->eventfns[ButtonPress] = xbtnpress;
- redraw(x);
-}
-
-int main(int argc, char** argv) {
- XConf x = {0};
- if (argc >= 2) {
- size_t sz = min(strlen(argv[1]), sizeof(Query)-1);
- strncpy(Query, argv[1], sz);
- QueryIdx = sz;
- }
- xinit(&x);
- load_choices();
- score();
- if (vec_size(&Choices) > 1)
- x11_event_loop(&x, redraw);
- Choice* choice = (Choice*)vec_at(&Choices, ChoiceIdx);
- if (vec_size(&Choices) && ChoiceIdx != SIZE_MAX)
- printf("%s\n", choice->string);
- return 0;
}