cryptop.list: Fix up the column-spec documentation.
[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; the
42 separator character determines the behaviour as shown below. The default is
43 \`+0'.
44 NAME=WIDTH Exact width: truncate the contents to fit if necessary.
45 NAME:WIDTH Minimum width: spill into the next column (breaking the
46 alignment) if necessary.
47 NAME+WIDTH Minimum width: if a value won't fit then make the entire
48 column wider.
49
50 Columns names:
51 flags Various flags for the key. (Unset flags are shown as \`.')
52 R key has recovery information
53 ! key nub needs recovery
54 label The key's label (relative to its owner).
55 profile The key's profile name.
56 recov Recovery key labels, comma-separated.
57 HELP
58
59 ###--------------------------------------------------------------------------
60 ### Column types.
61
62 ALLCOLS=""
63 defcol () { ALLCOLS=${ALLCOLS:+$ALLCOLS,}$1; }
64
65 defcol flags
66 col_flags () {
67 label=$1
68 flags=""
69
70 rflag=.
71 for i in recov/*/current/$label.recov; do
72 if [ -f "$i" ]; then rflag=R; break; fi
73 done
74 flags=$flags$rflag
75
76 bangflag=!
77 if [ -f nub/$label ]; then bangflag=.; fi
78 flags=$flags$bangflag
79
80 echo "$flags"
81 }
82
83 defcol label
84 col_label () {
85 label=$1
86
87 case $all,$label in
88 nil,$USERV_USER*) plabel=${label#*/} ;;
89 t,*) plabel=${label%%/*}:${label#*/} ;;
90 esac
91 echo "$plabel"
92 }
93
94 defcol profile
95 col_profile () {
96 label=$1
97
98 readmeta store/$label
99 echo "$profile"
100 }
101
102 defcol recov
103 col_recov () {
104 label=$1
105 recov=""
106
107 for i in recov/*; do
108 if [ -f "$i/current/$label.recov" ]; then
109 recov=${recov:+$recov,}${i#recov/}
110 fi
111 done
112 echo "$recov"
113 }
114
115 ###--------------------------------------------------------------------------
116 ### Main program.
117
118 ## Parse the command-line options. Remaining arguments are glob patterns.
119 header=t
120 cols=$ALLCOLS
121 all=nil
122 user=$USERV_USER
123 while getopts "HaC:u:" opt; do
124 case "$opt" in
125 a) all=t ;;
126 H) header=nil ;;
127 C) cols=$OPTARG ;;
128 u) user=$OPTARG ;;
129 *) usage_err ;;
130 esac
131 done
132 shift $(( $OPTIND - 1 ))
133 case $# in 0) set "*" ;; esac
134 cd $KEYS
135
136 ## First pass: validate the column specifications. Translate all bare column
137 ## names into explicit `NAME+0' forms. Decide whether we need a width-
138 ## measuring pass.
139 calcwd=nil
140 cc=$cols
141 wdcols=""
142 while :; do
143
144 ## Pick off the next column name. If none are left, leave the loop.
145 case "$cc" in
146 *,*) col=${cc%%,*} cc=${cc#*,} ;;
147 ?*) col=$cc cc="" ;;
148 *) break ;;
149 esac
150
151 ## Extract the column name for later.
152 name=${col%[:=+]*}
153
154 ## If we have a minimum width or no width, we need a measuring pass.
155 case "$col" in *[:=]*) ;; *) calcwd=t ;; esac
156
157 ## Check the column width is valid. Build the new column list with
158 ## explicit widths.
159 case "$col" in
160 *[:=+]*)
161 wd=${col#*[:=+]}
162 wdcols=${wdcols:+$wdcols,}$col
163 checknumber "column width" "$wd"
164 ;;
165 *)
166 wdcols=${wdcols:+$wdcols,}$col+0
167 ;;
168 esac
169
170 ## Check the column name.
171 case ,$ALLCOLS, in
172 *,"$name",*) ;;
173 *) echo >&2 "$quis: unknown column \`$name'"; exit 1 ;;
174 esac
175 done
176
177 ## Second and third pass: find the keys, compute their properties and either
178 ## measure column widths or display the results.
179 while :; do
180
181 ## Decide whether we need to display a header. (We need this on both
182 ## passes, because it may contribute to width.)
183 doheader=$header
184
185 ## Find the metadata files. This tells us where the keys are.
186 case $all in
187 t) dir=store ;;
188 nil) dir=store/$user ;;
189 esac
190 metas=$(find store -type f -name meta)
191
192 ## Work through the keys we found.
193 while :; do
194
195 ## If we're not doing a header line, read the next metadata file name.
196 ## Check that it matches at least one pattern.
197 case $doheader in
198 nil)
199 if ! read meta; then break; fi
200 label=${meta#store/}; label=${label%/meta}
201 matchp=nil
202 for pat in "$@"; do
203 case "$label" in $pat) matchp=t; break ;; esac
204 done
205 case $matchp in nil) continue ;; esac
206 ;;
207 esac
208
209 ## Now iterate over the columns. If we're calculating widths, use the
210 ## ones we worked out last time, and clear the list so we can build a new
211 ## one as we go.
212 case $calcwd in t) cols=$wdcols ;; esac
213 cc=$cols wdcols="" sep=""
214 while :; do
215
216 ## Pick off the next column spec.
217 case "$cc" in
218 *,*) col=${cc%%,*} cc=${cc#*,} ;;
219 ?*) col=$cc cc="" ;;
220 *) break ;;
221 esac
222
223 ## Work out the column name.
224 name=${col%[:=+]*} wd=${col#*[:=+]}
225
226 ## Work out the value. If this is a header line, then it's just the
227 ## column name in upper case; otherwise we have work to do.
228 case $doheader in
229 t) value=$(echo $name | tr a-z A-Z) ;;
230 nil) value=$(col_$name $label) ;;
231 esac
232
233 ## Work out what to do about it. If we're measuring, then update our
234 ## idea of the column width. If we're printing, work out a format.
235 case $calcwd,$col in
236 t,*[:=]*)
237 wdcols=${wdcols:+$wdcols,}$col
238 ;;
239 t,*+*)
240 colwd=$(( $(echo "$value" | wc -c) - 1 ))
241 if [ $colwd -gt $wd ]; then wd=$colwd; fi
242 wdcols=${wdcols:+$wdcols,}$name+$wd
243 ;;
244 nil,*[=+]*)
245 fmt="%-${wd}.${wd}s"
246 ;;
247 nil,*:*)
248 fmt="%-${wd}s"
249 ;;
250 esac
251
252 ## If we're printing, then print something. Leave space between the
253 ## columns.
254 case $calcwd in nil) printf "$sep$fmt" "$value"; sep=" " ;; esac
255 done
256
257 ## The next line will certainly not be a header.
258 doheader=nil
259
260 ## Start a new line if we're printing.
261 case $calcwd in nil) printf "\n" ;; esac
262 done <<EOF
263 $metas
264 EOF
265
266 ## If we were measuring, go round again and print; otherwise we're done.
267 case $calcwd in
268 t) calcwd=nil ;;
269 nil) break ;;
270 esac
271 done
272
273 ###----- That's all, folks --------------------------------------------------