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