#!/bin/bash -efu

. shell-error
. shell-quote

UDEV_BUILTINS=()
readarray -t UDEV_BUILTINS <<< "$(
	"$UDEVADM" test-builtin --help 2>&1 >/dev/null |
		sed -nr -e 's/^[[:space:]]+([^[:space:]]+)[[:space:]]+.*$/\1/p' |
		sort -u
)"

FILES=()
put_file()
{
	local a
	for a in "${FILES[@]}"; do
		[ "$1" != "$a" ] || return 0
	done
	FILES+=("$1")
}

is_builtin()
{
	local a
	for a in "${UDEV_BUILTINS[@]}"; do
		[ "$a" != "$1" ] || return 0
	done
	return 1
}

parse_command()
{
	local rule hint cmd args arg path

	rule="$1"; shift
	hint="$1"; shift

	quote_shell_args args "$1"
	eval "set -- $args"

	cmd=
	for arg; do
		case "$arg" in
			'$'*|[A-Za-z_]*=*)
				continue
				;;
		esac

		cmd="$arg"
		break
	done

	[ -n "$cmd" ] ||
		return 0

	if [ -z "${cmd##/*}" ]; then
		# Absolute path is given.
		put_file "$cmd"
		return 0
	fi

	case "$rule" in
		program)
			;;
		run)
			case "$hint" in
				builtin)
					! is_builtin "$cmd" ||
						return 0
					;;
			esac
			;;
		import)
			case "$hint" in
				file)
					fatal "it's not obvious where to look for '$cmd'"
					;;
				program|builtin)
					! is_builtin "$cmd" ||
						return 0
					;;
			esac
			;;
		*)
			;;
	esac

	local PATH="/lib/udev:$PATH"

	# Search external command
	if path=$(type -P "$cmd" 2>/dev/null); then
		put_file "$path"
		return 0
	fi

	fatal "command not found: $cmd"
}

while IFS=$'\t' read -r file rule attr cmd; do
	[ -n "$cmd" ] ||
		continue

	# Skip initramfs-only wrappers for real command.
	# shellcheck disable=SC2043
	for w in /bin/udev-run; do
		if [ -z "${cmd##$w *}" ]; then
			cmd="${cmd#$w }"
			break
		fi
	done

	verbose "$rule{$attr} = $cmd"
	parse_command "$rule" "$attr" "$cmd"
done < <(udev-rules -x "$1")

for a in "${FILES[@]}"; do
	printf '%s\n' "$a"
done
