#!/bin/bash

#
# Based upon Debian's locale-gen, fetched from glibc_2.3.6-7.diff.gz
#

unset POSIXLY_CORRECT IFS
umask 0022

argv0=${0##*/}

EPREFIX="@GENTOO_PORTAGE_EPREFIX@"
if [[ ${EPREFIX} == "@"GENTOO_PORTAGE_EPREFIX"@" ]] ; then
	EPREFIX=""
fi

source "${EPREFIX}"/etc/init.d/functions.sh || {
	echo "${argv0}: Could not source /etc/init.d/functions.sh!" 1>&2
	exit 1
}

show_usage() {
	cat <<-EOF
	Usage: ${HILITE}${argv0}${NORMAL} ${GOOD}[options]${NORMAL} -- ${GOOD}[localedef options]${NORMAL}

	Generate locales based upon the config file /etc/locale.gen.

	${HILITE}Options:${NORMAL}
	    ${GOOD}-k, --keep${NORMAL}               Don't nuke existing locales
	    ${GOOD}-d, --destdir <dir>${NORMAL}      Use locale data in specified DESTDIR tree
	    ${GOOD}-c, --config <config>${NORMAL}    Use specified config instead of default locale.gen
	    ${GOOD}-l, --list${NORMAL}               List all the locales to be generated
	    ${GOOD}-a, --ask${NORMAL}                Ask before generating each locale
	    ${GOOD}-A, --all${NORMAL}                Pretend the locale list contains all locales
	    ${GOOD}-u, --update${NORMAL}             Only generate locales that are missing
	    ${GOOD}-G, --generate <locale>${NORMAL}  Generate specified locale (one shot; implies -k -u)
	    ${GOOD}-j, --jobs <num>${NORMAL}         Number of locales to generate at a time (parallel)
	    ${GOOD}-q, --quiet${NORMAL}              Only show errors
	    ${GOOD}-V, --version${NORMAL}            Meaningless version information
	    ${GOOD}-h, --help${NORMAL}               Show this help cruft

	${HILITE}Localedef Options:${NORMAL}
	    By default, ${GOOD}${LOCALEDEF_OPTS}${NORMAL} is passed to localedef.

	For more info, see the ${HILITE}locale-gen${NORMAL}(1) and ${HILITE}locale.gen${NORMAL}(8) manpages.
	EOF
	[[ -z $@ ]] && exit 0
	echo ""
	eerror "Unknown option '$1'"
	exit 1
}
show_version() {
	local b="(" a=")"
	local cvsver="$Revision: 1.33 $b $Date: 2013/07/01 00:32:15 $a"
	echo "locale-gen-${cvsver//: }"
	exit 0
}



LOCALEDEF_OPTS="-c"
KEEP=""
DESTDIR=""
CONFIG=""
JUST_LIST=""
ASK=""
ALL=""
UPDATE=""
GENERATE=""
JOBS_MAX=1
QUIET=0
SET_X=""
LOCALE_ARCHIVE=true
while [[ -n $1 ]] ; do
	case $1 in
		-k|--keep|--keep-existing) KEEP=$1;;
		-d|--destdir)              shift; DESTDIR=$1; unset ROOT;;
		-c|--config)               shift; CONFIG=$1;;
		-l|--list)                 JUST_LIST=$1;;
		-a|--ask)                  ASK=$1;;
		-A|--all)                  ALL=$1;;
		-u|--update)               UPDATE=$1;;
		-G|--generate)             shift; GENERATE=$1;;
		-j|--jobs)                 shift; JOBS_MAX=$(( $1 ));;
		-j*)                       : $(( JOBS_MAX = ${1#-j} ));;
		-q|--quiet)                : $(( ++QUIET ));;
		-x|--debug)                SET_X="true";;
		-V|--version)              show_version;;
		-h|--help)                 show_usage;;
		--)                        shift; LOCALEDEF_OPTS=$*; break;;
		*)                         show_usage $1;;
	esac
	shift
done
[[ ${JOBS_MAX} -lt 1 ]] && JOBS_MAX=1
[[ -n ${SET_X} ]] && set -x
: ${KEEP:=${JUST_LIST}}
[[ -n ${GENERATE} ]] && UPDATE="true" && KEEP="true"

: ${ROOT:=/}
ROOT="${ROOT%/}/"
if [[ -n ${DESTDIR} ]] && [[ ${ROOT} != "/" ]] ; then
	eerror "DESTDIR and ROOT are mutually exclusive options"
	exit 1
fi
: ${EROOT:="${ROOT%/}${EPREFIX}/"}
if [[ ${EROOT} != "/" ]] ; then
	einfo "Using locale.gen from ROOT ${EROOT}etc/"
fi
if [[ -n ${DESTDIR} ]] ; then
	einfo "Building locales in DESTDIR '${DESTDIR}'"
else
	DESTDIR=${EROOT}
fi

# XXX: should fix this ...
if [[ ${ROOT} != "/" ]] ; then
	eerror "Sorry, but ROOT support is incomplete at this time."
	exit 0
fi

: ${CONFIG:=${EROOT}etc/locale.gen}
LOCALES=${DESTDIR}usr/share/i18n/locales
CHARMAPS=${DESTDIR}usr/share/i18n/charmaps
SUPPORTED=${DESTDIR}usr/share/i18n/SUPPORTED
ALIAS=${DESTDIR}usr/share/locale/locale.alias

#
# Grab any user options in their config file
options=$(sed -n \
	-e '/^[[:space:]]*#%/s:^[[:space:]]*#%[[:space:]]*::p'\
	"${CONFIG}" 2>/dev/null
)
IFS=$'\n'
for option in ${options} ; do
	case ${option} in
		no-locale-archive)
			LOCALE_ARCHIVE=false
			;;
		*)
			ewarn "Unrecognized option '${option}'"
			;;
	esac
done
unset IFS

if ${LOCALE_ARCHIVE} ; then
	if [[ ${JOBS_MAX} != 1 ]] ; then
		ewarn "Generating locale-archive: forcing # of jobs to 1"
		JOBS_MAX=1
	fi
else
	LOCALEDEF_OPTS="--no-archive ${LOCALEDEF_OPTS}"
fi

[[ -n ${ALL} ]] && CONFIG=${SUPPORTED}

# Extract the location of the locale dir on the fly as `localedef --help` has:
#                        locale path    : /usr/lib64/locale:/usr/share/i18n
# For long paths, the line may get wrapped into two, in which case space (' ') is replaced
# by newline (\n).
LOCALEDIR=$(LC_ALL="C" "${DESTDIR}"usr/bin/localedef --help | sed -n -r '/locale path/{N;s|.*:[ \n](.*):/.*|\1|;p}')
LOCALEDIR="${DESTDIR}${LOCALEDIR#${EPREFIX}}"
if [[ $? -ne 0 ]] || [[ -z ${LOCALEDIR} ]] || [[ ${LOCALEDIR} != ${DESTDIR}/usr/lib*/locale ]] ; then
	eerror "Unable to parse the output of your localedef utility." 1>&2
	eerror "File a bug about this issue and include the output of 'localedef --help'." 1>&2
	exit 1
fi



if [[ ${QUIET} -eq 0 ]] && [[ -z ${JUST_LIST} ]] && \
   [[ -e ${EROOT}etc/locales.build ]]
then
	ewarn "You should upgrade your /etc/locales.build to /etc/locale.gen"
	ewarn "and then remove /etc/locales.build when you're done.\n"
fi

locales_to_generate=${GENERATE}

if [[ -z ${locales_to_generate} ]] && [[ -e ${CONFIG} ]] ; then
	locales_to_generate=$(sed \
		-e 's:#.*::' \
		-e '/^[[:space:]]*$/d' \
		"${CONFIG}")
fi

if [[ -z ${locales_to_generate} ]] ; then
	#
	# NOTE: begin retarded situation
	# if user does not have locale.gen setup, but they have a
	# locales.build, and they have USE=-userlocales, then they
	# probably have the default locales.build file.  the problem
	# with this is the default locales.build had a bunch of
	# uncommented locales, so the resulting system will end up
	# with only a few locales and the user will be left wondering
	# wtf just happened.  so our hack logic here is:
	#  - if we are outside of ebuild (i.e. user ran us) then go for it
	#  - if we are inside of ebuild and USE=-userlocales, then just bail
	#

	# if these vars are set we just assume in ebuild
	if [[ ${PN} == "glibc" && -n ${FILESDIR} && -n ${D} ]] ; then
		has() { [[ " ${*:2} " == *" $1 "* ]] ; }
		! has userlocales ${USE} && CONFIG=""
	fi

	if [[ ${CONFIG} == */etc/locale.gen ]] ; then
		CONFIG=${CONFIG/%locale.gen/locales.build}
		if [[ -e ${CONFIG} ]] ; then
			locales_to_generate=$(sed \
				-e 's:#.*::' \
				-e '/^[[:space:]]*$/d' \
				-e 's:/: :g' \
				"${CONFIG}")
		fi
		if [[ -n ${locales_to_generate} ]] && \
		   [[ ${QUIET} -eq 0 ]] && [[ -z ${JUST_LIST} ]]
		then
			ewarn "Automatically importing locales from /etc/locales.build ..."
			ewarn "You really should do this yourself ...\n"
		fi
	fi

	if [[ -z ${locales_to_generate} ]] ; then
		[[ ${QUIET} -eq 0 ]] && [[ -z ${JUST_LIST} ]] && \
			ewarn "No locales found"
		exit 0
	fi
fi

mkdir -p "${LOCALEDIR}"
if [[ -z ${KEEP} && -z ${UPDATE} ]] ; then
	# Remove all old locale dir and locale-archive before generating new
	# locale data.  Scrubbing via update is done elsewhere.
	rm -rf "${LOCALEDIR}"/* || true
fi

# Transform the name in locales.gen to the name used when storing
# the locale data in /usr/lib/locale/ ... this normalize algo is
# taken out of the glibc localedef source code ...
normalize() {
	if [[ $1 == *.* ]] ; then
		local ret=$(echo ${1##*.} | tr '[[:upper:]]' '[[:lower:]]')
		echo ${1%%.*}.${ret//-}
	else
		echo $1
	fi
}

# These funky sed's are based on the stuff in glibc's localedata/Makefile
# Basically we want to rewrite the display like so:
# <locale without a . or @>.<charmap>[@extra stuff after the @ in the locale]
# en_US       ISO-8859-1  -> en_US.ISO-8859-1
# en_US.UTF-8 UTF-8       -> en_US.UTF-8
# de_DE@euro  ISO-8859-15 -> de_DE.ISO-8859-15@euro
locales_disp=$(echo "${locales_to_generate}" | sed \
	-e '   /@/   s:[[:space:]]*\([^@[:space:]]*\)\([^[:space:]]*\)[[:space:]]\+\([^[:space:]]*\):\1.\3\2:' \
	-e '/^[^@]*$/s:[[:space:]]*\([^.[:space:]]*\)\([^[:space:]]*\)[[:space:]]\+\([^[:space:]]*\):\1.\3:')

eval declare -a locales_disp=(${locales_disp})
eval declare -a locales_to_generate=(${locales_to_generate})
total=$(( ${#locales_to_generate[*]} / 2 ))

[[ ${QUIET} -eq 0 ]] && [[ -z ${JUST_LIST} ]] && \
einfo "Generating ${total} locales (this might take a while) with ${JOBS_MAX} jobs"

if [[ -n ${UPDATE} ]] ; then
	# normalize newlines into spaces
	existing_locales=" $(echo $(locale -a 2>/dev/null)) "
fi

generate_locale() {
	local output=""

	if [[ -z ${ASK} ]] && [[ ${QUIET} -eq 0 ]] ; then
		output=" (${cnt_fmt}/${total}) Generating ${disp}"
	fi

	if [[ $(( JOB_IDX_E - JOB_IDX_S )) == ${JOBS_MAX} ]] ; then
		wait ${JOB_PIDS[$(( JOB_IDX_S++ ))]}
		JOB_RETS+=( $? )
	fi
	(
		# Accumulate all the output in one go so the parallel
		# jobs don't tromp on each other
		x=$(
			[[ -n ${output} ]] && ebegin "${output}"
			"${DESTDIR}"usr/bin/localedef ${LOCALEDEF_OPTS} \
				-i "${input}" \
				-f "${charmap}" \
				-A "${ALIAS}" \
				--prefix "${DESTDIR%${EPREFIX}/}/" \
				"${locale}" 2>&1
			ret=$?
			[[ -n ${output} ]] && eend ${ret}
			exit ${ret}
		)
		ret=$?
		[[ -n ${output} ]] && echo "${x}"
		exit ${ret}
	) &
	JOB_PIDS+=( $! )
	: $(( ++JOB_IDX_E ))

	if [[ ${ret} != 0 && ${locale} == */* ]] ; then
		ewarn "Perhaps you meant to use a space instead of a / in your config file ?"
	fi
}

JOB_PIDS=()
JOB_RETS=()
JOB_IDX_S=0
JOB_IDX_E=0
cnt=0
lidx=0
while [[ -n ${locales_to_generate[${lidx}]} ]] ; do
	: $(( ++cnt ))
	locale=${locales_to_generate[$((lidx++))]}
	charmap=${locales_to_generate[$((lidx++))]}

	# XXX: if we wanted to, we could check existence of
	#      ${LOCALES}/${locale} and ${CHARMAPS}/${charmap}
	#      this would fail for things like "en_US.UTF8", but
	#      in that case we could fall back to checking the
	#      SUPPORTED file ... then again, the localedef
	#      below will abort nicely for us ...
	if [[ -z ${locale} || -z ${charmap} ]] ; then
		eerror "Bad entry in locale.gen: '${locale} ${charmap}'; skipping"
		continue
	fi

	disp=${locales_disp[$(( cnt - 1 ))]}

	if [[ -n ${UPDATE} ]] ; then
		normalized_locale=$(normalize ${locale})
		if [[ ${existing_locales} == *" ${normalized_locale} "* ]] ; then
			existing_locales=${existing_locales/ ${normalized_locale} / }
			if [[ ${QUIET} -eq 0 ]] ; then
				cnt_fmt=$(printf "%${#total}i" ${cnt})
				einfo " (${cnt_fmt}/${total}) Skipping ${disp}"
			fi
			continue
		fi
	fi

	# If the locale is like 'en_US.UTF8', then we really want 'en_US'
	if [[ -f ${LOCALES}/${locale} ]] ; then
		input=${locale}
	else
		input=${locale%%.*}
	fi

	if [[ -z ${JUST_LIST} ]] ; then
		# Format the output for the question/status
		cnt_fmt=$(printf "%${#total}i" ${cnt})
		if [[ -n ${ASK} ]] ; then
			einfon " (${cnt_fmt}/${total}) Generate ${disp} ? (Y/n) "
			read user_answer
			[[ ${user_answer} == [nN]* ]] && continue
		fi
		generate_locale
	else
		echo "${disp}"
	fi
done

for (( i = JOB_IDX_S; i < JOB_IDX_E; ++i )) ; do
	wait ${JOB_PIDS[$(( i++ ))]}
	JOB_RETS+=( $? )
done
ret=$(( 0 ${JOB_RETS[@]/#/+} ))

[[ ${QUIET} -eq 0 ]] && [[ -z ${JUST_LIST} ]] && \
einfo "Generation complete"

# Remove locales that existed but were not requested
if [[ -n ${UPDATE} ]] ; then
	# Ignore these pseudo locales
	existing_locales=${existing_locales/ C / }
	existing_locales=${existing_locales/ POSIX / }
	if [[ -n ${existing_locales// } ]] ; then
		if [[ -z ${KEEP} ]] ; then
			[[ ${QUIET} -eq 0 ]] && einfo "Scrubbing old locales:"${existing_locales}
			cd "${LOCALEDIR}" && rm -rf ${existing_locales}
		else
			[[ ${QUIET} -eq 0 ]] && einfo "Keeping old locales:"${existing_locales}
		fi
	fi
fi

exit ${ret}
