From db8d3eeeebb78632cdfb23d37ff57bf4350912e1 Mon Sep 17 00:00:00 2001 From: Igor Murzov Date: Sat, 19 Nov 2011 23:06:56 +0300 Subject: [PATCH] mount, umount: Add linux-specific completions. This linux-specific completion won't parse /etc/fstab as a lot of people including myself find it useless. And there is no reliable way to guess what user wants to mount. Some examples of what is ok for mount: $ mount /home $ mount /dev/sda1 /home $ mount file.iso /media/iso -o loop $ mount LABEL=mydisk /media/disk It is more sane to provide just filenames completion for mount. --- completions/.gitignore | 1 - completions/Makefile.am | 8 +- completions/mount | 107 ++----------------- completions/mount.linux | 225 +++++++++++++++++++++++++++++++++++++++ completions/umount | 21 ++++ completions/umount.linux | 117 ++++++++++++++++++++ 6 files changed, 373 insertions(+), 106 deletions(-) create mode 100644 completions/mount.linux create mode 100644 completions/umount create mode 100644 completions/umount.linux diff --git a/completions/.gitignore b/completions/.gitignore index 9ce8b5a4..713d825e 100644 --- a/completions/.gitignore +++ b/completions/.gitignore @@ -150,7 +150,6 @@ stream tightvncviewer tracepath6 typeset -umount vgcfgbackup vgcfgrestore vgchange diff --git a/completions/Makefile.am b/completions/Makefile.am index c6ff1ec8..d35fe00b 100644 --- a/completions/Makefile.am +++ b/completions/Makefile.am @@ -187,6 +187,7 @@ bashcomp_DATA = a2x \ mmsitepass \ monodevelop \ mount \ + mount.linux \ mplayer \ msynctool \ mtx \ @@ -289,6 +290,8 @@ bashcomp_DATA = a2x \ tcpnice \ tracepath \ tune2fs \ + umount \ + umount.linux \ unace \ unpack200 \ unrar \ @@ -483,7 +486,6 @@ CLEANFILES = \ tightvncviewer \ tracepath6 \ typeset \ - umount \ vgcfgbackup \ vgcfgrestore \ vgchange \ @@ -660,10 +662,6 @@ symlinks: rm -f $(targetdir)/$$file && \ $(LN_S) mcrypt $(targetdir)/$$file ; \ done - for file in umount ; do \ - rm -f $(targetdir)/$$file && \ - $(LN_S) mount $(targetdir)/$$file ; \ - done for file in mplayer2 mencoder gmplayer kplayer ; do \ rm -f $(targetdir)/$$file && \ $(LN_S) mplayer $(targetdir)/$$file ; \ diff --git a/completions/mount b/completions/mount index e1acef1e..ffe6629a 100644 --- a/completions/mount +++ b/completions/mount @@ -1,79 +1,16 @@ # mount(8) completion -*- shell-script -*- + +if [[ $OSTYPE == *linux* ]]; then + . "$BASH_SOURCE.linux" + return +fi + # This will pull a list of possible mounts out of # /etc/{,v}fstab, unless the word being completed contains a ':', which # would indicate the specification of an NFS server. In that case, we # query the server for a list of all available exports and complete on # that instead. # - -# Just like COMPREPLY=(`compgen -W "${COMPREPLY[*]}" -- "$cur"`), only better! -# -# This will correctly escape special characters in COMPREPLY. -_reply_compgen_array() -{ - # Create the argument for compgen -W by escaping twice. - # - # One round of escape is because we want to reply with escaped arguments. A - # second round is required because compgen -W will helpfully expand it's - # argument. - local i wlist - for i in ${!COMPREPLY[*]}; do - local q=$(quote "$(printf %q "${COMPREPLY[$i]}")") - wlist+=$q$'\n' - done - - # We also have to add another round of escaping to $cur. - local ecur="$cur" - ecur="${ecur//\\/\\\\}" - ecur="${ecur//\'/\'}" - - # Actually generate completions. - local oldifs=$IFS - IFS=$'\n' eval 'COMPREPLY=(`compgen -W "$wlist" -- "${ecur}"`)' - IFS=$oldifs -} - -# Unescape strings in the linux fstab(5) format (with octal escapes). -__linux_fstab_unescape() { - eval $1="'${!1//\'/\047}'" - eval $1="'${!1/%\\/\\\\}'" - eval "$1=$'${!1}'" -} - -# Complete linux fstab entries. -# -# Reads a file from stdin in the linux fstab(5) format; as used by /etc/fstab -# and /proc/mounts. -_linux_fstab() -{ - COMPREPLY=() - - # Read and unescape values into COMPREPLY - local fs_spec fs_file fs_other - local oldifs="$IFS" - while read -r fs_spec fs_file fs_other; do - if [[ $fs_spec = [#]* ]]; then continue; fi - if [[ $1 == -L ]]; then - local fs_label=${fs_spec/#LABEL=} - if [[ $fs_label != "$fs_spec" ]]; then - __linux_fstab_unescape fs_label - IFS=$'\0' - COMPREPLY+=("$fs_label") - IFS=$oldifs - fi - else - __linux_fstab_unescape fs_spec - __linux_fstab_unescape fs_file - IFS=$'\0' - [[ $fs_spec = */* ]] && COMPREPLY+=("$fs_spec") - [[ $fs_file = */* ]] && COMPREPLY+=("$fs_file") - IFS=$oldifs - fi - done - - _reply_compgen_array -} - _mount() { local cur prev words cword @@ -116,39 +53,9 @@ _mount() # probably Cygwin COMPREPLY=( $( compgen -W "$( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' )" -- "$cur" ) ) else - # probably Linux - if [[ $prev == -L ]]; then - _linux_fstab -L < /etc/fstab - elif [[ $prev == -U ]]; then - COMPREPLY=( $( compgen -W '$(sed -ne "s/^[[:space:]]*UUID=\([^[:space:]]*\).*/\1/p" /etc/fstab )' -- "$cur" ) ) - else - _linux_fstab < /etc/fstab - fi + COMPREPLY=( $( compgen -W "$( awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' /etc/fstab )" -- "$cur" ) ) fi - - return 0 } && complete -F _mount -o default -o dirnames mount -# umount(8) completion. This relies on the mount point being the third -# space-delimited field in the output of mount(8) -# -_umount() -{ - local cur prev words cword - _init_completion || return - - if [[ $OSTYPE == *linux* && -r /proc/mounts ]]; then - # Linux /proc/mounts is properly quoted. This is important when - # unmounting usb devices with pretty names. - _linux_fstab < /proc/mounts - else - local IFS=$'\n' - COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) ) - fi - - return 0 -} && -complete -F _umount -o dirnames umount - # ex: ts=4 sw=4 et filetype=sh diff --git a/completions/mount.linux b/completions/mount.linux new file mode 100644 index 00000000..9c47342f --- /dev/null +++ b/completions/mount.linux @@ -0,0 +1,225 @@ +# mount(8) completion -*- shell-script -*- + +_mount() +{ + local cur prev words cword + _init_completion -n =: || return + + local split=false + case "$prev" in + -t|--types) + # find /lib/modules/$(uname -r)/ -type f -path '*/fs/*.ko' -printf '%f\n' | cut -d. -f1 + # FIXME: no + if [[ "$cur" == ?*,* ]]; then + prev="${cur%,*}" + cur="${cur##*,}" + split=true + fi + COMPREPLY=( $(compgen -W 'auto adfs affs autofs btrfs cifs coda + cramfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs + iso9660 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 + ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs + umsdos usbfs vfat xfs' -- "$cur") ) + _fstypes + $split && COMPREPLY=( ${COMPREPLY[@]/#/$prev,} ) + return + ;; + --bind|-B|--rbind|-R) + _filedir -d + return + ;; + -p|--pass-fd) + COMPREPLY=( $(compgen -W '{0..9}') ) + compopt -o nospace + return + ;; + -L) + COMPREPLY=( $( cd "/dev/disk/by-label/" 2>/dev/null || return; \ + compgen -f -- "$cur" ) ) + return + ;; + -U) + COMPREPLY=( $( cd "/dev/disk/by-uuid/" 2>/dev/null || return; \ + compgen -f -- "$cur" ) ) + return + ;; + -O|--test-opts) + # argument required but no completions available + return + ;; + -o|--options) + local fstype=auto # default fstype + for (( i=${#words[@]}-1; i>0; i-- )); do + if [[ "${words[i]}" == -@(t|-types)* ]]; then + if [[ "${words[i]}" == *=* ]]; then + [[ "${words[i]}" == ?*,* ]] && break + fstype="${words[i]#-*=}" + else + [[ "${words[i+1]}" == ?*,* ]] && break + fstype="${words[i+1]}" + fi + break + fi + done + # no is not a real fstype, reset to "auto" + [[ "$fstype" == no?* ]] && fstype=auto + # split options list + if [[ "$cur" == ?*,* ]]; then + prev="${cur%,*}" + cur="${cur##*,}" + split=true + fi + # no completion if $cur is opt=smth + [[ "$cur" == *=* ]] && return + # mount options + COMPREPLY=( $(compgen -W 'loop async {,no}atime {,no}auto + {,fs,def,root}context= defaults {,no}dev {,no}diratime dirsync + {,no}exec group {,no}iversion {,no}mand _netdev nofail + {,no}relatime {,no}strictatime {,no}suid owner remount ro rw + sync {,no}user users' -- "$cur") ) + case "$fstype" in + adfs|auto) + COMPREPLY+=( $(compgen -W 'uid= gid= {own,oth}mask=' -- \ + "$cur") ) + ;;& + affs|auto) + COMPREPLY+=( $(compgen -W '{u,g}id= set{u,g}id= mode= protect + usemp verbose prefix= volume= reserved= root= bs= + {,no,usr,grp}quota' -- "$cur") ) + ;;& + btrfs|auto) + COMPREPLY+=( $(compgen -W 'degraded subvol= subvolid= device= + nodatasum nodatacow nobarrier max_inline= alloc_start= + thread_pool= compress= compress-force= ssd noacl notreelog + flushoncommit metadata_ratio= space_cache clear_cache + user_subvol_rm_allowed autodefrag inode_cache' -- "$cur") ) + ;;& + cifs|auto) + COMPREPLY+=( $(compgen -W 'user= password= credentials= {u,g}id= + force{u,g}id port= servern= netbiosname= {file,dir}_mode= + ip= domain= guest iocharset ro rw {,no}setuids {,no,dyn}perm + directio {,no}mapchars {,no}intr hard soft noacl nocase sec= + nobrl sfu {,no}serverino nounix nouser_xattr {r,w}size=' \ + -- "$cur") ) + ;;& + ext[2-4]|auto) + COMPREPLY+=( $(compgen -W '{,no}acl bsddf minixdf check= debug + errors= {,no}grpid {bsd,sysv}groups {,no,usr,grp}quota + nobh nouid32 oldalloc orlov res{u,g}id= sb= + {,no}user_xattr' -- "$cur") ) + ;;& + ext[34]|auto) + COMPREPLY+=( $(compgen -W 'journal= journal_dev= norecovery + noload data= barrier= commit=' -- "$cur") ) + ;;& + ext4|auto) + COMPREPLY+=( $(compgen -W 'journal_checksum journal_async_commit + nobarrier inode_readahead= stripe= {,no}delalloc abort + {max,min}_batch_time= journal_ioprio= {,no}auto_da_alloc + {,no}discard nouid32 resize {,no}block_validity + dioread_{,no}lock i_version' -- "$cur") ) + ;;& + msdos|umsdos|vfat|auto) + COMPREPLY+=( $(compgen -W 'blocksize= {u,g}id= {u,d,f}mask= + allow_utime= check= codepage= conv= cvf_format= cvf_option= + debug fat= iocharset= tz= quiet showexec sys_immutable flush + usefree {,no}dots dotsOK=' -- "$cur") ) + ;;& + vfat|auto) + COMPREPLY+=( $(compgen -W 'uni_xlate posix nonumtail utf8 + shortname=' -- "$cur") ) + ;;& + iso9660|auto) + COMPREPLY+=( $(compgen -W 'norock nojoliet check= {u,g}id= map= + mode= unhide block= conv= cruft session= sbsector= + iocharset= utf8' -- "$cur") ) + ;;& + jfs|auto) + COMPREPLY+=( $(compgen -W 'iocharset= resize= {,no}integrity + errors= {,no,usr,grp}quota' -- "$cur") ) + ;;& + ntfs-3g) + COMPREPLY+=( $(compgen -W '{u,g}id= {u,f,d}mask= usermapping= + permissions inherit ro locale= force {,no}recover + ignore_case remove_hiberfile {,no,rel}atime show_sys_files + hide_{hid,dot}_files windows_names allow_other max_read= + silent no_def_opts streams_interface= user_xattr efs_raw + {,no}compression debug no_detach' -- "$cur") ) + ;;& + proc|auto) + COMPREPLY+=( $(compgen -W '{u,g}id=' -- "$cur") ) + ;;& + reiserfs|auto) + COMPREPLY+=( $(compgen -W 'conv hash= {,no_un}hashed_relocation + noborder nolog notail replayonly resize= user_xattr acl + barrier=' -- "$cur") ) + ;;& + tmpfs|auto) + COMPREPLY+=( $(compgen -W 'size= nr_blocks= nr_inodes= mode= + {u,g}id= mpol=' -- "$cur") ) + ;;& + udf|auto) + COMPREPLY+=( $(compgen -W '{u,g}id= umask= unhide undelete + nostrict iocharset bs= novrs session= anchor= volume= + partition= lastblock= fileset= rootdir=' -- "$cur") ) + ;;& + usbfs|auto) + COMPREPLY+=( $(compgen -W 'dev{u,g}id= devmode= bus{u,g}id= + busmode= list{u,g}id= listmode=' -- "$cur") ) + ;;& + xfs|auto) + COMPREPLY+=( $(compgen -W 'allocsize= {,no}attr2 barrier dmapi + {,no}grpid {bsd,sysv}groups ihashsize= {,no}ikeep inode64 + {,no}largeio logbufs= logbsize= logdev= rtdev= mtpt= noalign + noatime norecovery nouuid osyncisosync {u,g,p}qnoenforce + {,u,usr,g,grp,p,prj}quota sunit= swidth= swalloc' \ + -- "$cur") ) + ;;& + esac + # COMP_WORDBREAKS is a real pain in the ass + prev="${prev##*[$COMP_WORDBREAKS]}" + $split && COMPREPLY=( ${COMPREPLY[@]/#/"$prev,"} ) + [[ $COMPREPLY == *= ]] && compopt -o nospace + return + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-V --version -h --help -v --verbose -a --all + -F --fork -f --fake -i --internal-only -l -n --no-mtab + --no-canonicalize -p --pass-fd -s -r --read-only -w --rw -L -U + -t --types -O --test-opts -o --options -B --bind -R --rbind + -M --move' -- "$cur" ) ) + [[ $COMPREPLY ]] && return + fi + + [[ "$cur" == \\ ]] && cur="/" + + local sm host + + if [[ "$cur" == *:* ]]; then + for sm in "$(type -P showmount)" {,/usr}/{,s}bin/showmount; do + [[ -x $sm ]] || continue + COMPREPLY=( $( compgen -W "$( "$sm" -e ${cur%%:*} | \ + awk 'NR>1 {print $1}' )" -- "${cur#*:}" ) ) + return 0 + done + fi + + if [[ "$cur" == //* ]]; then + host=${cur#//} + host=${host%%/*} + if [[ -n $host ]]; then + COMPREPLY=( $( compgen -P "//$host" -W \ + "$( smbclient -d 0 -NL $host 2>/dev/null | + sed -ne '/^['"$'\t '"']*Sharename/,/^$/p' | + sed -ne '3,$s|^[^A-Za-z]*\([^'"$'\t '"']*\).*$|/\1|p' )" \ + -- "${cur#//$host}" ) ) + fi + fi + + _filedir +} && +complete -F _mount mount + +# ex: ts=4 sw=4 et filetype=sh diff --git a/completions/umount b/completions/umount new file mode 100644 index 00000000..6c15182b --- /dev/null +++ b/completions/umount @@ -0,0 +1,21 @@ +# umount(8) completion -*- shell-script -*- + +if [[ $OSTYPE == *linux* ]]; then + . "$BASH_SOURCE.linux" + return +fi + +# umount(8) completion. This relies on the mount point being the third +# space-delimited field in the output of mount(8) +# +_umount() +{ + local cur prev words cword + _init_completion || return + + local IFS=$'\n' + COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) ) +} && +complete -F _umount -o dirnames umount + +# ex: ts=4 sw=4 et filetype=sh diff --git a/completions/umount.linux b/completions/umount.linux new file mode 100644 index 00000000..13a923c1 --- /dev/null +++ b/completions/umount.linux @@ -0,0 +1,117 @@ +# umount(8) completion -*- shell-script -*- + +# Just like COMPREPLY=(`compgen -W "${COMPREPLY[*]}" -- "$cur"`), only better! +# +# This will correctly escape special characters in COMPREPLY. +_reply_compgen_array() +{ + # Create the argument for compgen -W by escaping twice. + # + # One round of escape is because we want to reply with escaped arguments. A + # second round is required because compgen -W will helpfully expand it's + # argument. + local i wlist + for i in ${!COMPREPLY[*]}; do + local q=$(quote "$(printf %q "${COMPREPLY[$i]}")") + wlist+=$q$'\n' + done + + # We also have to add another round of escaping to $cur. + local ecur="$cur" + ecur="${ecur//\\/\\\\}" + ecur="${ecur//\'/\'}" + + # Actually generate completions. + local oldifs=$IFS + IFS=$'\n' eval 'COMPREPLY=(`compgen -W "$wlist" -- "${ecur}"`)' + IFS=$oldifs +} + +# Unescape strings in the linux fstab(5) format (with octal escapes). +__linux_fstab_unescape() { + eval $1="'${!1//\'/\047}'" + eval $1="'${!1/%\\/\\\\}'" + eval "$1=$'${!1}'" +} + +# Complete linux fstab entries. +# +# Reads a file from stdin in the linux fstab(5) format; as used by /etc/fstab +# and /proc/mounts. +_linux_fstab() +{ + COMPREPLY=() + + # Read and unescape values into COMPREPLY + local fs_spec fs_file fs_other + local oldifs="$IFS" + while read -r fs_spec fs_file fs_other; do + if [[ $fs_spec = [#]* ]]; then continue; fi + if [[ $1 == -L ]]; then + local fs_label=${fs_spec/#LABEL=} + if [[ $fs_label != "$fs_spec" ]]; then + __linux_fstab_unescape fs_label + IFS=$'\0' + COMPREPLY+=("$fs_label") + IFS=$oldifs + fi + else + __linux_fstab_unescape fs_spec + __linux_fstab_unescape fs_file + IFS=$'\0' + [[ $fs_spec = */* ]] && COMPREPLY+=("$fs_spec") + [[ $fs_file = */* ]] && COMPREPLY+=("$fs_file") + IFS=$oldifs + fi + done + + _reply_compgen_array +} + +_umount() +{ + local cur prev words cword + _init_completion || return + + case "$prev" in + -t) + # FIXME: no + local split=false + if [[ "$cur" == ?*,* ]]; then + prev="${cur%,*}" + cur="${cur##*,}" + split=true + fi + COMPREPLY=( $(compgen -W 'adfs affs autofs btrfs cifs coda + cramfs debugfs devpts efs ext2 ext3 ext4 fuse hfs hfsplus hpfs + iso9660 jfs minix msdos ncpfs nfs nfs4 ntfs ntfs-3g proc qnx4 + ramfs reiserfs romfs squashfs smbfs sysv tmpfs ubifs udf ufs + umsdos usbfs vfat xfs' -- "$cur") ) + _fstypes + $split && COMPREPLY=( ${COMPREPLY[@]/#/$prev,} ) + return + ;; + -O) + # argument required but no completions available + return + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-V -h -v -n -r -d -i -a -t -O -f -l + --no-canonicalize --fake' -- "$cur" ) ) + [[ $COMPREPLY ]] && return + fi + + if [[ -r /proc/mounts ]]; then + # Linux /proc/mounts is properly quoted. This is important when + # unmounting usb devices with pretty names. + _linux_fstab < /proc/mounts + else + local IFS=$'\n' + COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- "$cur" ) ) + fi +} && +complete -F _umount -o dirnames umount + +# ex: ts=4 sw=4 et filetype=sh