1#####
2#To: chet@po.cwru.edu, sarahmckenna@lucent.com
3#Message-Id: <slrn8mqioc.msb.ian@lovelorn.linuxcare.com>
4#Posted-To: comp.unix.shell, gnu.bash.bug
5#Subject: bash 2.04 programmable completion examples
6#Reply-To: ian@linuxcare.com, ian@caliban.org
7#Summary: examples of programmable completion for bash 2.04
8#Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT)
9#From: ianmacd@linuxcare.com (Ian Macdonald)
10#####
11
12#########################################################################
13# Turn on extended globbing
14shopt -s extglob
15
16# A lot of the following one-liners were taken directly from the
17# completion examples provided with the bash 2.04 source distribution
18
19# Make directory commands see only directories
20complete -d cd mkdir rmdir pushd
21
22# Make file commands see only files
23complete -f cat less more chown ln strip
24complete -f -X '*.gz' gzip
25complete -f -X '*.Z' compress
26complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore
27complete -f -X '!*.Z' uncompress zmore zcat
28complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv
29complete -f -X '!*.+(ps|PS|ps.gz)' gv
30complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype
31complete -f -X '!*.+(pdf|PDF)' acroread xpdf
32complete -f -X '!*.texi*' makeinfo texi2dvi texi2html
33complete -f -X '!*.+(tex|TEX)' tex latex slitex
34complete -f -X '!*.+(mp3|MP3)' mpg123
35
36# kill sees only signals
37complete -A signal kill -P '%'
38
39# user commands see only users
40complete -u finger su usermod userdel passwd
41
42# bg completes with stopped jobs
43complete -A stopped -P '%' bg
44
45# other job commands
46complete -j -P '%' fg jobs disown
47
48# network commands complete with hostname
49complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \
50	    nslookup
51
52# export and others complete with shell variables
53complete -v export local readonly unset
54
55# set completes with set options
56complete -A setopt set
57
58# shopt completes with shopt options
59complete -A shopt shopt
60
61# helptopics
62complete -A helptopic help
63
64# unalias completes with aliases
65complete -a unalias
66
67# various commands complete with commands
68complete -c command type nohup exec nice eval strace gdb
69
70# bind completes with readline bindings (make this more intelligent)
71complete -A binding bind
72
73# Now we get to the meat of the file, the functions themselves. Some
74# of these are works in progress. Most assume GNU versions of the
75# tools in question and may require modifications for use on vanilla
76# UNIX systems.
77#
78# A couple of functions may have non-portable, Linux specific code in
79# them, but this will be noted where applicable
80
81
82# GNU chown(1) completion. This should be expanded to allow the use of
83# ':' as well as '.' as the user.group separator.
84#
85_chown ()
86{
87	local cur prev user group
88
89        COMPREPLY=()
90        cur=${COMP_WORDS[COMP_CWORD]}
91        prev=${COMP_WORDS[COMP_CWORD-1]}
92
93	# do not attempt completion if we're specifying an option
94	if [ "${cur:0:1}" = "-" ]; then return 0; fi
95
96	# first parameter on line or first since an option?
97	if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then
98		case "$cur" in
99		[a-zA-Z]*.*)
100			user=${cur%.*}
101			group=${cur#*.}
102			COMPREPLY=( $( awk 'BEGIN {FS=":"} \
103					{if ($1 ~ /^'$group'/) print $1}' \
104					/etc/group ) )
105			for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
106				COMPREPLY[i]=$user.${COMPREPLY[i]}
107			done
108			return 0
109			;;
110		*)
111			COMPREPLY=( $( compgen -u $cur -S '.' ) )
112			return 0
113			;;
114		esac
115	else
116		COMPREPLY=( $( compgen -f $cur ) )
117	fi
118
119	return 0
120}
121complete -F _chown chown
122
123# umount(8) completion. This relies on the mount point being the third
124# space-delimited field in the output of mount(8)
125#
126_umount ()
127{
128	local cur
129
130        COMPREPLY=()
131        cur=${COMP_WORDS[COMP_CWORD]}
132
133	# could rewrite the cut | grep to be a sed command, but this is
134	# clearer and doesn't result in much overhead
135	COMPREPLY=( $( mount | cut -d' ' -f 3 | grep ^$cur) )
136	return 0
137}
138complete -F _umount umount
139
140# GID completion. This will get a list of all valid group names from
141# /etc/group and should work anywhere.
142#
143_gid_func ()
144{
145	local cur
146
147        COMPREPLY=()
148        cur=${COMP_WORDS[COMP_CWORD]}
149	COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($1 ~ /^'$cur'/) print $1}' \
150			   /etc/group ) )
151	return 0
152}
153complete -F _gid_func groupdel groupmod
154
155# mount(8) completion. This will pull a list of possible mounts out of
156# /etc/fstab, unless the word being completed contains a ':', which
157# would indicate the specification of an NFS server. In that case, we
158# query the server for a list of all available exports and complete on
159# that instead.
160#
161_mount ()
162
163{       local cur
164
165        COMPREPLY=()
166        cur=${COMP_WORDS[COMP_CWORD]}
167
168	case "$cur" in
169	*:*)
170	      COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\
171			       grep ^${cur#*:} | awk '{print $1}'))
172		return 0
173		;;
174	*)
175		COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \
176			       grep ^$cur ))
177		return 0
178		;;
179	esac
180}
181complete -F _mount mount
182
183# Linux rmmod(1) completion. This completes on a list of all currently
184# installed kernel modules.
185#
186_rmmod ()
187{
188	local cur
189
190        COMPREPLY=()
191        cur=${COMP_WORDS[COMP_CWORD]}
192
193	COMPREPLY=($( lsmod | awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}'))
194	return 0
195}
196complete -F _rmmod rmmod
197
198# Linux insmod(1) completion. This completes on a list of all
199# available modules for the version of the kernel currently running.
200#
201_insmod ()
202{
203	local cur modpath
204
205        COMPREPLY=()
206        cur=${COMP_WORDS[COMP_CWORD]}
207	modpath=/lib/modules/`uname -r`
208
209	COMPREPLY=($( ls -R $modpath | sed -ne 's/^\('$cur'.*\)\.o$/\1/p'))
210	return 0
211}
212complete -F _insmod insmod depmod modprobe
213
214# man(1) completion. This relies on the security enhanced version of
215# GNU locate(1). UNIX variants having non-numeric man page sections
216# other than l, m and n should add the appropriate sections to the
217# first clause of the case statement.
218#
219# This is Linux specific, in that 'man <section> <page>' is the
220# expected syntax. This allows one to do something like
221# 'man 3 str<tab>' to obtain a list of all string handling syscalls on
222# the system.
223#
224_man ()
225{
226	local cur prev
227
228        COMPREPLY=()
229        cur=${COMP_WORDS[COMP_CWORD]}
230        prev=${COMP_WORDS[COMP_CWORD-1]}
231
232	case "$prev" in
233	[0-9lmn])
234		COMPREPLY=($( slocate -ql 0 -r '/man/man'$prev'/'$cur | \
235		      sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
236		return 0
237		;;
238	*)
239		COMPREPLY=($( slocate -ql 0 -r '/man/man./'$cur | \
240		      sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
241		return 0
242		;;
243	esac
244}
245complete -F _man man
246
247# Linux killall(1) completion. This wouldn't be much use on, say,
248# Solaris, where killall does exactly that: kills ALL processes.
249#
250# This could be improved. For example, it currently doesn't take
251# command line options into account
252#
253_killall ()
254{
255	local cur prev
256
257        COMPREPLY=()
258        cur=${COMP_WORDS[COMP_CWORD]}
259        prev=${COMP_WORDS[COMP_CWORD-1]}
260
261	case "$prev" in
262	-[A-Z0-9]*)
263		# get a list of processes (the first sed evaluation
264		# takes care of swapped out processes, the second
265		# takes care of getting the basename of the process)
266	       COMPREPLY=( $( ps ahx | awk '{if ($5 ~ /^'$cur'/) print $5}' | \
267			       sed -e 's#[]\[]##g' -e 's#^.*/##' ))
268		return 0
269		;;
270	esac
271
272	# first parameter can be either a signal or a process
273	if [ $COMP_CWORD -eq 1 ]; then
274		# standard signal completion is rather braindead, so we need
275		# to hack around to get what we want here, which is to
276		# complete on a dash, followed by the signal name minus
277		# the SIG prefix
278		COMPREPLY=( $( compgen -A signal SIG${cur#-} ))
279		for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
280			COMPREPLY[i]=-${COMPREPLY[i]#SIG}
281		done
282	fi
283
284	# get processes, adding to signals if applicable
285	COMPREPLY=( ${COMPREPLY[*]} $( ps ahx | \
286		                       awk '{if ($5 ~ /^'$cur'/) print $5}' | \
287				       sed -e 's#[]\[]##g' -e 's#^.*/##' ))
288	return 0
289}
290complete -F _killall killall
291
292# GNU find(1) completion. This makes heavy use of ksh style extended
293# globs and contains Linux specific code for completing the parameter
294# to the -fstype option.
295#
296_find ()
297{
298	local cur prev
299
300        COMPREPLY=()
301        cur=${COMP_WORDS[COMP_CWORD]#-}
302        prev=${COMP_WORDS[COMP_CWORD-1]}
303
304	case "$prev" in
305	-@(max|min)depth)
306		COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' ) )
307		return 0
308		;;
309	-?(a)newer|-fls|-fprint?(0|f))
310		COMPREPLY=( $( compgen -f $cur ) )
311		return 0
312		;;
313	-fstype)
314		# this is highly non-portable (the option to -d is a tab)
315		COMPREPLY=( $( cut -d'	' -f 2 /proc/filesystems | grep ^$cur ) )
316		return 0
317		;;
318	-gid)
319		COMPREPLY=( $( awk 'BEGIN {FS=":"} \
320				{if ($3 ~ /^'$cur'/) print $3}' /etc/group ) )
321		return 0
322		;;
323	-group)
324		COMPREPLY=( $( awk 'BEGIN {FS=":"} \
325				{if ($1 ~ /^'$cur'/) print $1}' /etc/group ) )
326		return 0
327		;;
328	-?(x)type)
329		COMPREPLY=( $( compgen -W 'b c d p f l s' $cur ) )
330		return 0
331		;;
332	-uid)
333		COMPREPLY=( $( awk 'BEGIN {FS=":"} \
334				{if ($3 ~ /^'$cur'/) print $3}' /etc/passwd ) )
335		return 0
336		;;
337	-user)
338		COMPREPLY=( $( compgen -u $cur ) )
339		return 0
340		;;
341	-[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \
342	-links|-perm|-size|-used|-exec|-ok|-printf)
343		# do nothing, just wait for a parameter to be given
344		return 0
345		;;
346	esac
347
348	# complete using basic options ($cur has had its dash removed here,
349	# as otherwise compgen will bomb out with an error, since it thinks
350	# the dash is an option to itself)
351	COMPREPLY=( $( compgen -W 'daystart depth follow help maxdepth \
352			mindepth mount noleaf version xdev amin anewer atime \
353			cmin cnewer ctime empty false fstype gid group ilname \
354			iname inum ipath iregex links lname mmin mtime name \
355			newer nouser nogroup perm regex size true type uid \
356			used user xtype exec fls fprint fprint0 fprintf ok \
357			print print0 printf prune ls' $cur ) )
358
359	# this removes any options from the list of completions that have
360	# already been specified somewhere on the command line.
361	COMPREPLY=( $( echo "${COMP_WORDS[@]}-" | \
362		       (while read -d '-' i; do
363			    [ "$i" == "" ] && continue
364			    # flatten array with spaces on either side,
365			    # otherwise we cannot grep on word boundaries of
366			    # first and last word
367			    COMPREPLY=" ${COMPREPLY[@]} "
368			    # remove word from list of completions
369			    COMPREPLY=( ${COMPREPLY/ ${i%% *} / } )
370		        done
371		        echo ${COMPREPLY[@]})
372		  ) )
373	
374	# put dashes back
375	for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
376		COMPREPLY[i]=-${COMPREPLY[i]}
377	done
378		
379	return 0
380}
381complete -F _find find
382
383# Linux ifconfig(8) completion
384#
385_ifconfig ()
386{
387	local cur
388
389        COMPREPLY=()
390        cur=${COMP_WORDS[COMP_CWORD]}
391
392	case "${COMP_WORDS[1]}" in
393	-|*[0-9]*)
394		COMPREPLY=( $( compgen -W '-a up down arp promisc allmulti \
395					   metric mtu dstaddr netmask add del \
396					   tunnel irq io_addr mem_start media \
397					   broadcast pointopoint hw multicast \
398					   address txqueuelen' $cur ))
399		COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \
400			       (while read -d ' ' i; do
401				   [ "$i" == "" ] && continue
402				   # flatten array with spaces on either side,
403				   # otherwise we cannot grep on word
404				   # boundaries of first and last word
405				   COMPREPLY=" ${COMPREPLY[@]} "
406				   # remove word from list of completions
407				   COMPREPLY=( ${COMPREPLY/ $i / } )
408				done
409			        echo ${COMPREPLY[@]})
410			  ) )
411		return 0
412		;;
413	esac
414
415	COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' ))
416}
417complete -F _ifconfig ifconfig
418
419# Linux ipsec(8) completion (for FreeS/WAN). Very basic.
420#
421_ipsec ()
422{
423	local cur
424
425        COMPREPLY=()
426        cur=${COMP_WORDS[COMP_CWORD]}
427
428	COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \
429				   pluto ranbits rsasigkey setup showdefaults \
430				   showhostkey spi spigrp tncfg whack' $cur ))
431}
432complete -F _ipsec ipsec
433#########################################################################
434