--- /dev/null
+#! /bin/sh
+###
+### List a user's keys
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+[-Ha] [-C COLUMN,...] [-u USER] [PATTERN ...]
+List stored keys. If PATTERNs are given, only list keys whose labels match
+at least one PATTERN.
+
+Options:
+ -H Don't show the column headings (useful for scripts).
+ -C COLUMN,... Select the columns to show.
+ -a Show keys owned by all users.
+ -u USER Show keys owned by USER.
+
+A COLUMN spec consists of a column name and an optional column width,
+separated by a colon. The widths of omitted columns are computed
+automatically.
+
+Columns:
+ flags Various flags for the key. (Unset flags are shown as \`.')
+ R key has recovery information
+ ! key nub needs recovery
+ label The key's label (relative to its owner).
+ profile The key's profile name.
+ recov Recovery key labels, comma-separated.
+HELP
+
+###--------------------------------------------------------------------------
+### Column types.
+
+ALLCOLS=""
+defcol () { ALLCOLS=${ALLCOLS:+$ALLCOLS,}$1; }
+
+defcol flags
+col_flags () {
+ label=$1
+ flags=""
+
+ rflag=.
+ for i in recov/*/current/$label.recov; do
+ if [ -f "$i" ]; then rflag=R; break; fi
+ done
+ flags=$flags$rflag
+
+ bangflag=!
+ if [ -f nub/$label ]; then bangflag=.; fi
+ flags=$flags$bangflag
+
+ echo "$flags"
+}
+
+defcol label
+col_label () {
+ label=$1
+
+ case $all,$label in
+ nil,$USERV_USER*) plabel=${label#*/} ;;
+ t,*) plabel=${label%%/*}:${label#*/} ;;
+ esac
+ echo "$plabel"
+}
+
+defcol profile
+col_profile () {
+ label=$1
+
+ readmeta store/$label
+ echo "$profile"
+}
+
+defcol recov
+col_recov () {
+ label=$1
+ recov=""
+
+ for i in recov/*; do
+ if [ -f "$i/current/$label.recov" ]; then
+ recov=${recov:+$recov,}${i#recov/}
+ fi
+ done
+ echo "$recov"
+}
+
+###--------------------------------------------------------------------------
+### Main program.
+
+## Parse the command-line options. Remaining arguments are glob patterns.
+header=t
+cols=$ALLCOLS
+all=nil
+user=$USERV_USER
+while getopts "HaC:u:" opt; do
+ case "$opt" in
+ a) all=t ;;
+ H) header=nil ;;
+ C) cols=$OPTARG ;;
+ u) user=$OPTARG ;;
+ *) usage_err ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+case $# in 0) set "*" ;; esac
+cd $KEYS
+
+## First pass: validate the column specifications. Translate all bare column
+## names into explicit `NAME+0' forms. Decide whether we need a width-
+## measuring pass.
+calcwd=nil
+cc=$cols
+wdcols=""
+while :; do
+
+ ## Pick off the next column name. If none are left, leave the loop.
+ case "$cc" in
+ *,*) col=${cc%%,*} cc=${cc#*,} ;;
+ ?*) col=$cc cc="" ;;
+ *) break ;;
+ esac
+
+ ## Extract the column name for later.
+ name=${col%[:=+]*}
+
+ ## If we have a minimum width or no width, we need a measuring pass.
+ case "$col" in *[:=]*) ;; *) calcwd=t ;; esac
+
+ ## Check the column width is valid. Build the new column list with
+ ## explicit widths.
+ case "$col" in
+ *[:=+]*)
+ wd=${col#*[:=+]}
+ wdcols=${wdcols:+$wdcols,}$col
+ checknumber "column width" "$wd"
+ ;;
+ *)
+ wdcols=${wdcols:+$wdcols,}$col+0
+ ;;
+ esac
+
+ ## Check the column name.
+ case ,$ALLCOLS, in
+ *,"$name",*) ;;
+ *) echo >&2 "$quis: unknown column \`$name'"; exit 1 ;;
+ esac
+done
+
+## Second and third pass: find the keys, compute their properties and either
+## measure column widths or display the results.
+while :; do
+
+ ## Decide whether we need to display a header. (We need this on both
+ ## passes, because it may contribute to width.)
+ doheader=$header
+
+ ## Find the metadata files. This tells us where the keys are.
+ case $all in
+ t) dir=store ;;
+ nil) dir=store/$user ;;
+ esac
+ metas=$(find store -type f -name meta)
+
+ ## Work through the keys we found.
+ while :; do
+
+ ## If we're not doing a header line, read the next metadata file name.
+ ## Check that it matches at least one pattern.
+ case $doheader in
+ nil)
+ if ! read meta; then break; fi
+ label=${meta#store/}; label=${label%/meta}
+ matchp=nil
+ for pat in "$@"; do
+ case "$label" in $pat) matchp=t; break ;; esac
+ done
+ case $matchp in nil) continue ;; esac
+ ;;
+ esac
+
+ ## Now iterate over the columns. If we're calculating widths, use the
+ ## ones we worked out last time, and clear the list so we can build a new
+ ## one as we go.
+ case $calcwd in t) cols=$wdcols ;; esac
+ cc=$cols wdcols="" sep=""
+ while :; do
+
+ ## Pick off the next column spec.
+ case "$cc" in
+ *,*) col=${cc%%,*} cc=${cc#*,} ;;
+ ?*) col=$cc cc="" ;;
+ *) break ;;
+ esac
+
+ ## Work out the column name.
+ name=${col%[:=+]*} wd=${col#*[:=+]}
+
+ ## Work out the value. If this is a header line, then it's just the
+ ## column name in upper case; otherwise we have work to do.
+ case $doheader in
+ t) value=$(echo $name | tr a-z A-Z) ;;
+ nil) value=$(col_$name $label) ;;
+ esac
+
+ ## Work out what to do about it. If we're measuring, then update our
+ ## idea of the column width. If we're printing, work out a format.
+ case $calcwd,$col in
+ t,*[:=]*)
+ wdcols=${wdcols:+$wdcols,}$col
+ ;;
+ t,*+*)
+ colwd=$(( $(echo "$value" | wc -c) - 1 ))
+ if [ $colwd -gt $wd ]; then wd=$colwd; fi
+ wdcols=${wdcols:+$wdcols,}$name+$wd
+ ;;
+ nil,*[=+]*)
+ fmt="%-${wd}.${wd}s"
+ ;;
+ nil,*:*)
+ fmt="%-${wd}s"
+ ;;
+ esac
+
+ ## If we're printing, then print something. Leave space between the
+ ## columns.
+ case $calcwd in nil) printf "$sep$fmt" "$value"; sep=" " ;; esac
+ done
+
+ ## The next line will certainly not be a header.
+ doheader=nil
+
+ ## Start a new line if we're printing.
+ case $calcwd in nil) printf "\n" ;; esac
+ done <<EOF
+$metas
+EOF
+
+ ## If we were measuring, go round again and print; otherwise we're done.
+ case $calcwd in
+ t) calcwd=nil ;;
+ nil) break ;;
+ esac
+done
+
+###----- That's all, folks --------------------------------------------------