On Tue, Aug 17, 2010 at 02:15:13AM +0200, LuX wrote:
On Sun, 15 Aug 2010 18:51:55 -0400, Kris Maglione wrote:
Yes, I already fixed that problem when I made it into an example
file for distribution. Attached.

I have a few unessential remarks, if you allow me:

Of course.

- Although I don't understand this, it seems that the last 'tail -1' is
no longer needed.

Ah, you're right. I'd intended to remove that. Fortunately this changeset hasn't left my patch queue yet. :)

- Since you replaced the call to dirname by a 'sub' command just after
 the comment '# Strip the trailing filename', it happens that when I
 type 'ls /' in the input area, it is the content of the $HOME
 directory instead of / which is displayed by wimenu. Here is a fix:

             # Strip the trailing filename
             if(match(str, "^/[^/]*$"))
                 str = "/" str
             sub("(/|^)[^/]*$", "", str)

This would be better:

    sub("[^/]+$", "", str)

I'll update the script.

- I have read somewhere that /dev/shm was a good place to put files
 used by programs to discuss one with each others. This would apply
to "fifo" in this case. I don't know if you have an opinion on this.

That would not help in this case. For one thing, /dev/shm is very system specific. Only certain Linux distributions support it. For another, its value is in that it's a memory filesystem, so file contents are stored in RAM rather than on disk. But our file is a FIFO, anyway. Its contents never touch the disk. It would probably be better in ~/.wmii/menu_fifo or the like, though.

- I liked more the previous behaviour, when the script ends by sending
 the input string to stdout (like wimenu does) so that it can can be
 processed by some independent script like for example this one:

I think that you're right in principle. However, I'm going to leave the example as is because it illustrates the purpose of the script better and as it's fully self-contained, it works without modification.

In addition I enjoyed adding another minor feature: when a list of
options has been declared in the script for the initial command of the
input line, every time a '-' is typed at the beginning of a new
argument in a line starting with that command, this list of options is
displayed instead of a list of files. I find it convenient for some
commands, like 'lp' or 'pdfnup' which accept many options, useful to
me but that I do not use to remember.

I think this is a good idea, and it was what my help file example was meant to suggest. I'd suggest some slight changes, though:

--- menu_thing  2010-08-16 20:32:26.000000000 -0400
+++ -   2010-08-16 20:49:19.051116173 -0400
@@ -5,18 +5,18 @@
 # Program name completion requires that a program list already
 # exist in $(wmiir namespace)/.proglist
-fifo="/dev/shm/wim_$USER"
+fifo=$HOME/.wmii/menu_fifo
 mkfifo $fifo 2>/dev/null
script=$(cat <<'!'
     BEGIN {
-        progs = "cat $(wmiir namespace)/.proglist"
+        progs = read("cat $(wmiir namespace)/.proglist")
# Favorite options for some programs
         opt["lp"] = "-o media=a4\n-o landscape\n-o 
sides=two-sided-long-edge\nsides=two-sided-short-edge\n-o number-up=N\n"
# Print the first set of completions to wimenu’s fifo
-        print read(progs) >fifo
+        print progs >fifo
         fflush(fifo)
     }
@@ -25,13 +25,17 @@
         # Skip the trailing part of the command.
         # If there is none, this is the result.
         if (!getline rest) {
-        print
-        exit
+            print
+            exit
         }
if (!match($0, /.*[ \t]/))
             # First argument, provide the program list
-            update(0, progs)
+            update(0, "", progs)
+        else if($NF ~ /^-/)
+ # If the last argument starts with a -, list + # options declared in opt instead of files
+            update(RLENGTH, "", opt[$1])
         else {
             # Set the offset to the location of the last
             # space, and save that part of the completion
@@ -46,31 +50,21 @@
             # If the last component of the path begins with
             # a ., include hidden files
             arg = ""
-            if(match(str, "(^|/)\\.[^/]*$"))
-                arg = "-A"
+            if(str ~ "(^|/)\\.[^/]*$")
+                arg = "-A "
# Substitute ~/ for $HOME/
             sub("^~/", ENVIRON["HOME"] "/", str)
# Strip the trailing filename
-            if(match(str, "^/[^/]*$"))
-                str = "/" str
-            sub("(/|^)[^/]*$", "", str)
-
- # If the last argument starts with a -, list - # options declared in opt instead of files
-            lscmd = "ls " arg quote(str)
-            if(match($0, "\\ -[^\\ ]*$")) {
-                lsopt = opt[gensub(/\ .*/, "", 1)]
-                lscmd = "echo -n \"" lsopt "\""
-            }
+            sub("[^/]+$", "", str)
- update(offset, lscmd)
+            update(offset, "ls " arg quote(str))
         }
     }
# Push out a new set of completions
-    function update(offset, cmd) {
+    function update(offset, cmd, cmpl) {
         # Only push out the completion if the offset or the
         # ls command has changed. The behavior will be the
         # same regardless, but this is a minor optimization
@@ -78,7 +72,8 @@
             loffset = offset
             lcmd = cmd
- cmpl = read(cmd)
+            if(cmd && cmpl == "")
+                cmpl = read(cmd)
             print offset >fifo
             print cmpl >fifo
             fflush(fifo)


Note: It would be better to declare the 'opt' array outside this
script, in a sort of configuration file, but I don't know how to do
this.

The following will do what you want:

cat >>$HOME/.wmii/menu_opts <<!
lp:
    -o media=a4
    -o landscape
    -o sides=two-sided-long-edge
    -o sides=two-sided-short-edge
    -o number-up=N
!

#!/bin/sh
# This script will launch wimenu and provide command
# completion for the first argument and filename completion
# for each following argument, and pass the result to stdout.
# Program name completion requires that a program list already
# exist in $(wmiir namespace)/.proglist

export opts=$HOME/.wmii/menu_opts
fifo=$HOME/.wmii/menu_fifo
mkfifo $fifo 2>/dev/null

script=$(cat <<'!'
    BEGIN {
        progs = "cat $(wmiir namespace)/.proglist"

        # Print the first set of completions to wimenu’s fifo
        print read(progs) >fifo
        fflush(fifo)
    }

    # Process the input and provide the completions
    {
        # Skip the trailing part of the command.
        # If there is none, this is the result.
        if (!getline rest) {
            print
            exit
        }

        program = $1
        sub(/"/, "\\\"", program)

        if (!match($0, /.*[ \t]/))
            # First argument, provide the program list
            update(0, progs)
        else if($NF ~ /^-/)
# If the last argument starts with a -, list # options declared in opt instead of files
            update(RLENGTH,
                "awk <$opts '$0 == \"" program ":\", /^$/ {" \
                    "if(/^[ \\t]/) { sub(/^[ \\t]+/, \"\"); print }" \
                "}'")
        else {
            # Set the offset to the location of the last
            # space, and save that part of the completion
            offset = RLENGTH
            str = substr($0, offset + 1)

            # If we are completing a sub-directory, adjust
            # the offset to the position of the last /
            if (match(str, ".*/"))
                offset += RLENGTH

            # If the last component of the path begins with
            # a ., include hidden files
            arg = ""
            if(str ~ "(^|/)\\.[^/]*$")
                arg = "-A "

            # Substitute ~/ for $HOME/
            sub("^~/", ENVIRON["HOME"] "/", str)

            # Strip the trailing filename
            sub("[^/]+$", "", str)

            update(offset, "ls " arg quote(str))
        }
    }

    # Push out a new set of completions
    function update(offset, cmd, cmpl) {
        # Only push out the completion if the offset or the
        # ls command has changed. The behavior will be the
        # same regardless, but this is a minor optimization
        if (offset != loffset || cmd != lcmd) {
            loffset = offset
            lcmd = cmd

            print offset >fifo
            print read(cmd) >fifo
            fflush(fifo)
        }
    }

    # Quote a string. This should work in any Bourne
    # or POSIX compatible shell.
    function quote(str) {
        if (!match(str, /[\[\](){}$'"^#~!&;*?|<>]/))
            return str
        gsub(/\\/, "'\\\\'", str)
        gsub(/'/, "'\\''", str)
        return "'" str "'"
    }

    # Read the output of a command and return it
    function read(cmd) {
        if (cmd in cache)
            return cache[cmd]
        print "read " cmd
        res = ""
        while (cmd | getline)
            res = res quote($0) "\n"
        close(cmd)
        return cache[cmd] = res
    }
!
)
wimenu -h "$HOME/.bash_history" -n 5000 -c "$@" <$fifo | awk -v "fifo=$fifo" 
"$script"

--
Kris Maglione

Sufficiently advanced political correctness is indistinguishable from
sarcasm.
        --Eric Naggum


Reply via email to