cryptop.list: New tool for listing keys. 0.99.1
authorMark Wooding <mdw@distorted.org.uk>
Mon, 26 Dec 2011 04:19:01 +0000 (04:19 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 26 Dec 2011 04:19:01 +0000 (04:19 +0000)
Surprisingly nice output format.

Makefile.am
cryptop.list [new file with mode: 0755]

index ba7fdff..9491f71 100644 (file)
@@ -119,6 +119,7 @@ dist_pkglib_DATA    += ktype.seccure
 
 ## Commands.
 dist_pkglib_SCRIPTS    += cryptop.genkey
+dist_pkglib_SCRIPTS    += cryptop.list
 dist_pkglib_SCRIPTS    += cryptop.delkey
 dist_pkglib_SCRIPTS    += cryptop.recover
 dist_pkglib_SCRIPTS    += cryptop.info
diff --git a/cryptop.list b/cryptop.list
new file mode 100755 (executable)
index 0000000..c51f402
--- /dev/null
@@ -0,0 +1,268 @@
+#! /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 --------------------------------------------------