chiark / gitweb /
cryptop.list: New tool for listing keys.
[distorted-keys] / cryptop.list
1 #! /bin/sh
2 ###
3 ### List a user's keys
4 ###
5 ### (c) 2011 Mark Wooding
6 ###
7
8 ###----- Licensing notice ---------------------------------------------------
9 ###
10 ### This file is part of the distorted.org.uk key management suite.
11 ###
12 ### distorted-keys is free software; you can redistribute it and/or modify
13 ### it under the terms of the GNU General Public License as published by
14 ### the Free Software Foundation; either version 2 of the License, or
15 ### (at your option) any later version.
16 ###
17 ### distorted-keys is distributed in the hope that it will be useful,
18 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ### GNU General Public License for more details.
21 ###
22 ### You should have received a copy of the GNU General Public License
23 ### along with distorted-keys; if not, write to the Free Software Foundation,
24 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 set -e
27 case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
28 . "$KEYSLIB"/keyfunc.sh
29
30 defhelp <<HELP
31 [-Ha] [-C COLUMN,...] [-u USER] [PATTERN ...]
32 List stored keys.  If PATTERNs are given, only list keys whose labels match
33 at least one PATTERN.
34
35 Options:
36   -H            Don't show the column headings (useful for scripts).
37   -C COLUMN,... Select the columns to show.
38   -a            Show keys owned by all users.
39   -u USER       Show keys owned by USER.
40
41 A COLUMN spec consists of a column name and an optional column width,
42 separated by a colon.  The widths of omitted columns are computed
43 automatically.
44
45 Columns:
46   flags         Various flags for the key.  (Unset flags are shown as \`.')
47                   R     key has recovery information
48                   !     key nub needs recovery
49   label         The key's label (relative to its owner).
50   profile       The key's profile name.
51   recov         Recovery key labels, comma-separated.
52 HELP
53
54 ###--------------------------------------------------------------------------
55 ### Column types.
56
57 ALLCOLS=""
58 defcol () { ALLCOLS=${ALLCOLS:+$ALLCOLS,}$1; }
59
60 defcol flags
61 col_flags () {
62   label=$1
63   flags=""
64
65   rflag=.
66   for i in recov/*/current/$label.recov; do
67     if [ -f "$i" ]; then rflag=R; break; fi
68   done
69   flags=$flags$rflag
70
71   bangflag=!
72   if [ -f nub/$label ]; then bangflag=.; fi
73   flags=$flags$bangflag
74
75   echo "$flags"
76 }
77
78 defcol label
79 col_label () {
80   label=$1
81
82   case $all,$label in
83     nil,$USERV_USER*) plabel=${label#*/} ;;
84     t,*) plabel=${label%%/*}:${label#*/} ;;
85   esac
86   echo "$plabel"
87 }
88
89 defcol profile
90 col_profile () {
91   label=$1
92
93   readmeta store/$label
94   echo "$profile"
95 }
96
97 defcol recov
98 col_recov () {
99   label=$1
100   recov=""
101
102   for i in recov/*; do
103     if [ -f "$i/current/$label.recov" ]; then
104       recov=${recov:+$recov,}${i#recov/}
105     fi
106   done
107   echo "$recov"
108 }
109
110 ###--------------------------------------------------------------------------
111 ### Main program.
112
113 ## Parse the command-line options.  Remaining arguments are glob patterns.
114 header=t
115 cols=$ALLCOLS
116 all=nil
117 user=$USERV_USER
118 while getopts "HaC:u:" opt; do
119   case "$opt" in
120     a) all=t ;;
121     H) header=nil ;;
122     C) cols=$OPTARG ;;
123     u) user=$OPTARG ;;
124     *) usage_err ;;
125   esac
126 done
127 shift $(( $OPTIND - 1 ))
128 case $# in 0) set "*" ;; esac
129 cd $KEYS
130
131 ## First pass: validate the column specifications.  Translate all bare column
132 ## names into explicit `NAME+0' forms.  Decide whether we need a width-
133 ## measuring pass.
134 calcwd=nil
135 cc=$cols
136 wdcols=""
137 while :; do
138
139   ## Pick off the next column name.  If none are left, leave the loop.
140   case "$cc" in
141     *,*) col=${cc%%,*} cc=${cc#*,} ;;
142     ?*) col=$cc cc="" ;;
143     *) break ;;
144   esac
145
146   ## Extract the column name for later.
147   name=${col%[:=+]*}
148
149   ## If we have a minimum width or no width, we need a measuring pass.
150   case "$col" in *[:=]*) ;; *) calcwd=t ;; esac
151
152   ## Check the column width is valid.  Build the new column list with
153   ## explicit widths.
154   case "$col" in
155     *[:=+]*)
156       wd=${col#*[:=+]}
157       wdcols=${wdcols:+$wdcols,}$col
158       checknumber "column width" "$wd"
159       ;;
160     *)
161       wdcols=${wdcols:+$wdcols,}$col+0
162       ;;
163   esac
164
165   ## Check the column name.
166   case ,$ALLCOLS, in
167     *,"$name",*) ;;
168     *) echo >&2 "$quis: unknown column \`$name'"; exit 1 ;;
169   esac
170 done
171
172 ## Second and third pass: find the keys, compute their properties and either
173 ## measure column widths or display the results.
174 while :; do
175
176   ## Decide whether we need to display a header.  (We need this on both
177   ## passes, because it may contribute to width.)
178   doheader=$header
179
180   ## Find the metadata files.  This tells us where the keys are.
181   case $all in
182     t) dir=store ;;
183     nil) dir=store/$user ;;
184   esac
185   metas=$(find store -type f -name meta)
186
187   ## Work through the keys we found.
188   while :; do
189
190     ## If we're not doing a header line, read the next metadata file name.
191     ## Check that it matches at least one pattern.
192     case $doheader in
193       nil)
194         if ! read meta; then break; fi
195         label=${meta#store/}; label=${label%/meta}
196         matchp=nil
197         for pat in "$@"; do
198           case "$label" in $pat) matchp=t; break ;; esac
199         done
200         case $matchp in nil) continue ;; esac
201         ;;
202     esac
203
204     ## Now iterate over the columns.  If we're calculating widths, use the
205     ## ones we worked out last time, and clear the list so we can build a new
206     ## one as we go.
207     case $calcwd in t) cols=$wdcols ;; esac
208     cc=$cols wdcols="" sep=""
209     while :; do
210
211       ## Pick off the next column spec.
212       case "$cc" in
213         *,*) col=${cc%%,*} cc=${cc#*,} ;;
214         ?*) col=$cc cc="" ;;
215         *) break ;;
216       esac
217
218       ## Work out the column name.
219       name=${col%[:=+]*} wd=${col#*[:=+]}
220
221       ## Work out the value.  If this is a header line, then it's just the
222       ## column name in upper case; otherwise we have work to do.
223       case $doheader in
224         t) value=$(echo $name | tr a-z A-Z) ;;
225         nil) value=$(col_$name $label) ;;
226       esac
227
228       ## Work out what to do about it.  If we're measuring, then update our
229       ## idea of the column width.  If we're printing, work out a format.
230       case $calcwd,$col in
231         t,*[:=]*)
232           wdcols=${wdcols:+$wdcols,}$col
233           ;;
234         t,*+*)
235           colwd=$(( $(echo "$value" | wc -c) - 1 ))
236           if [ $colwd -gt $wd ]; then wd=$colwd; fi
237           wdcols=${wdcols:+$wdcols,}$name+$wd
238           ;;
239         nil,*[=+]*)
240           fmt="%-${wd}.${wd}s"
241           ;;
242         nil,*:*)
243           fmt="%-${wd}s"
244           ;;
245       esac
246
247       ## If we're printing, then print something.  Leave space between the
248       ## columns.
249       case $calcwd in nil) printf "$sep$fmt" "$value"; sep="  " ;; esac
250     done
251
252     ## The next line will certainly not be a header.
253     doheader=nil
254
255     ## Start a new line if we're printing.
256     case $calcwd in nil) printf "\n" ;; esac
257   done <<EOF
258 $metas
259 EOF
260
261   ## If we were measuring, go round again and print; otherwise we're done.
262   case $calcwd in
263     t) calcwd=nil ;;
264     nil) break ;;
265   esac
266 done
267
268 ###----- That's all, folks --------------------------------------------------