diff --git a/config.def.h b/config.def.h index a9122f7..c0110f3 100644 --- a/config.def.h +++ b/config.def.h @@ -15,3 +15,4 @@ static const char *outbgcolor = "#00ffff"; static const char *outfgcolor = "#000000"; /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ static unsigned int lines = 0; +static char *histfile = NULL; /* path to file where history is stored */ diff --git a/dmenu.c b/dmenu.c index a07f8e3..c6360fd 100644 --- a/dmenu.c +++ b/dmenu.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,43 @@ static Drw *drw; static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; static char *(*fstrstr)(const char *, const char *) = strstr; +struct history_entry { + char *text; + int count; +}; +struct history_entry *history_buffer = NULL; +int history_length; +struct item *history = NULL; + +static void +writehistory(char *command) +{ + if (histfile == NULL) { + fprintf(stderr, "no history file\n"); + return; + } + fprintf(stderr, "writing history\n"); + FILE *f = fopen(histfile, "w"); + int found = 0; + for (int i = 0; i < history_length; ++i) { + if (strcmp(history_buffer[i].text, command) == 0) { + history_buffer[i].count++; + found = 1; + break; + } + } + if (found) { + fprintf(f, "%d\n", history_length); + } else { + fprintf(f, "%d\n", history_length + 1); + fprintf(f, "1 %s\n", command); + } + for (int i = 0; i < history_length; ++i) { + fprintf(f, "%d %s\n", history_buffer[i].count, history_buffer[i].text); + } + fclose(f); +} + static void appenditem(struct item *item, struct item **list, struct item **last) { @@ -219,6 +257,22 @@ match(void) matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; textsize = strlen(text); + + for (item = history; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + for (item = items; item && item->text; item++) { for (i = 0; i < tokc; i++) if (!fstrstr(item->text, tokv[i])) @@ -284,6 +338,7 @@ keypress(XKeyEvent *ev) int len; KeySym ksym = NoSymbol; Status status; + char *command; len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); if (status == XBufferOverflow) @@ -414,7 +469,9 @@ keypress(XKeyEvent *ev) break; case XK_Return: case XK_KP_Enter: - puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + command = (sel && !(ev->state & ShiftMask)) ? sel->text : text; + puts(command); + writehistory(command); if (!(ev->state & ControlMask)) { cleanup(); exit(0); @@ -464,11 +521,18 @@ paste(void) drawmenu(); } +static int +history_cmp(const void *a, const void *b) { + struct history_entry *a_ = (struct history_entry *)a; + struct history_entry *b_ = (struct history_entry *)b; + return b_->count - a_->count; +} + static void readstdin(void) { char buf[sizeof text], *p, *maxstr = NULL; - size_t i, max = 0, size = 0; + size_t i, max = 0, size = 0, length = 0; /* read each line from stdin and add it to the item list */ for (i = 0; fgets(buf, sizeof buf, stdin); i++) { @@ -483,8 +547,60 @@ readstdin(void) if (strlen(items[i].text) > max) max = strlen(maxstr = items[i].text); } + length = i; + + if (histfile) { + FILE *f = fopen(histfile, "r"); + if (f) { + + /* read the line count */ + if (fgets(buf, sizeof buf, f) == NULL) { + fclose(f); + die("history file (%s) is empty", histfile); + } + if (p = strchr(buf, '\n')) + *p = '\0'; + history_length = atoi(buf); + history_buffer = malloc(sizeof(struct history_entry) * history_length); + i = 0; + while (fgets(buf, sizeof buf, f)) { + if (p = strchr(buf, '\n')) + *p = '\0'; + + char *space = strchr(buf, ' '); + char *text = space + 1; + *space = '\0'; + int count = atoi(buf); + + history_buffer[i].count = count; + history_buffer[i].text = strdup(text); + + i++; + } + + qsort( + history_buffer /* base */, + history_length /* length */, + sizeof(struct history_entry), /* size */ + history_cmp + ); + + history = malloc(sizeof(struct item) * (history_length + 1)); + for (i = 0; i < history_length; ++i) { + history[i].text = history_buffer[i].text; + history[i].out = false; + if (strlen(items[i].text) > max) + max = strlen(maxstr = history[i].text); + } + length += i; + fclose(f); + } + } + if (items) items[i].text = NULL; + if (history) + history[history_length].text = NULL; inputw = maxstr ? TEXTW(maxstr) : 0; lines = MIN(lines, i); }