#!/usr/bin/env bash
#
# Simple option parsing library
# Copyright (c) Petr Baudis, 2005; GPL
#
# See the bottom for example code.

_cg_cmd=${0##*/}

die()
{
	echo "$_cg_cmd: $@" >&2
	exit 1
}


# Usage: path_lookup COMMAND VARNAME [CMDTEST]
# Lookup COMMAND in $PATH and save the full path to VARNAME (optionally,
# only if CMDTEST on the command succeeds).
# This would have been type -P but we want to be bash2 compatible.
path_lookup()
{
	local exename="$1" varname="$2" cmdtest="$3"

	# We do our own $PATH iteration as it's faster than the fork()
	# of $(which), and this happens many times every time we
	# execute some cg tool.
	# Cut'n'pasted to the 'cg' source.
	local save_IFS dir cmd
	save_IFS="$IFS"; IFS=:
	for dir in $PATH; do
		IFS="$save_IFS"
		cmd="$dir/$exename"
		if [ -x "$cmd" ] && { [ -z "$cmdtest" ] || eval "$cmdtest"; }; then
			export $varname="$cmd"
			break
		fi
	done
	IFS="$save_IFS"
}

# Usage: width="$(...single column... | column_width MINUSPREFIX MAXWIDTH)"
column_width()
{
	local line= minusprefix="$1" maxwidth="$2"
	[ "$maxwidth" ] || maxwidth=35

	while read line; do
		line=${line#$1};
		echo ${#line}
	done | sort -nr | head -n 1 |
	(
		read maxlen;
		[ ${maxlen:-0} -le $maxwidth ] || maxlen=$maxwidth;
		echo ${maxlen:-0}
	)
}

# Usage: columns_print COL1 WIDTH COL2 - COL3 tWIDTH COL4 - ...
columns_print()
{
	local fmt= cols=
	cols=()
	while [ $# -gt 0 ]; do
		local col="$1"; shift
		local width="$1"; shift
		local tab=
		local trim=
		if [ x"${width:0:1}" = x"t" ]; then
			tab=1; width="${width:1}"
		fi
		if [ x"${width:0:1}" = x"m" ]; then
			trim=1; width="${width:1}"
		fi
		if [ x"$width" = x"-" ]; then
			fmt="$fmt%s"
		else
			fmt="$fmt%-${width}s"
			if [ -n "$trim" ] && [ ${#col} -gt "$width" ]; then
				width=$((width - 3))
				col="${col:0:$width}..."
			fi
		fi
		cols[${#cols[@]}]="$col"
		[ -z "$tab" ] || fmt="$fmt\t";
	done
	printf "$fmt\n" "${cols[@]}"
}


print_help()
{
 	path_lookup "cg-$2" "_cg_cmd"
	[ -n "$_cg_cmd" ] || exit 1

	sed -n '/^USAGE=/,0s/.*"\(.*\)"/Usage: \1/p' < "$_cg_cmd"
	if [ x"$1" = xlong ]; then
		echo
		# TODO: Reduce this to just one sed if possible.
		sed -n '3,/^$/s/^# *//p' < "$_cg_cmd" | sed 's/^\(-.*\)::.*/\1::/'
		exit
	fi

	sed -n '3s/^# *//p' < "$_cg_cmd"
	echo
	echo "Options:"
	maxlen="$(sed -n 's/^# \(-.*\)::[^A-Za-z0-9].*/\1/p' < "$_cg_cmd" | column_width)"
	[ $maxlen -ge 11 ] || maxlen=11 # --long-help
	_cg_fmt="  %-20s %s\n"
	sed -n 's/# \(-.*\)::[^A-Za-z0-9]\(.*\)/\1\n\2/p' < "$_cg_cmd" | while read line; do
		case "$line" in
		-*)
			_cg_option="$line"
			;;
		*)
			columns_print "  " - "$_cg_option" "$maxlen" "  $line" -
			;;
		esac
	done
	columns_print "  " - "-h, --help" "$maxlen" "  Print usage summary" -
	columns_print "  " - "--long-help" "$maxlen" "  Print user manual" -
	columns_print "  " - "--version" "$maxlen" "  Print version" -
	exit
}

for option in "$@"; do
	[ x"$option" != x-- ] || break
	if [ x"$option" = x"-h" ] || [ x"$option" = x"--help" ]; then
		print_help short "${_cg_cmd##cg-}"
	elif [ x"$option" = x"--long-help" ]; then
		print_help long "${_cg_cmd##cg-}"
	elif [ x"$option" = x"--version" ]; then
		exec "$(dirname "$0")"/cg-version
	fi
done


ARGS=("$@")
ARGPOS=0
set '' # clear positional parameters - use $ARGS[] instead

if [ -z "$CG_NORC" -a -t 1 -a -e "$HOME/.cgrc" ]; then
	_cg_name="${_cg_cmd#cg-}"
	# We hope that there are no weird (regex-sensitive) characters
	# in Cogito command names.
	_cg_defaults1="$(sed -n "/^$_cg_cmd/s/^$_cg_cmd //p" < "$HOME/.cgrc")"
	_cg_defaults2="$(sed -n "/^$_cg_name/s/^$_cg_name //p" < "$HOME/.cgrc")"
	# And here we explicitly do not quote, allowing multiple arguments
	# to be specified - default word splitting will do its work here.
	ARGS=($_cg_defaults1 $_cg_defaults2 "${ARGS[@]}")
fi

optshift()
{
	unset ARGS[$ARGPOS]
	ARGS=("${ARGS[@]}")
	[ -z "$1" -o -n "${ARGS[$ARGPOS]}" ] ||
		die "option $1 requires an argument"
}

optfail()
{
	die "unrecognized option ${ARGS[$ARGPOS]}"
}

optconflict()
{
	die "conflicting options $CUROPT and $1"
}

optparse()
{
	unset OPTARG
	if [ -z "$1" ]; then
		case "${ARGS[$ARGPOS]}" in
		--)	optshift; return 1 ;;
		-*)	return 0 ;;
		*)	while (( ++ARGPOS < ${#ARGS[@]} )); do
				[[ "${ARGS[$ARGPOS]}" != -- ]] || return 1
				[[ "${ARGS[$ARGPOS]}" != -* ]] || return 0
			done;
			return 1 ;;
		esac
	fi

	CUROPT="${ARGS[$ARGPOS]}"
	local match="${1%=}" minmatch="${2:-1}" opt="$CUROPT" o="$CUROPT" val
	[[ "$1" != *= ]] || val="$match"
	case "$match" in
	--*)
		[ -z "$val" ] || o="${o%%=*}"
		[ ${#o} -ge $((2 + $minmatch)) -a \
			"${match:0:${#o}}" = "$o" ] || return 1
		if [[ -n "$val" && "$opt" == *=?* ]]; then
			ARGS[$ARGPOS]="${opt#*=}"
		else
			optshift "$val"
		fi ;;
	-?)
		[[ "$o" == $match* ]] || return 1
		[[ "$o" != -?-* || -n "$val" ]] || optfail
		ARGS[$ARGPOS]=${o#$match}
		if [ -n "${ARGS[$ARGPOS]}" ]; then
			[ -n "$val" ] || ARGS[$ARGPOS]=-"${ARGS[$ARGPOS]}";
		else
			optshift "$val"
		fi ;;
	*)
		die "optparse cannot handle $1" ;;
	esac

	if [ "$val" ]; then
		OPTARG="${ARGS[$ARGPOS]}"
		optshift
	fi
}


### Example:
