Hi,

On Thu, Jul 29, 2010 at 12:26:54PM +0200, Catalin David wrote:
> Hello!
> 
> On Sun, Jul 18, 2010 at 9:49 PM, Sean Neilan <s...@seanneilan.com> wrote:
> > I'm sorry, I found what I was looking for a couple minutes after I sent this
> > email.
> > http://lists.suckless.org/dwm/0901/7355.html
> > This patch will let you use tab completion to select a file. Wow.
> 
> Unfortunately, i was not able to patch the latest version of dmenu
> with it... Can you please tell me what version of dmenu you used?
> 
> Thanks,
> 
> Catalin
> 

The attached patch is what I did to get it to work on a reasonably 
recent version of dmenu.  I suggest you apply the changes in this 
manually to get it to work.  It's pretty neat :-)

S

-- 
Sean Whitton / <s...@silentflame.com>
OpenPGP KeyID: 0x3B6D411B
http://seanwhitton.com/

diff --git a/dmenu/dmenu.1 b/dmenu/dmenu.1
index 2279835..4e669a2 100644
--- a/dmenu/dmenu.1
+++ b/dmenu/dmenu.1
@@ -14,6 +14,7 @@ dmenu \- dynamic menu
 .RB [ \-sf " <color>"]
 .RB [ \-xs ]
 .RB [ \-v ]
+.RB [ \-c ]
 .SH DESCRIPTION
 .SS Overview
 dmenu is a generic menu for X, originally designed for
@@ -55,6 +56,9 @@ xmms-like pattern matching.
 .TP
 .B \-v
 prints version information to standard output, then exits.
+.TP
+.B \-c
+enables filename completion for text after a space (useful with the dmenu_run 
script).
 .SH USAGE
 dmenu reads a list of newline-separated items from standard input and creates a
 menu.  When the user selects an item or enters any text and presses Return, 
his/her
@@ -76,7 +80,9 @@ Select the first item of the previous/next 'page' of items.
 Select the first/last item.
 .TP
 .B Tab (Control\-i)
-Copy the selected item to the input field.
+Copy the selected item to the input field.  Also, if the -c option is given 
and there
+is a space in the input, will try to expand and complete text after the space 
into a
+valid filename. (First Tab - Longest Completion, Multiple Tabs - cycle through 
files)
 .TP
 .B Return (Control\-j)
 Confirm selection and quit (print the selected item to standard output). 
Returns
diff --git a/dmenu/dmenu.c b/dmenu/dmenu.c
index b30b92a..8d1f33c 100644
--- a/dmenu/dmenu.c
+++ b/dmenu/dmenu.c
@@ -7,6 +7,7 @@
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
+#include <wordexp.h>
 #include <X11/keysym.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
@@ -61,6 +62,7 @@ static Bool grabkeyboard(void);
 static void initfont(const char *fontstr);
 static void kpress(XKeyEvent * e);
 static void match(char *pattern);
+static void matchfile(char *filestart, Bool cycling);
 static void readstdin(void);
 static void run(void);
 static void setup(Bool topbar);
@@ -81,7 +83,7 @@ static int cursor = 0;
 static int screen;
 static unsigned int mw, mh;
 static unsigned int numlockmask = 0;
-static Bool running = True;
+static Bool running = True, filecomplete = False;
 static Bool xmms = False;
 static Display *dpy;
 static DC dc;
@@ -384,6 +386,7 @@ initfont(const char *fontstr) {
 
 void
 kpress(XKeyEvent * e) {
+       static KeySym lastkey=0;
        char buf[32];
        int i, num;
        unsigned int len;
@@ -498,7 +501,11 @@ kpress(XKeyEvent * e) {
                if(num && !iscntrl((int) buf[0])) {
                        buf[num] = 0;
                        memmove(text + cursor + num, text + cursor, sizeof text 
- cursor);
-                       strncpy(text + cursor, buf, sizeof text - cursor);
+                       //strncpy(text + cursor, buf, sizeof text - cursor);
+                       if (len > 0)
+                               strncat(text, buf, sizeof text);
+                       else
+                               strncpy(text, buf, sizeof text);
                        cursor+=num;
                        match(text);
                }
@@ -581,6 +588,11 @@ kpress(XKeyEvent * e) {
                        return;
                break;
        case XK_Tab:
+               if( filecomplete && strchr(text, ' ')!=NULL ) {
+                       matchfile( strchr(text, ' ')+1, lastkey==XK_Tab );
+                       cursor = strlen(text);
+                       break;
+               }
                if(!sel)
                        return;
                strncpy(text, sel->text, sizeof text);
@@ -591,6 +603,7 @@ kpress(XKeyEvent * e) {
        len = strlen(text);
        cursor = MIN(cursor, len);
        cursor = MAX(cursor, 0);
+       lastkey=ksym;
        drawmenu();
 }
 
@@ -669,6 +682,44 @@ match(char *pattern) {
 }
 
 void
+matchfile(char *filestart, Bool cycling ) {
+       static int try=0, p=0;
+       wordexp_t exp;
+       int i, j, k;
+
+       if( !cycling ) { 
+                p = strlen(filestart);
+                try=0;
+       }
+       filestart[ p+1 ] = 0;
+       filestart[ p ] = '*';
+
+       wordexp(filestart, &exp, 0);
+       if( exp.we_wordc > 0 ) {
+                for(j=0,i=0; exp.we_wordv[try][i]!=0; i++,j++) {
+                         if( exp.we_wordv[try][i]==' ' ) filestart[j++]='\\';
+                         filestart[j]=exp.we_wordv[try][i];
+                }
+                filestart[j]=0;
+
+                if( cycling )
+                         try = (try+1)%exp.we_wordc;
+                else
+                         for(k=1; k<exp.we_wordc; k++)
+                                       for(j=0, i=0; exp.we_wordv[k][i]; 
i++,j++) {
+                                                if( filestart[j]=='\\' ) j++;
+                                                if( 
filestart[j]!=exp.we_wordv[k][i] ) {
+                                                         filestart[j]=0;
+                                                         break;
+                                                }
+                                       }
+       } else {
+                filestart[ p ] = 0;
+       }
+       wordfree(&exp);
+}
+
+void
 readstdin(void) {
        char *p, buf[1024];
        unsigned int len = 0, max = 0;
@@ -830,6 +881,8 @@ main(int argc, char *argv[]) {
                }
                else if(!strcmp(argv[i], "-b"))
                        topbar = False;
+               else if(!strcmp(argv[i], "-c"))
+                       filecomplete = True;
                else if(!strcmp(argv[i], "-e")) {
                        if(++i < argc) root = atoi(argv[i]);
                }
@@ -863,7 +916,7 @@ main(int argc, char *argv[]) {
                        eprint("dmenu-"VERSION", © 2006-2009 dmenu engineers, 
see LICENSE for details\n");
                else
                        eprint("usage: dmenu [-i] [-b] [-l <lines>] [-fn 
<font>] [-nb <color>] [-nf <color>]\n"
-                              "             [-p <prompt>] [-sb <color>] [-sf 
<color>] [-xs] [-v]\n");
+                              "             [-p <prompt>] [-sb <color>] [-sf 
<color>] [-xs] [-v] [-c]\n");
        if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
                fprintf(stderr, "warning: no locale support\n");
        if(!(dpy = XOpenDisplay(NULL)))
diff --git a/dmenu/dmenu_run b/dmenu/dmenu_run
index 3e1e6e4..a4c09e5 100755
--- a/dmenu/dmenu_run
+++ b/dmenu/dmenu_run
@@ -1,2 +1,2 @@
 #!/bin/sh
-exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe
+exe=`dmenu_path | dmenu ${1+"$@"}` && exe2=${exe//\\ / } && exec ${exe2%% *} 
"${exe2#* }"
diff --git a/dmenu/filecompletion2.diff b/dmenu/filecompletion2.diff
new file mode 100644
index 0000000..080bc80
--- /dev/null
+++ b/dmenu/filecompletion2.diff
@@ -0,0 +1,170 @@
+diff -r 13402291bc76 dmenu.1
+--- a/dmenu.1  Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu.1  Wed Jan 28 18:15:59 2009 -0500
+@@ -12,6 +12,7 @@
+ .RB [ \-sb " <color>"]
+ .RB [ \-sf " <color>"]
+ .RB [ \-v ]
++.RB [ \-c ]
+ .SH DESCRIPTION
+ .SS Overview
+ dmenu is a generic menu for X, originally designed for
+@@ -46,6 +47,9 @@
+ .TP
+ .B \-v
+ prints version information to standard output, then exits.
++.TP
++.B \-c
++enables filename completion for text after a space (useful with the dmenu_run 
script).
+ .SH USAGE
+ dmenu reads a list of newline-separated items from standard input and creates 
a
+ menu.  When the user selects an item or enters any text and presses Return, 
his/her
+@@ -67,7 +71,9 @@
+ Select the first/last item.
+ .TP
+ .B Tab (Control\-i)
+-Copy the selected item to the input field.
++Copy the selected item to the input field.  Also, if the -c option is given 
and there
++is a space in the input, will try to expand and complete text after the space 
into a
++valid filename. (First Tab - Longest Completion, Multiple Tabs - cycle 
through files)
+ .TP
+ .B Return (Control\-j)
+ Confirm selection and quit (print the selected item to standard output). 
Returns
+diff -r 13402291bc76 dmenu.c
+--- a/dmenu.c  Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu.c  Wed Jan 28 18:15:59 2009 -0500
+@@ -8,6 +8,7 @@
+ #include <string.h>
+ #include <strings.h>
+ #include <unistd.h>
++#include <wordexp.h>
+ #include <X11/keysym.h>
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+@@ -59,6 +60,7 @@
+ static void initfont(const char *fontstr);
+ static void kpress(XKeyEvent * e);
+ static void match(char *pattern);
++static void matchfile(char *filestart, Bool cycling);
+ static void readstdin(void);
+ static void run(void);
+ static void setup(Bool topbar);
+@@ -77,7 +79,7 @@
+ static int screen;
+ static unsigned int mw, mh;
+ static unsigned int numlockmask = 0;
+-static Bool running = True;
++static Bool running = True, filecomplete = False;
+ static Display *dpy;
+ static DC dc;
+ static Item *allitems = NULL; /* first of all items */
+@@ -311,6 +313,7 @@
+ 
+ void
+ kpress(XKeyEvent * e) {
++      static KeySym lastkey=0;
+       char buf[32];
+       int i, num;
+       unsigned int len;
+@@ -396,7 +399,10 @@
+       default:
+               if(num && !iscntrl((int) buf[0])) {
+                       buf[num] = 0;
+-                      strncpy(text + len, buf, sizeof text - len);
++                      if(len > 0)
++                              strncat(text, buf, sizeof text);
++                      else
++                              strncpy(text, buf, sizeof text);
+                       match(text);
+               }
+               break;
+@@ -467,12 +473,17 @@
+               }
+               break;
+       case XK_Tab:
++              if( filecomplete && strchr(text, ' ')!=NULL ) {
++                      matchfile( strchr(text, ' ')+1, lastkey==XK_Tab );
++                      break;
++              }
+               if(!sel)
+                       return;
+               strncpy(text, sel->text, sizeof text);
+               match(text);
+               break;
+       }
++      lastkey=ksym;
+       drawmenu();
+ }
+ 
+@@ -518,6 +529,44 @@
+ }
+ 
+ void
++matchfile(char *filestart, Bool cycling ) {
++      static int try=0, p=0;
++      wordexp_t exp;
++      int i, j, k;
++
++      if( !cycling ) { 
++              p = strlen(filestart);
++              try=0;
++      }
++      filestart[ p+1 ] = 0;
++      filestart[ p ] = '*';
++
++      wordexp(filestart, &exp, 0);
++      if( exp.we_wordc > 0 ) {
++              for(j=0,i=0; exp.we_wordv[try][i]!=0; i++,j++) {
++                      if( exp.we_wordv[try][i]==' ' ) filestart[j++]='\\';
++                      filestart[j]=exp.we_wordv[try][i];
++              }
++              filestart[j]=0;
++
++              if( cycling )
++                      try = (try+1)%exp.we_wordc;
++              else
++                      for(k=1; k<exp.we_wordc; k++)
++                              for(j=0, i=0; exp.we_wordv[k][i]; i++,j++) {
++                                      if( filestart[j]=='\\' ) j++;
++                                      if( filestart[j]!=exp.we_wordv[k][i] ) {
++                                              filestart[j]=0;
++                                              break;
++                                      }
++                              }
++      } else {
++              filestart[ p ] = 0;
++      }
++      wordfree(&exp);
++}
++
++void
+ readstdin(void) {
+       char *p, buf[1024];
+       unsigned int len = 0, max = 0;
+@@ -677,6 +726,8 @@
+               }
+               else if(!strcmp(argv[i], "-b"))
+                       topbar = False;
++              else if(!strcmp(argv[i], "-c"))
++                      filecomplete = True;
+               else if(!strcmp(argv[i], "-fn")) {
+                       if(++i < argc) font = argv[i];
+               }
+@@ -699,7 +750,7 @@
+                       eprint("dmenu-"VERSION", © 2006-2008 dmenu engineers, 
see LICENSE for details\n");
+               else
+                       eprint("usage: dmenu [-i] [-b] [-fn <font>] [-nb 
<color>] [-nf <color>]\n"
+-                             "             [-p <prompt>] [-sb <color>] [-sf 
<color>] [-v]\n");
++                             "             [-p <prompt>] [-sb <color>] [-sf 
<color>] [-v] [-c]\n");
+       if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+               fprintf(stderr, "warning: no locale support\n");
+       if(!(dpy = XOpenDisplay(0)))
+diff -r 13402291bc76 dmenu_run
+--- a/dmenu_run        Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu_run        Wed Jan 28 18:15:59 2009 -0500
+@@ -1,2 +1,2 @@
+-#!/bin/sh
+-exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe
++#!/bin/zsh
++exe=`dmenu_path | dmenu ${1+"$@"}` && exe2=${exe//\\ / } && exec ${exe2%% *} 
"${exe2#* }"
+

Attachment: pgpSGcdCQs5fn.pgp
Description: PGP signature

Reply via email to