]>
Commit | Line | Data |
---|---|---|
1ffe90c5 TF |
1 | # Copyright (c) 2013, Aneurin Price <aneurin.price@gmail.com> |
2 | ||
3 | # Permission is hereby granted, free of charge, to any person | |
4 | # obtaining a copy of this software and associated documentation | |
5 | # files (the "Software"), to deal in the Software without | |
6 | # restriction, including without limitation the rights to use, | |
7 | # copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | # copies of the Software, and to permit persons to whom the | |
9 | # Software is furnished to do so, subject to the following | |
10 | # conditions: | |
11 | ||
12 | # The above copyright notice and this permission notice shall be | |
13 | # included in all copies or substantial portions of the Software. | |
14 | ||
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
17 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
19 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
20 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
21 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
22 | # OTHER DEALINGS IN THE SOFTWARE. | |
23 | ||
24 | if [[ -w /dev/zfs ]]; then | |
25 | __ZFS_CMD="zfs" | |
26 | __ZPOOL_CMD="zpool" | |
27 | else | |
28 | __ZFS_CMD="sudo zfs" | |
29 | __ZPOOL_CMD="sudo zpool" | |
30 | fi | |
31 | ||
32 | __zfs_get_commands() | |
33 | { | |
34 | $__ZFS_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | cut -f1 -d '|' | uniq | |
35 | } | |
36 | ||
37 | __zfs_get_properties() | |
38 | { | |
39 | $__ZFS_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all name space | |
40 | } | |
41 | ||
42 | __zfs_get_editable_properties() | |
43 | { | |
44 | $__ZFS_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' | |
45 | } | |
46 | ||
47 | __zfs_get_inheritable_properties() | |
48 | { | |
49 | $__ZFS_CMD get 2>&1 | awk '$3 == "YES" {print $1}' | |
50 | } | |
51 | ||
52 | __zfs_list_datasets() | |
53 | { | |
54 | $__ZFS_CMD list -H -o name -t filesystem,volume | |
55 | } | |
56 | ||
57 | __zfs_list_filesystems() | |
58 | { | |
59 | $__ZFS_CMD list -H -o name -t filesystem | |
60 | } | |
61 | ||
62 | __zfs_match_snapshot() | |
63 | { | |
64 | local base_dataset=${cur%@*} | |
65 | if [[ $base_dataset != $cur ]] | |
66 | then | |
67 | $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset | |
68 | else | |
69 | $__ZFS_CMD list -H -o name -t filesystem,volume | awk '{print $1"@"}' | |
70 | fi | |
71 | } | |
72 | ||
73 | __zfs_match_explicit_snapshot() | |
74 | { | |
75 | local base_dataset=${cur%@*} | |
76 | if [[ $base_dataset != $cur ]] | |
77 | then | |
78 | $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset | |
79 | fi | |
80 | } | |
81 | ||
82 | __zfs_match_multiple_snapshots() | |
83 | { | |
84 | local existing_opts=$(expr "$cur" : '\(.*\)[%,]') | |
85 | if [[ $existing_opts ]] | |
86 | then | |
87 | local base_dataset=${cur%@*} | |
88 | if [[ $base_dataset != $cur ]] | |
89 | then | |
90 | local cur=${cur##*,} | |
91 | if [[ $cur =~ ^%|%.*% ]] | |
92 | then | |
93 | # correct range syntax is start%end | |
94 | return 1 | |
95 | fi | |
96 | local range_start=$(expr "$cur" : '\(.*%\)') | |
97 | $__ZFS_CMD list -H -o name -t snapshot -d 1 $base_dataset | sed 's$.*@$'$range_start'$g' | |
98 | fi | |
99 | else | |
100 | __zfs_match_explicit_snapshot; __zfs_list_datasets | |
101 | fi | |
102 | } | |
103 | ||
104 | __zfs_list_volumes() | |
105 | { | |
106 | $__ZFS_CMD list -H -o name -t volume | |
107 | } | |
108 | ||
109 | __zfs_argument_chosen() | |
110 | { | |
111 | local word property | |
112 | for word in $(seq $((COMP_CWORD-1)) -1 2) | |
113 | do | |
114 | local prev="${COMP_WORDS[$word]}" | |
115 | if [[ ${COMP_WORDS[$word-1]} != -[tos] ]] | |
116 | then | |
117 | if [[ "$prev" == [^,]*,* ]] || [[ "$prev" == *[@:]* ]] | |
118 | then | |
119 | return 0 | |
120 | fi | |
121 | for property in $@ | |
122 | do | |
123 | if [[ $prev == "$property" ]] | |
124 | then | |
125 | return 0 | |
126 | fi | |
127 | done | |
128 | fi | |
129 | done | |
130 | return 1 | |
131 | } | |
132 | ||
133 | __zfs_complete_ordered_arguments() | |
134 | { | |
135 | local list1=$1 | |
136 | local list2=$2 | |
137 | local cur=$3 | |
138 | local extra=$4 | |
139 | if __zfs_argument_chosen $list1 | |
140 | then | |
141 | COMPREPLY=($(compgen -W "$list2 $extra" -- "$cur")) | |
142 | else | |
143 | COMPREPLY=($(compgen -W "$list1 $extra" -- "$cur")) | |
144 | fi | |
145 | } | |
146 | ||
147 | __zfs_complete_multiple_options() | |
148 | { | |
149 | local options=$1 | |
150 | local cur=$2 | |
151 | ||
152 | COMPREPLY=($(compgen -W "$options" -- "${cur##*,}")) | |
153 | local existing_opts=$(expr "$cur" : '\(.*,\)') | |
154 | if [[ $existing_opts ]] | |
155 | then | |
156 | COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" ) | |
157 | fi | |
158 | } | |
159 | ||
160 | __zfs_complete_switch() | |
161 | { | |
162 | local options=$1 | |
163 | if [[ ${cur:0:1} == - ]] | |
164 | then | |
165 | COMPREPLY=($(compgen -W "-{$options}" -- "$cur")) | |
166 | return 0 | |
167 | else | |
168 | return 1 | |
169 | fi | |
170 | } | |
171 | ||
172 | __zfs_complete() | |
173 | { | |
174 | local cur prev cmd cmds | |
175 | COMPREPLY=() | |
176 | # Don't split on colon | |
177 | _get_comp_words_by_ref -n : -c cur -p prev -w COMP_WORDS -i COMP_CWORD | |
178 | cmd="${COMP_WORDS[1]}" | |
179 | ||
180 | if [[ ${prev##*/} == zfs ]] | |
181 | then | |
182 | cmds=$(__zfs_get_commands) | |
183 | COMPREPLY=($(compgen -W "$cmds -?" -- "$cur")) | |
184 | return 0 | |
185 | fi | |
186 | ||
187 | case "${cmd}" in | |
188 | clone) | |
189 | case "${prev}" in | |
190 | -o) | |
191 | COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) | |
192 | ;; | |
193 | *) | |
194 | if ! __zfs_complete_switch "o,p" | |
195 | then | |
196 | if __zfs_argument_chosen | |
197 | then | |
198 | COMPREPLY=($(compgen -W "$(__zfs_list_datasets)" -- "$cur")) | |
199 | else | |
200 | COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) | |
201 | fi | |
202 | fi | |
203 | ;; | |
204 | esac | |
205 | ;; | |
206 | get) | |
207 | case "${prev}" in | |
208 | -d) | |
209 | COMPREPLY=($(compgen -W "" -- "$cur")) | |
210 | ;; | |
211 | -t) | |
212 | __zfs_complete_multiple_options "filesystem volume snapshot all" "$cur" | |
213 | ;; | |
214 | -s) | |
215 | __zfs_complete_multiple_options "local default inherited temporary none" "$cur" | |
216 | ;; | |
217 | -o) | |
218 | __zfs_complete_multiple_options "name property value source received all" "$cur" | |
219 | ;; | |
220 | *) | |
221 | if ! __zfs_complete_switch "H,r,p,d,o,t,s" | |
222 | then | |
223 | if __zfs_argument_chosen $(__zfs_get_properties) | |
224 | then | |
225 | COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) | |
226 | else | |
227 | __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" | |
228 | fi | |
229 | fi | |
230 | ;; | |
231 | esac | |
232 | ;; | |
233 | inherit) | |
234 | if ! __zfs_complete_switch "r" | |
235 | then | |
236 | __zfs_complete_ordered_arguments "$(__zfs_get_inheritable_properties)" "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" $cur | |
237 | fi | |
238 | ;; | |
239 | list) | |
240 | case "${prev}" in | |
241 | -d) | |
242 | COMPREPLY=($(compgen -W "" -- "$cur")) | |
243 | ;; | |
244 | -t) | |
245 | __zfs_complete_multiple_options "filesystem volume snapshot all" "$cur" | |
246 | ;; | |
247 | -o) | |
248 | __zfs_complete_multiple_options "$(__zfs_get_properties)" "$cur" | |
249 | ;; | |
250 | -s|-S) | |
251 | COMPREPLY=($(compgen -W "$(__zfs_get_properties)" -- "$cur")) | |
252 | ;; | |
253 | *) | |
254 | if ! __zfs_complete_switch "H,r,d,o,t,s,S" | |
255 | then | |
256 | COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) | |
257 | fi | |
258 | ;; | |
259 | esac | |
260 | ;; | |
261 | promote) | |
262 | COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) | |
263 | ;; | |
264 | rollback) | |
265 | if ! __zfs_complete_switch "r,R,f" | |
266 | then | |
267 | COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) | |
268 | fi | |
269 | ;; | |
270 | send) | |
271 | if ! __zfs_complete_switch "d,n,P,p,R,v,i,I" | |
272 | then | |
273 | COMPREPLY=($(compgen -W "$(__zfs_match_snapshot)" -- "$cur")) | |
274 | fi | |
275 | ;; | |
276 | snapshot) | |
277 | case "${prev}" in | |
278 | -o) | |
279 | COMPREPLY=($(compgen -W "$(__zfs_get_editable_properties)" -- "$cur")) | |
280 | ;; | |
281 | *) | |
282 | if ! __zfs_complete_switch "o,r" | |
283 | then | |
284 | COMPREPLY=($(compgen -W "$(__zfs_list_datasets | awk '{print $1"@"}')" -- "$cur")) | |
285 | fi | |
286 | ;; | |
287 | esac | |
288 | ;; | |
289 | set) | |
290 | __zfs_complete_ordered_arguments "$(__zfs_get_editable_properties)" "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" $cur | |
291 | ;; | |
292 | upgrade) | |
293 | case "${prev}" in | |
294 | -a|-V|-v) | |
295 | COMPREPLY=($(compgen -W "" -- "$cur")) | |
296 | ;; | |
297 | *) | |
298 | if ! __zfs_complete_switch "a,V,v,r" | |
299 | then | |
300 | COMPREPLY=($(compgen -W "$(__zfs_list_filesystems)" -- "$cur")) | |
301 | fi | |
302 | ;; | |
303 | esac | |
304 | ;; | |
305 | destroy) | |
306 | if ! __zfs_complete_switch "d,f,n,p,R,r,v" | |
307 | then | |
308 | __zfs_complete_multiple_options "$(__zfs_match_multiple_snapshots)" $cur | |
309 | fi | |
310 | ;; | |
311 | *) | |
312 | COMPREPLY=($(compgen -W "$(__zfs_match_explicit_snapshot) $(__zfs_list_datasets)" -- "$cur")) | |
313 | ;; | |
314 | esac | |
315 | __ltrim_colon_completions "$cur" | |
316 | return 0 | |
317 | } | |
318 | ||
319 | __zpool_get_commands() | |
320 | { | |
321 | $__ZPOOL_CMD 2>&1 | awk '/^\t[a-z]/ {print $1}' | uniq | |
322 | } | |
323 | ||
324 | __zpool_get_properties() | |
325 | { | |
326 | $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" || $2 == "NO" {print $1}'; echo all | |
327 | } | |
328 | ||
329 | __zpool_get_editable_properties() | |
330 | { | |
331 | $__ZPOOL_CMD get 2>&1 | awk '$2 == "YES" {print $1"="}' | |
332 | } | |
333 | ||
334 | __zpool_list_pools() | |
335 | { | |
336 | $__ZPOOL_CMD list -H -o name | |
337 | } | |
338 | ||
339 | __zpool_complete() | |
340 | { | |
341 | local cur prev cmd cmds | |
342 | COMPREPLY=() | |
343 | cur="${COMP_WORDS[COMP_CWORD]}" | |
344 | prev="${COMP_WORDS[COMP_CWORD-1]}" | |
345 | cmd="${COMP_WORDS[1]}" | |
346 | ||
347 | if [[ ${prev##*/} == zpool ]] | |
348 | then | |
349 | cmds=$(__zpool_get_commands) | |
350 | COMPREPLY=($(compgen -W "$cmds" -- "$cur")) | |
351 | return 0 | |
352 | fi | |
353 | ||
354 | case "${cmd}" in | |
355 | get) | |
356 | __zfs_complete_ordered_arguments "$(__zpool_get_properties)" "$(__zpool_list_pools)" $cur | |
357 | return 0 | |
358 | ;; | |
359 | import) | |
360 | if [[ $prev == -d ]] | |
361 | then | |
362 | _filedir -d | |
363 | else | |
364 | COMPREPLY=($(compgen -W "$(__zpool_list_pools) -d" -- "$cur")) | |
365 | fi | |
366 | return 0 | |
367 | ;; | |
368 | set) | |
369 | __zfs_complete_ordered_arguments "$(__zpool_get_editable_properties)" "$(__zpool_list_pools)" $cur | |
370 | return 0 | |
371 | ;; | |
372 | add|attach|clear|create|detach|offline|online|remove|replace) | |
373 | local pools="$(__zpool_list_pools)" | |
374 | if __zfs_argument_chosen $pools | |
375 | then | |
376 | _filedir | |
377 | else | |
378 | COMPREPLY=($(compgen -W "$pools" -- "$cur")) | |
379 | fi | |
380 | return 0 | |
381 | ;; | |
382 | *) | |
383 | COMPREPLY=($(compgen -W "$(__zpool_list_pools)" -- "$cur")) | |
384 | return 0 | |
385 | ;; | |
386 | esac | |
387 | ||
388 | } | |
389 | ||
390 | complete -F __zfs_complete zfs | |
391 | complete -F __zpool_complete zpool |