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 <locale.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <string.h>
 #include <strings.h>
 #include <time.h>
@@ -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);
 }
