| 1 | #####
|
|---|
| 2 | #To: [email protected], [email protected]
|
|---|
| 3 | #Message-Id: <[email protected]>
|
|---|
| 4 | #Posted-To: comp.unix.shell, gnu.bash.bug
|
|---|
| 5 | #Subject: bash 2.04 programmable completion examples
|
|---|
| 6 | #Reply-To: [email protected], [email protected]
|
|---|
| 7 | #Summary: examples of programmable completion for bash 2.04
|
|---|
| 8 | #Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT)
|
|---|
| 9 | #From: [email protected] (Ian Macdonald)
|
|---|
| 10 | #####
|
|---|
| 11 |
|
|---|
| 12 | #########################################################################
|
|---|
| 13 | # Turn on extended globbing
|
|---|
| 14 | shopt -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
|
|---|
| 20 | complete -d cd mkdir rmdir pushd
|
|---|
| 21 |
|
|---|
| 22 | # Make file commands see only files
|
|---|
| 23 | complete -f cat less more chown ln strip
|
|---|
| 24 | complete -f -X '*.gz' gzip
|
|---|
| 25 | complete -f -X '*.Z' compress
|
|---|
| 26 | complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore
|
|---|
| 27 | complete -f -X '!*.Z' uncompress zmore zcat
|
|---|
| 28 | complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv
|
|---|
| 29 | complete -f -X '!*.+(ps|PS|ps.gz)' gv
|
|---|
| 30 | complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype
|
|---|
| 31 | complete -f -X '!*.+(pdf|PDF)' acroread xpdf
|
|---|
| 32 | complete -f -X '!*.texi*' makeinfo texi2dvi texi2html
|
|---|
| 33 | complete -f -X '!*.+(tex|TEX)' tex latex slitex
|
|---|
| 34 | complete -f -X '!*.+(mp3|MP3)' mpg123
|
|---|
| 35 |
|
|---|
| 36 | # kill sees only signals
|
|---|
| 37 | complete -A signal kill -P '%'
|
|---|
| 38 |
|
|---|
| 39 | # user commands see only users
|
|---|
| 40 | complete -u finger su usermod userdel passwd
|
|---|
| 41 |
|
|---|
| 42 | # bg completes with stopped jobs
|
|---|
| 43 | complete -A stopped -P '%' bg
|
|---|
| 44 |
|
|---|
| 45 | # other job commands
|
|---|
| 46 | complete -j -P '%' fg jobs disown
|
|---|
| 47 |
|
|---|
| 48 | # network commands complete with hostname
|
|---|
| 49 | complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \
|
|---|
| 50 | nslookup
|
|---|
| 51 |
|
|---|
| 52 | # export and others complete with shell variables
|
|---|
| 53 | complete -v export local readonly unset
|
|---|
| 54 |
|
|---|
| 55 | # set completes with set options
|
|---|
| 56 | complete -A setopt set
|
|---|
| 57 |
|
|---|
| 58 | # shopt completes with shopt options
|
|---|
| 59 | complete -A shopt shopt
|
|---|
| 60 |
|
|---|
| 61 | # helptopics
|
|---|
| 62 | complete -A helptopic help
|
|---|
| 63 |
|
|---|
| 64 | # unalias completes with aliases
|
|---|
| 65 | complete -a unalias
|
|---|
| 66 |
|
|---|
| 67 | # various commands complete with commands
|
|---|
| 68 | complete -c command type nohup exec nice eval strace gdb
|
|---|
| 69 |
|
|---|
| 70 | # bind completes with readline bindings (make this more intelligent)
|
|---|
| 71 | complete -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 | }
|
|---|
| 121 | complete -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 | }
|
|---|
| 138 | complete -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 | }
|
|---|
| 153 | complete -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 | }
|
|---|
| 181 | complete -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 | }
|
|---|
| 196 | complete -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 | }
|
|---|
| 212 | complete -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 | }
|
|---|
| 245 | complete -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 | }
|
|---|
| 290 | complete -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 | }
|
|---|
| 381 | complete -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 | }
|
|---|
| 417 | complete -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 | }
|
|---|
| 432 | complete -F _ipsec ipsec
|
|---|
| 433 | #########################################################################
|
|---|