Xana/ xana2/ quanks
Bowling for madducks

A long time ago, I had the idea of improving the interface between shell completion and the programs being completed. The result of this was the bzr shell-complete command (or bzr s-c for short), which was never fully fleshed out, and has since fallen into disrepair.

The principles behind this are + the program is the best place to store up-to-date and accurate information + the program already knows all these things (albeit usually in unparseable forms) + duplicating information and effort is annoying

Here is an excerpt from bzr s-c, which was intended to give a comprehensive list of subcommands, paired with short descriptions:

diff:show differences in the working tree, between revisions or branches
export:export current or past revision to a destination directory or archive
get:create a new copy of a branch
help:show help on a command or other topic
ignore:ignore specified files or patterns
ignored:list ignored files and the patterns that matched them
info:show information about a working tree, branch or repository
init:make a directory into a versioned branch

There is one subcommand per line, separated from its description by a colon. Next you can invoke something like bzr s-c diff to get the possible options and arguments for the diff subcommand, although the output you would see today is broken and nearly useless.

Since I've lost faith in bzr, I'll illustrate what the output might be corresponding to topgit's tg remote if topgit supported this kind of thing:

--populate
REMOTE

This would mean that tg remote can understand the option --populate, which takes no argument, and that the first non-option argument should be a REMOTE.

REMOTE would then be defined, for example, in zsh's _topgit function as some kind of git remote which is completed in the same way you might complete a git remote for git.

The exciting part then, is that if tg remote starts taking a --decimate option, the topgit completion helper subsystem will start outputting it and _topgit will do the right thing without having to be altered.

For tg export, things are a bit more complicated, so let's have it be described in the style of the zsh completion system:

'(--collapse)--quilt:directory:_directories'
'(--collapse -b --branch)'{-b,--branch=}':branches:BRANCHES'
'(--quilt)--collapse:branch:BRANCH'

This means that --collapse and --quilt are exclusive, that -b and --branch cannot be used with --collapse, that -b and --branch are equivalent, that -b and --branch take an argument in the form of BRANCHES, that --collapse takes an argument in the form of BRANCH, and that --quilt takes an argument that's a real directory in the filesystem.

Then _topgit would have logic to interpret BRANCH as a branch, and BRANCHES as a comma-separated list of branches.

A similar idea is the one used by axp. If you invoke axp self completion zsh, it will output zsh completion functions for you. To me this seems more onerous on both the developers and the end users, but I suppose it gives you immediate flexibility that a more generic interface would lack.

Posted Tue 11 Nov 2008 09:43:59 AM EST Tags: battlefield bzr casino completion madduck quanks royale topgit zsh
VCS info in prompts

For a while, lots of people have been using their zsh prompts to display information about their current VCS (git in particular) working directories. I am no exception, though I was just doing a simple git rev-parse and git symbolic-ref in my precmd().

Starting with zsh-beta 4.3.6-dev-0+20080921-1, I am now using the vcs_info subsystem developed by Frank Terbeck. It has backends for bzr, cdv, cvs, darcs, git, hg, mtn, p4, svk, svn, and tla. These backends can be enabled or disabled via configuration.

To get it working quickly, do something like

autoload -Uz vcs_info

precmd() {
  psvar=()

  vcs_info
  [[ -n $vcs_info_msg_0_ ]] && psvar[1]="$vcs_info_msg_0_"
}

PS1="%m%(1v.%F{red}%1v%f.)%# "
Posted Sun 21 Sep 2008 11:52:25 AM EDT Tags: git prompt quanks vcs zsh
bts show

Freshly stolen from Europe:

b() {
  setopt localoptions extendedglob

  if [[ $# -eq 1 ]]; then
    case "$1" in
      ([0-9]##)
    links "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=$1"
    ;;
      (*@*)
        links "http://bugs.debian.org/cgi-bin/pkgreport.cgi?submitter=$1"
        ;;
      (*)
        links "http://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=${1%%_*}"
        ;;
    esac
  else
    print "$0 needs one argument"
  fi
}
Posted Sun 09 Dec 2007 12:31:47 PM EST Tags:
Stuffing the flash

I needed to quickly stuff a bunch of random music onto a ridiculously small vfat medium. I used this:

#!/bin/zsh
# also released under the gnocchi-ng license
zmodload -i zsh/datetime

zomg_shuffle() {
  declare -A h
  local +h -Z 5 RANDOM=$EPOCHSECONDS
  integer i
  for ((i=1; i <= $#; ++i)) { h[$i.$RANDOM]=$argv[i] }
  reply=( $h )
}

zug=( /pathtomusic/ogg/**/*.ogg )
zomg_shuffle $zug

for i in "$reply[@]"
do
 cp -v "$i" /media/tinyflashdrive/"${${i:t}//[:?\"*]/_}" || { rm -v /media/tinyflashdrive/"${${i:t}//[:?\"*]/_}" ; exit 1}
done
Posted Wed 05 Dec 2007 07:49:14 PM EST Tags:
zrockboxing out

Someone gave me an MP3 player. He did so for somewhat devious reasons, but that's outside the scope of this post. It is a SanDisk Sansa—stop thinking of pulp fantasy—and it has roughly 500 gigs too little storage space. The FAQ says such cute things as “The original firmware is recommended for charging at this time,” and “Rockbox does not currently provide either functionality so you will need to continue using the original firmware (for now) in MSC (UMS) mode to add music to the Sansa.”

Please don't tell my Sansa about this FAQ because it thinks that both of those claims are false.

I want my newfangled device to participate in the spyware fiesta known as last.fm, and Rockbox has an option for supporting that; if you turn it on, it fills a /.scrobbler.log file with tab-delimited lines for your parsing pleasure.

Rather than download additional software to cope with my new lifestyle, I whipped up the following script (released under the gnocchi-ng license) which “converts” the log to something you can just stuff into your \~/.zomg/cache file and send up to the submission server at your next appropriate invocation of zomg. I'm afraid you'll have to handle any timestamp sorting by hand, but if you script it, be a dear and implement some kind of cache file locking.

#!/bin/zsh
# Copyright (C) 2007  Clint Adams.  All rights reserved.
# This program has no name and is released under the terms of
# the gnocchi-ng license.

audioscrobbler_urlencode() {
  if (( $+options[multibyte] )); then
    setopt localoptions extendedglob nomultibyte
  else
    setopt localoptions extendedglob
  fi

  input=( ${(s::)1} )
  print -- ${(j::)input/(#b)([^A-Za-z0-9_.!~*\'\(\)-])/%$(([##16]#match))}
}

audioscrobbler_constructquery() {
  local sid="$1"
  local artist=$(audioscrobbler_urlencode "$2")
  local track=$(audioscrobbler_urlencode "$3")
  local album=$(audioscrobbler_urlencode "$4")
  local mbid=$(audioscrobbler_urlencode "$5")
  local length=$(audioscrobbler_urlencode "$6")
  local ttime=$(audioscrobbler_urlencode "$7")
  local source="$8"
  local tracknum="$9"

  reply=("&s=${sid}" "&a[0]=${artist}&t[0]=${track}&i[0]=${ttime}&o[0]=${source}&r[0]=&l[0]=${length}&b[0]=${album}&n[0]=${tracknum}&m[0]=${mbid}"$'\0')
}

while read -r line
do
  local -a field

  field=("${(@ps:\t:)${line}}")

  if [[ $field[6] == L ]]; then
    audioscrobbler_constructquery "" "$field[1]" "$field[3]" "$field[2]" "" "$field[5]" "$field[7]" P "$field[4]"
    print "$reply[2]"
  fi
done <<(grep -v '^#' .scrobbler.log)
Posted Thu 25 Oct 2007 11:11:25 PM EDT Tags: rockbox
Too many colons

The zsh-lovers man page contains the following example.

# Show me all the .c files for which there doesn't exist a .o file.
$ c=(*.c) o=(*.o(N)) eval 'ls ${${c:#(${~${(j:|:)${o:r}}}).c}:?done}'

What's with the ugly dollar-sign prompt? I'd do it this way instead.

print *.c(e_' &#33; -e &#36;REPLY:r.o '_)
Posted Sat 06 Oct 2007 05:16:51 PM EDT Tags:
Having your caret and eating it too

Let's say you're a French person who has EXTENDED_GLOB on and types HEAD^ all the time, but can't be bothered to type a backslash before the caret. Personally, I have no problem typing HEAD\^, but I probably only do that twice a day.

Here are a few/several/many (depending on how you count) “solutions”. They all have their downsides. Finding out their side effects is an exercise for the reader.

Number one: setopt noextendedglob

Number two: setopt nonomatch

Number three: alias git='noglob git'

Number four: custom ZLE widget:

accept_line_with_headcaret () {
  if  &#36;&#123;BUFFER&#125; &#61; git&#42;HEAD&#92;&#94;&#42; ; then
    BUFFER="${BUFFER//HEAD\^/HEAD\\^}"
  fi

  zle .accept-line
}
zle -N accept-line accept_line_with_headcaret

I'll just keep hitting the backslash key.

Posted Thu 04 Oct 2007 03:17:15 PM EDT Tags:
ZOMG goes cutting-edge

ZOMG has now been updated for Audioscrobbler 1.2, and is now in a Mercurial repo.

Users should be aware that the caching of credentials and the caching of unsubmitted tracks have both changed in incompatible ways, so be sure to flush or remove your old data before upgrading.

Probably the only exciting user-visible change is that currently-playing tracks will now show up on last.fm as “now listening”.

Posted Sun 12 Aug 2007 02:02:32 PM EDT Tags: zomg
Meaningless numbers

With these shells as sh, results from the current posh testsuite:

ShellFailedPassed
bash25 (22 unexpected)140 (1 unexpected)
dash47 (45 unexpected)118 (2 unexpected)
ksh36 (32 unexpected)129
pdksh16 (12 unexpected)149
posh4 (as expected)161

This may reflect more on the unsuitability of the posh testsuite for other shells than the failure of those shells to conform to the expectations set by Debian policy. Patches welcome.

Posted Fri 17 Nov 2006 10:10:20 PM EST Tags:
ABC, easy as 321, simple as fa-sol-la

Far too late after #394749 got fixed, ZOMG switched from mpg321 to mpg123.

The reason that this is so exciting is that mpg123 supports output buffering, whereas mpg321 has been sucking ass for over five years.

Posted Tue 14 Nov 2006 10:02:35 PM EST Tags: zomg