#!/bin/sh
#
# Shell command language linker
#
# Copyright (C) 2015  Patrick "P. J." McDermott
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

set -u

VERSION='0.1.0'

output='out.sh'
make_executable=true
interpreter='/bin/sh'
entry_point='main'

die()
{
	local fmt="${1}"
	shift 1

	printf "shld: ${fmt}\n" "${@}"
	exit 2
}

link()
{
	local input=

	# Open output file.
	if ! exec 3>"${output}~"; then
		die 'Cannot open file "%s"' "${output}~"
	fi

	# Write magic number and interpreter path.
	if ${make_executable}; then
		printf '#!%s\n' "${interpreter}" >&3
	fi

	# Write __init() function.
	cat >&3 <<-'EOF'
		__init_funcs=''
		__init()
		{
			__init_funcs="${__init_funcs} ${1}"
		}
		EOF

	# Read input files.
	for input in "${@}"; do
		if ! cat "${input}" >&3; then
			die 'Cannot read file "%s"' "${input}"
		fi
	done

	# Add call to init functions.
	cat >&3 <<-'EOF'
		for __func in ${__init_funcs}; do
			${__func}
		done
		EOF

	# Add call to entry point.
	if ${make_executable}; then
		printf '%s "${@}"\n' "${entry_point}" >&3
	fi

	# Close output file, make it executable, and set its name.
	exec 3>&-
	if ${make_executable}; then
		if ! chmod a+x "${output}~"; then
			die 'Cannot set mode of file "%s"' "${output}~"
		fi
	fi
	if ! mv "${output}~" "${output}"; then
		die 'Cannot rename file to "%s"' "${output}"
	fi
}

usage()
{
	printf 'Usage: %s [option ...] <file>...\n' "${0}"
}

help()
{
	usage
	cat <<EOF
Options:
  -h           Display this information
  -V           Display linker version information
  -I <interp>  Use <interp> as the interpreter for your program, instead of the
               default of "/bin/sh"
  -e <entry>   Use <entry> as the function for beginning execution of your
               program, instead of the default of "main"
  -o <output>  Use <output> as the name of the program produced by shld, instead
               of the default of "out.sh"
EOF
}

version()
{
	cat <<EOF
shld ${VERSION}
Copyright (C) 2015  Patrick "P. J." McDermott
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
EOF
}

main()
{
	while getopts 'hVI:e:o:' opt; do
		case "${opt}" in
			'h')
				help
				exit
				;;
			'V')
				version
				exit
				;;
			'I')
				interpreter="${OPTARG}"
				;;
			'e')
				entry_point="${OPTARG}"
				;;
			'o')
				output="${OPTARG}"
				;;
		esac
	done
	shift $(($OPTIND - 1))

	if [ ${#} -lt 1 ]; then
		usage >&2
		exit 1
	fi

	link "${@}"
}

main "${@}"