#! /bin/bash
#########################################################################
#									#
#	mget is automatically generated,				#
#		please do not modify!					#
#									#
#########################################################################

#########################################################################
#									#
# Script ID: mget							#
# Author: Copyright (C) 2013-2017, 2019, 2021  Mark Grant		#
#									#
# Released under the GPLv3 only.					#
# SPDX-License-Identifier: GPL-3.0-only					#
#									#
# Purpose:								#
# Retrieves one or more files whose URL's are specified in a text file.	#
#                                         	                     	#
# Syntax:      mget [OPTIONS]						#
#			[OPTIONS] are:-					#
#	-g or --group group - Save target file with 'group' owner.	#
#	-h or --help Displays usage information.			#
#	-o or --owner owner - Save target file with 'owner' owner.	#
#	-p or --persist Saves command line options for future use.	#
#	-q or --quiet Quiet mode, limited output.			#
#	-s or --silent Silent mode, no output.				#
#	-S or --sourcefile sourcefile Name of the file containing the	#
#		URL's of files to be fetched.				#
#	-T or --targetdir targetdir Directory in which to store		#
#		retieved files.						#
#	-u or --unix Use Unix LF instead of Windows CR/LF line endings.	#
#	-v or --verbose Verbose mode.					#
#	-V or --version Displays version information.			#
#	-w or --windows Use Windows CR/LF instead of Unix LF line	#
#		endings.						#
#									#
# Exit codes used:-							#
# Bash standard Exit Codes:	0 - success				#
#				1 - general failure			#
# User-defined exit code range is 64 - 113				#
#	C/C++ Semi-standard exit codes from sysexits.h range is 64 - 78	#
#		EX_USAGE	64	command line usage error	#
#		EX_DATAERR	65	data format error		#
#		EX_NOINPUT	66	cannot open input		#
#		EX_NOUSER	67	addressee unknown		#
#		EX_NOHOST	68	host name unknown		#
#		EX_UNAVAILABLE	69	service unavailable		#
#		EX_SOFTWARE	70	internal software error		#
#		EX_OSERR	71	system error (e.g., can't fork)	#
#		EX_OSFILE	72	critical OS file missing	#
#		EX_CANTCREAT	73	can't create (user) output file	#
#		EX_IOERR	74	input/output error		#
#		EX_TEMPFAIL	75	temp failure; user is invited	#
#					to retry			#
#		EX_PROTOCOL	76	remote error in protocol	#
#		EX_NOPERM	77	permission denied		#
#		EX_CONFIG	78	configuration error		#
#	User-defined (here) exit codes range 79 - 113:			#
#		None							#
#									#
# Further Info:								#
#									#
#########################################################################

#########################################################################
#									#
# Changelog								#
#									#
# Date		Author	Version	Description				#
#									#
# 29/04/2013	MG	1.0.1	Created.Does not implement mail		#
#				notification.				#
# 30/04/2013	MG	1.0.2	Corrected Error status section on man	#
#				page. Introduced sed processing on	#
#				source file to ensure it has Unix type	#
#				line endings. Removed part implemented	#
#				'MailTo' option.			#
# 02/05/2013	MG	1.0.3	Introduce -q option to enable use of	#
#				fetch or wget quiet mode.		#
# 03/05/2013	MG	1.0.4	Included source and target information	#
#				in script output to be more useful.	#
# 05/05/2013	MG	1.0.5	Fixed ugly exit if 'sourcefile' = "" or	#
#				'targetdir' = "". 'targetdir' is now	#
#				defaulted to 'PWD'.			#
# 22/08/2013	MG	1.0.6	Debian best practice documentation	#
#				requires that programs placed in the	#
#				PATH should not have extensions as the	#
#				programs may be rewritten in a		#
#				different language. This would then	#
#				require a name change, or, if left	#
#				as-is would cause confusion. So name	#
#				changed to remove .sh extension.	#
# 13/12/2013	MG	1.0.7	Changed stdout & stderr message output	#
#				to use a function directing to one or	#
#				other based on a status.		#
# 14/12/2013	MG	1.0.8	Changed so that output function is the	#
#				only place to insert the program name	#
#				and it uses $0.				#
# 15/12/2013	MG	1.0.9	Use command name instead of $0.		#
# 01/01/2014	MG	1.0.10	Tidied up use of output function.	#
# 05/01/2014	MG	1.0.11	Introduced new standardised error	#
#				handler routine to test previous	#
#				command success.			#
#				Changed command line options to use -V	#
#				as the version option, S as Source URL	#
#				and -T as target directory. Introduced	#
#				-q, -s & -v as quiet, silent and	#
#				verbose	options. Fixed error handling	#
#				on existing file deletion.		#
# 06/01/2014	MG	1.0.12	Persist option code was in the file	#
#				processing loop and never got		#
#				executed if no files were to be		#
#				processed. Moved out of loop.		#
# 27/08/2014	MG	1.0.13	Change name to mget to avoid package	#
#				name duplication in Ubuntu.		#
# 03/09/2014	MG	1.1.1	Change to utilise configure.ac's check	#
#				on whether wget or fetch is in use.	#
#				Change to use awk script to convert to	#
#				and from Windows / Unix text file line	#
#				endings to ensure the existing ending 	#
#				is tested to determine if conversion is	#
#				required.				#
# 28/11/2014	MG	1.1.2	Switch to use GNU getopt to enable long	#
#				options.				#
# 28/11/2014	MG	1.1.3	Add overall package version to -V.	#
# 01/12/2014	MG	1.1.4	Replaced windows line ending conversion	#
#				code wuth use of wutconv.		#
# 01/12/2014	MG	1.1.5	Allow mget to retrieve specified file	#
#				with no line ending conversion as	#
#				default. Add --unix option as complement#
#				to --windows option.			#
# 26/06/2015	MG	1.1.6	Remove BSD support.			#
# 01/07/2017	MG	1.1.7	Enforce 80 column rule.			#
# 03/12/2017	MG	1.1.8	Add SPDX license tags to source files.	#
#				Adopt normal exit code policy; 0 on	#
#				success, 1 on failure.			#
# 06/09/2019	MG	1.2.1	Do not hardcode outputprefix.		#
#				Use C style formatting of conditional	#
#				and loop statements.			#
#				Use true booleans.			#
#				Use (( ... )) for numerics.		#
#				Remove useless and broken check for -p	#
#				only CL.				#
#				Refactor into functions.		#
#				Prefer [[ ... ]] AOT [ ... ]		#
#				Prefer printf AOT echo.			#
#				Use more meaningful exit codes.		#
#				Use actual bash path provided by	#
#				configure in shebang.			#
#				Do not automatically create the config	#
#				file.					#
#				If get is successful delete entry from	#
#				source file, otherwise retain for	#
#				possible re-run.			#
# 11/10/2019	MG	1.2.2	Move script_exit() before it is used.	#
# 26/11/2021	MG	1.2.3	Tighten SPDX tag.			#
#									#
#########################################################################


##################
# Init variables #
##################

readonly outputprefix="$(basename $0):"
readonly version=1.2.3			# set version variable
readonly packageversion=1.2.0	# Version of the complete package

readonly conffile="$HOME/.mget"
owner=false
ownergroup=false
persist=false
script_exit_code=0
sourcefile=""
targetdir=""
unix=false
verbosity="-nv"		# wget defaults to verbose so make non-verbose default
windows=false


#############
# Functions #
#############

# -h --help output.
# No parameters
# No return value
usage ()
{
cat << EOF
Usage is $0 [OPTIONS]
	[OPTIONS] are:-
	-g or --group newgroup Save target file with 'newgroup' owner.
	-h or --help Displays usage information.
	-o or --owner newowner Save target file with 'newowner' owner.
	-p or --persist Saves command line options for future use.
	-q or --quiet Quiet mode, limited output.
	-s or --silent Silent mode, no output.
	-S or --sourcefile sourcefile Name of file containing source URL's.
		Can be persisted with -p.
	-T or --targetdir targetdir Name of directory for storing retrieved
		files. Can be persisted with -p.
	-u or --unix Use Unix LF line endings instead of Windows CR/LF line
		endings.
	-v or --verbose Verbose mode.
	-V or --version Displays version information.
	-w or --windows Use Windows CR/LF line endings instead of Unix LF
		endings.
EOF
}

# Standard function to emit messages depending on various parameters.
# Parameters -	$1 What:-	The message to emit.
#		$2 Where:-	stdout == 0
#				stderr == 1
# No return value.
output()
{
	if (( !$2 )); then
		printf "%s %s\n" $outputprefix "$1"
	else
		printf "%s %s\n" $outputprefix "$1" 1>&2
	fi
}

# Standard function to tidy up and return exit code.
# Parameters - 	$1 is the exit code.
# No return value.
script_exit()
{
	exit $1
}

# Standard function to test command error and exit if non-zero.
# Parameters - 	$1 is the exit code, (normally $? from the preceeding command).
# No return value.
std_cmd_err_handler()
{
	if (( $1 )); then
		script_exit $1
	fi
}

# Standard trap exit function.
# No parameters.
# No return value.
trap_exit()
{
	local -i exit_code=$?
	local msg

	msg="Script terminating with exit code $exit_code due to trap received."
	output "$msg" 1
	script_exit $exit_code
}

# Setup trap.
trap trap_exit SIGHUP SIGINT SIGQUIT SIGTERM

# Function to write config file.
# No parameters.
# No return value.
write_file()
{
	printf "%s\n" "sourcefile=$sourcefile" >> $conffile
	printf "%s\n" "targetdir=$targetdir" >> $conffile
}

# Read or create config file.
# No parameters.
# No return value.
proc_config_file()
{
	local old_IFS

	if [[ -f $conffile ]]; then
		old_IFS=$IFS
		IFS="="
		exec 3<$conffile
		while read -u3 -ra input; do
			case ${input[0]} in
			sourcefile)
				sourcefile=${input[1]}
				;;
			targetdir)
				targetdir=${input[1]}
				;;
			esac
		done
		exec 3<&-
		IFS=$old_IFS
	fi
}

# Process command line arguments with GNU getopt.
# Parameters -	$1 is the command line.
# No return value.
proc_CL()
{
	local GETOPTTEMP
	local tmpGETOPTTEMP

	tmpGETOPTTEMP="getopt -o g:ho:pqsS:T:uvVw "
	tmpGETOPTTEMP+="--long group:,help,owner:,persist,quiet,silent,"
	tmpGETOPTTEMP+="sourcefile:,targetdir:,unix,verbose,version,windows"
	GETOPTTEMP=$($tmpGETOPTTEMP -n "$0" -- "$@")
	std_cmd_err_handler $?

	eval set -- "$GETOPTTEMP"
	std_cmd_err_handler $?

	while true; do
		case "$1" in
		-g|--group)
			ownergroup=true
			newownergroup=$2
			shift 2
			;;
		-h|--help)
			usage
			shift
			script_exit 0
			;;
		-o|--owner)
			owner=true
			newowner=$2
			shift 2
			;;
		-p|--persist)
			persist=true
			shift
			;;
		-q|--quiet)
			verbosity="-q"
			shift
			;;
		-s|--silent)
			verbosity=""
			exec 1>/dev/null 2>/dev/null
			shift
			;;
		-S|--sourcefile)
			sourcefile=$2
			shift 2
			;;
		-T|--targetdir)
			targetdir=$2
			shift 2
			;;
		-u|--unix)
			unix=true
			shift
			;;
		-v|--verbose)
			verbosity="-v"
			shift
			;;
		-V|--version)
			printf "%s Script version %s\n" $0 $version
			printf "%s Package version %s\n" $0 $packageversion
			shift
			script_exit 0
			;;
		-w|--windows)
			windows=true
			shift
			;;
		--)	shift
			break
			;;
		*)	output "Internal error." 1
			script_exit 64
			;;
		esac
	done

	# Script does not accept other arguments.
	if (( $# )); then
		output "Invalid argument." 1
		script_exit 64
	fi

	# u and w flag cannot both be set.
	if $windows && $unix; then
		output "-u and -w cannot both be set." 1
		script_exit 64
	fi
}

# Check source file exists and is readable and writeable.
# No parameters.
# No return value.
validate_source_file()
{
	if [[ ! -f "$sourcefile" ]]; then
		output "Source file not valid." 1
		script_exit 66
	fi

	if [[ ! -r "$sourcefile" ]]; then
		output "Source file not readable." 1
		script_exit 77
	fi

	if [[ ! -w "$sourcefile" ]]; then
		output "Source file not writeable." 1
		script_exit 77
	fi
}

# Check target directory exists and is writable.
# No parameters.
# No return value.
validate_target_dir()
{
	if [[ ! $targetdir ]]; then
		targetdir=$PWD
	fi

	if [[ ! -d "$targetdir" ]]; then
		output "Target directory not valid." 1
		script_exit 66
	fi

	if [[ ! -w "$targetdir" ]]; then
		output "Target directory not writeable." 1
		script_exit 77
	fi
}


########
# Main #
########

proc_config_file

proc_CL "$@"

validate_source_file

# Now ensure source file is in Unix text format.
wutconv --unix $sourcefile
std_cmd_err_handler $?

validate_target_dir

# Now get the files
touch "$sourcefile".new

old_IFS=$IFS
IFS="/"
exec 3<"$sourcefile"
while read -u3 -ra input; do
	outputfile=${input[${#input[*]}-1]}
	output "Attempting to get file - ""${input[*]}" 0
	output "Attempting to save file to - ""$targetdir/$outputfile" 0

	wget $verbosity --no-check-certificate -O "$targetdir/$outputfile" \
		"${input[*]}"
	status=$?

	output "File get completed with status "$status $status
	((script_exit_code +=  status))

	# If this wget errored then skip to the next.
	if (( status )); then
		rm -f "$targetdir/$outputfile"
		printf "%s\n" "${input[*]}" >> "$sourcefile".new
		continue
	fi

	# If Windows CR/LF line ending required, convert.
	if $windows; then
		wutconv --windows --verbose "$targetdir/$outputfile"
		status=$?
		((script_exit_code += status))
	fi
	# If Unix LF line ending required, convert.
	if $unix; then
		wutconv --unix --verbose "$targetdir/$outputfile"
		status=$?
		((script_exit_code += status))
	fi
	# Change owner?
	if $owner; then
		chown $newowner "$targetdir/$outputfile"
		status=$?
		output "Owner change complete with status "$status $status
		((script_exit_code += status))
	fi
	#Change group?
	if $ownergroup; then
		chown :$newownergroup "$targetdir/$outputfile"
		status=$?
		msg="Owner group change complete with status "$status
		output "$msg" $status
		((script_exit_code += status))
	fi
done
exec 3<&-
IFS=$old_IFS

rm -f "$sourcefile"
mv -f "$sourcefile".new "$sourcefile"

# If persist is TRUE, save parameters.
if $persist; then
	rm -f $conffile
	write_file
fi

# And exit
output "Script complete with exit code: "$script_exit_code $script_exit_code
script_exit $script_exit_code

