I have made a patch for dmenu that adds "fuzzy" matching(with support for case insensitive matching).
It matches items that have all characters entered, in sequence they are entered, but there may be any number of characters between matched characters. Basically it takes "txt" makes it to "*t*x*t" glob pattern and checks if it matches(of course it does not do any glob conversion or anything, it just works like matching described pattern). P.S. I labeled patch as being against 4.5, but actually it is against current tip, I couldn't figure out how should I name the file to reflect that, but I hope that won't be a problem.
diff -r b491483be453 dmenu.c --- a/dmenu.c Mon Jul 30 17:02:12 2012 +0200 +++ b/dmenu.c Tue Sep 25 14:30:42 2012 +0300 @@ -32,6 +32,9 @@ static void insert(const char *str, ssize_t n); static void keypress(XKeyEvent *ev); static void match(void); +static void match_fuzzy(void); +static void match_tokens(void); +static char *strchri(const char *s, int c); static size_t nextrune(int inc); static void paste(void); static void readstdin(void); @@ -60,9 +63,11 @@ static Item *prev, *curr, *next, *sel; static Window win; static XIC xic; +static Bool fuzzy; static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; static char *(*fstrstr)(const char *, const char *) = strstr; +static char *(*fstrchr)(const char *, const int) = strchr; int main(int argc, char *argv[]) { @@ -79,9 +84,12 @@ topbar = False; else if(!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ fast = True; + else if(!strcmp(argv[i], "-z")) /* enable fuzzy matching */ + fuzzy = True; else if(!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ fstrncmp = strncasecmp; fstrstr = cistrstr; + fstrchr = strchri; } else if(i+1 == argc) usage(); @@ -388,8 +396,52 @@ drawmenu(); } +char * +strchri(const char *s, int c) { + char *u, *l; + if(!isalpha(c)) return strchr(s, c); + if(isupper(c)) { + u = strchr(s, c); + l = strchr(s, tolower(c)); + } + else { + l = strchr(s, c); + u = strchr(s, toupper(c)); + } + + if(u && l) return u < l ? u : l; + return u == NULL ? l : u; +} + void match(void) { + if(fuzzy) match_fuzzy(); + else match_tokens(); +} + +void +match_fuzzy(void) { + int i; + size_t len; + Item *item; + + char *pos; + + len = strlen(text); + + matches = matchend = NULL; + for(item = items; item && item->text; item++) { + i = 0; + for(pos = fstrchr(item->text, text[i]); pos && text[i]; i++, pos = fstrchr(pos+1, text[i])); + if(i == len) appenditem(item, &matches, &matchend); + } + + curr = sel = matches; + calcoffsets(); +} + +void +match_tokens(void) { static char **tokv = NULL; static int tokn = 0;