view contrib/bash_completion @ 1825:a9343f9d7365

Make hgweb.staticfile() more secure and portable. Without this, files in directories next to the static directory starting with 'static' could be retrieved, e.g. with '../static.private/foo'. Additionally staticfile now generates platform specific pathnames from the /-separated paths given in the URL. Illegal file names (e.g. containing %00) now yield a sane error message.
author Thomas Arendsen Hein <thomas@intevation.de>
date Thu, 02 Mar 2006 09:17:04 +0100
parents 7e10518b2b9e
children 913397c27cd8
line wrap: on
line source

shopt -s extglob

_hg_command_list()
{
    "$hg" --debug help 2>/dev/null | \
	awk -F', ' '/^list of commands:/ {commands=1}
	    commands==1 && /^ [^ ]/ {
		line = substr($0, 2)
		colon = index(line, ":")
		if (colon > 0)
		    line = substr(line, 1, colon-1)
		n = split(line, aliases)
		command = aliases[1]
		if (index(command, "debug") == 1) {
		    for (i=1; i<=n; i++)
			debug[j++] = aliases[i]
		    next
		}
		print command
		for (i=2; i<=n; i++)
		    if (index(command, aliases[i]) != 1)
			print aliases[i]
	    }
	    /^global options:/ {exit 0}
	    END {for (i in debug) print debug[i]}'
}

_hg_option_list()
{
    "$hg" -v help $1 2>/dev/null | \
	awk '/^ *-/ {
		for (i = 1; i <= NF; i ++) {
		    if (index($i, "-") != 1)
			break;
		    print $i;
		}
	    }'
}


_hg_commands()
{
    local all commands result

    all=$(_hg_command_list)
    commands=${all%%$'\n'debug*}
    result=$(compgen -W '$commands' -- "$cur")

    # hide debug commands from users, but complete them if
    # there is no other possible command
    if [ "$result" = "" ]; then
	local debug
	debug=debug${all#*$'\n'debug}
	result=$(compgen -W '$debug' -- "$cur")
    fi

    COMPREPLY=(${COMPREPLY[@]:-} $result)
}

_hg_paths()
{
    local paths="$("$hg" paths 2>/dev/null | sed -e 's/ = .*$//')"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$paths' -- "$cur"))
}

_hg_repos()
{
    local i
    for i in $(compgen -d -- "$cur"); do
	test ! -d "$i"/.hg || COMPREPLY=(${COMPREPLY[@]:-} "$i")
    done
}

_hg_status()
{
    local files="$("$hg" status -n$1 . 2>/dev/null)"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur"))
}

_hg_tags()
{
    local tags="$("$hg" tags 2>/dev/null |
	sed -e 's/[0-9]*:[a-f0-9]\{40\}$//; s/ *$//')"
    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$tags' -- "$cur"))
}

# this is "kind of" ugly...
_hg_count_non_option()
{
    local i count=0
    local filters="$1"

    for ((i=1; $i<=$COMP_CWORD; i++)); do
	if [[ "${COMP_WORDS[i]}" != -* ]]; then
	    if [[ ${COMP_WORDS[i-1]} == @($filters|$global_args) ]]; then
		continue
	    fi
	    count=$(($count + 1))
	fi
    done

    echo $(($count - 1))
}

_hg()
{
    local cur prev cmd opts i
    # global options that receive an argument
    local global_args='--cwd|-R|--repository'
    local hg="$1"

    COMPREPLY=()
    cur="$2"
    prev="$3"

    # searching for the command
    # (first non-option argument that doesn't follow a global option that
    #  receives an argument)
    for ((i=1; $i<=$COMP_CWORD; i++)); do
	if [[ ${COMP_WORDS[i]} != -* ]]; then
	    if [[ ${COMP_WORDS[i-1]} != @($global_args) ]]; then
		cmd="${COMP_WORDS[i]}"
		break
	    fi
	fi
    done

    if [[ "$cur" == -* ]]; then
	opts=$(_hg_option_list $cmd)

	COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$opts' -- "$cur"))
	return
    fi

    # global options
    case "$prev" in
	-R|--repository)
	    _hg_repos
	    return
	;;
	--cwd)
	    # Stick with default bash completion
	    return
	;;
    esac

    if [ -z "$cmd" ] || [ $COMP_CWORD -eq $i ]; then
	_hg_commands
	return
    fi

    # canonicalize command name
    cmd=$("$hg" -q help "$cmd" 2>/dev/null | sed -e 's/^hg //; s/ .*//; 1q')

    if [ "$cmd" != status ] && [ "$prev" = -r ] || [ "$prev" = --rev ]; then
	_hg_tags
	return
    fi

    case "$cmd" in
	help)
	    _hg_commands
	;;
	export|manifest|update)
	    _hg_tags
	;;
	pull|push|outgoing|incoming)
	    _hg_paths
	    _hg_repos
	;;
	paths)
	    _hg_paths
	;;
	add)
	    _hg_status "u"
	;;
	commit)
	    _hg_status "mar"
	;;
	remove)
	    _hg_status "d"
	;;
	forget)
	    _hg_status "a"
	;;
	diff)
	    _hg_status "mar"
	;;
	revert)
	    _hg_status "mard"
	;;
	clone)
	    local count=$(_hg_count_non_option)
	    if [ $count = 1 ]; then
		_hg_paths
	    fi
	    _hg_repos
	;;
	debugindex|debugindexdot)
	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.i" -- "$cur"))
	;;
	debugdata)
	    COMPREPLY=(${COMPREPLY[@]:-} $(compgen -f -X "!*.d" -- "$cur"))
	;;
    esac

}

complete -o bashdefault -o default -F _hg hg 2>/dev/null \
    || complete -o default -F _hg hg