#!/bin/sh

# Copyright 2015-2023 Rivoreo

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


PROGRAM_DIR="`dirname \"$0\"`"
. "$PROGRAM_DIR/chflags.common.sh"

case "`uname -sm`" in
	"Linux mips"*|"Linux ppc"*|"Linux sparc"*)
		EXT2_IOC_GETFLAGS=0x40086601
		EXT2_IOC_SETFLAGS=0x80086602
		;;
	Linux\ *)
		EXT2_IOC_GETFLAGS=0x80086601
		EXT2_IOC_SETFLAGS=0x40086602
		;;
	#*FreeBSD\ *)
	#	EXT2_IOC_GETFLAGS=0x40086601
	#	EXT2_IOC_SETFLAGS=0x80086602
	#	;;
	*)
		echo "Kernel type not supported" 1>&2
		exit 1
esac

EXT2_IMMUTABLE_FL=0x00000010
EXT2_APPEND_FL=0x00000020

if ! toolbox_path="`which toolbox`"; then
	echo "toolbox(1) not available" 1>&2
	return 1
fi

elf_ident="`dd if=\"$toolbox_path\" bs=1 skip=4 count=2 2> /dev/null`"
case "${elf_ident%?}" in
	"`printf '\\001'`")
		flags_size=4
		;;
	"`printf '\\002'`")
		flags_size=8
		;;
	*)
		echo "Failed to determine register size of toolbox(1)" 1>&2
		exit 1
esac
case "${elf_ident#?}" in
	"`printf '\\001'`")
		flags_order=little
		;;
	"`printf '\\002'`")
		flags_order=big
		;;
	*)
		echo "Failed to determine byte order of toolbox(1)" 1>&2
		exit 1
esac

parse_flags_string() {
	local ORIG_IFS="$IFS"
	IFS=,
	set -- $1
	IFS="$ORIG_IFS"
	set_flags=0
	clear_flags=0
	local f set_var_name clear_var_name
	for f in "$@"; do
		fn="${f#no}"
		if [ "$fn" = "$f" ]; then
			set_var_name=set_flags
			clear_var_name=clear_flags
		else
			set_var_name=clear_flags
			clear_var_name=set_flags
		fi
		case "$fn" in
			simmutable)
				f=$EXT2_IMMUTABLE_FL
				;;
			sappend)
				f=$EXT2_APPEND_FL
				;;
			*)
				printf 'chflags: invalid flag: %s.\n' "$f" 1>&2
				return 1
		esac
		eval "$set_var_name=\$(($set_var_name|f))"
		eval "$clear_var_name=\$(($clear_var_name&(~f)))"
	done
}

get_flags() {
	local v
	v="`toolbox ioctl -l $flags_size -r \"$1\" $EXT2_IOC_GETFLAGS`" || return
	v="${v#*
}"
	flags=
	for b in ${v#*:}; do
		b=${b#0x}
		[ "$flags_order" = big ] && flags=$flags$b || flags=$b$flags
	done
	flags=0x$flags
}

case "$1" in
	0*)
		replace_flags="$1"
		;;
	=*)
		parse_flags_string "${1#=}" || exit 255
		replace_flags="$set_flags"
		;;
	*)
		replace_flags=
		parse_flags_string "$1" || exit 255
		;;
esac
shift
[ -n "$no_fail" ] && exec 2> /dev/null
r=0
chflags_loop() {
	local level=$1 f new_flags skip
	shift
	[ $level -gt 0 ] && [ $# = 1 ] && [ "${1%/\*}" != "$1" ] && [ ! -h "$1" ] && [ ! -e "$1" ] && return
	for f in "$@"; do
		if [ -n "$no_follow" ] && [ -h "$f" ]; then
			# Won't be able to ioctl(2) a symbolic link itself, skipping
			if [ -z "$no_fail" ]; then
				printf 'chflags: %s: Not following symbolic link\n' "$f" 1>&2
				r=1
			fi
			continue
		fi
		skip=
		if [ -n "$replace_flags" ]; then
			new_flags="$replace_flags"
		elif get_flags "$f"; then
			new_flags=$(((flags|set_flags)&(~clear_flags)))
			[ $new_flags = $((flags)) ] && skip=2
		else
			#[ -z "$no_fail" ] && r=1
			[ -z "$recursive" ] && continue
			skip=1
		fi
		if [ -z "$skip" ] && toolbox ioctl -l $flags_size -r "$f" $EXT2_IOC_SETFLAGS $new_flags || [ -n "$no_fail" ]; then
			[ -n "$verbose" ] && printf %s\\n "$f"
		elif [ "$skip" != 2 ]; then
			r=1
		fi
		if [ -n "$recursive" ]; then
			case "$recursive_follow" in
				never)
					[ -h "$f" ] && continue
					;;
				explict)
					[ $level -gt 0 ] && [ -h "$f" ] && continue
					;;
			esac
			[ -d "$f" ] && chflags_loop $((level+1)) "$f"/*
		fi
	done
}
chflags_loop 0 "$@"
exit $r
