-
Simon McVittie authored
At the moment it writes to the current working directory, and we invoke it via `bwrap ... --chdir DIRECTORY`; but we want to make it possible to use this code from inside a Flatpak runtime (at least in the case where we're using the host system's glibc), which means we won't be allowed to run bwrap and must do this some other way. Rather than using `env --chdir=...` (which isn't supported in scout anyway), let's teach this script to output to an explicitly specified directory. Signed-off-by:
Simon McVittie <smcv@collabora.com>
Simon McVittie authoredAt the moment it writes to the current working directory, and we invoke it via `bwrap ... --chdir DIRECTORY`; but we want to make it possible to use this code from inside a Flatpak runtime (at least in the case where we're using the host system's glibc), which means we won't be allowed to run bwrap and must do this some other way. Rather than using `env --chdir=...` (which isn't supported in scout anyway), let's teach this script to output to an explicitly specified directory. Signed-off-by:
Simon McVittie <smcv@collabora.com>
pressure-vessel-locale-gen 9.00 KiB
#!/bin/sh
# Copyright © 2019 Collabora Ltd.
#
# SPDX-License-Identifier: MIT
#
# 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 AUTHORS OR 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.
set -e
set -u
me="$0"
me="$(readlink -f "$0")"
here="${me%/*}"
me="${me##*/}"
opt_output_dir=.
opt_verbose=
# Look for try-setlocale helper executable next to this script
PATH="${here}:${PATH}"
LOCALE_ALIAS="/usr/share/locale/locale.alias"
if ! [ -f "$LOCALE_ALIAS" ]; then
LOCALE_ALIAS=
fi
# We currently only look for I18NDIR data in /usr/share/i18n, the
# glibc default path.
#
# If we discover that some distros use a different default, then we
# should enhance this script to iterate through a search path.
#
# Please keep this in sync with srt_system_info_get_locale_issues().
SUPPORTED="/usr/share/i18n/SUPPORTED"
log () {
printf '%s\n' "$me: $*" >&2
}
verbose () {
if [ -n "$opt_verbose" ]; then
log "$@"
fi
}
# Return nonzero if the given locale cannot be generated normally.
can_generate () {
local locale="$1"
case "$locale" in
(C|C.UTF-8|C.utf8|POSIX|"")
return 1
;;
(*..*|*/*)
log "Avoiding potential path traversal in '$locale'"
return_ex_config=1
return 1
;;
(*)
return 0
;;
esac
}
generate_locale () {
local at_modifier=
local codeset=
local language=
local list=
local locale="$1"
local modifier=
local supported_codeset=
local territory=
local underscore_territory=
local without_codeset=
local status=
language="$locale"
case "$language" in
(*@*)
modifier="${language##*@}"
at_modifier="@${modifier}"
language="${language%@*}"
;;
esac
case "$language" in
(*.*)
codeset="${language##*.}"
language="${language%.*}"
;;
esac
case "$language" in
(*_*)
territory="${language##*_}"
underscore_territory="_${territory}"
language="${language%_*}"
;;
esac
# Speed up locale generation a bit by making e.g. en_GB.utf8 a
# symlink to en_GB.UTF-8
if [ "${codeset}" = 'utf8' ]; then
codeset="UTF-8"
locale="${language}${underscore_territory}.UTF-8${at_modifier}"
ln -fns "$locale" \
"${opt_output_dir}/${language}${underscore_territory}.utf8${at_modifier}"
if [ -d "$opt_output_dir/$locale" ]; then
return 0
fi
fi
if [ -n "$codeset" ]; then
# We don't really need to parse $SUPPORTED to find out that
# en_US.UTF-8 is a UTF-8 locale. Do nothing. In particular,
# this works on distributions like Arch Linux that do not
# normally install $SUPPORTED.
:
else
for list in $SUPPORTED; do
if supported_codeset="$(
while read -r supported codeset; do
case "$supported" in
(\#*)
continue
;;
esac
if [ "$supported" = "$locale" ]; then
printf '%s' "$codeset"
fi
done < "$list"
)"; then
if [ -n "$supported_codeset" ]; then
break
fi
else
supported_codeset=
verbose "Unable to read $list"
fi
done
if [ -n "$supported_codeset" ]; then
codeset="$supported_codeset"
else
log "Unable to find $locale in $SUPPORTED"
log "Assuming UTF-8 and hoping that works..."
codeset="UTF-8"
fi
fi
without_codeset="${language}${underscore_territory}${at_modifier}"
if localedef \
${LOCALE_ALIAS+-A "${LOCALE_ALIAS}"} \
--no-archive \
-c \
-f "$codeset" \
-i "$without_codeset" \
"${opt_output_dir}/${locale}" \
; then
log "Generated locale $locale successfully"
else
status="$?"
if [ "$status" = 1 ]; then
log "Generated locale $locale, with warnings"
else
log "Unable to generate locale $locale: $status"
fi
fi
if ! LOCPATH="${opt_output_dir}" pressure-vessel-try-setlocale "$locale"; then
log "Warning: $locale was generated but does not appear to work!"
fi
}
usage () {
local code="$1"
shift
if [ "$code" -ne 0 ]; then
exec >&2
fi
echo "Usage: $me [OPTIONS] [LOCALE...]"
echo
echo "Generate the locales required by the locale environment"
echo "variables, and any extra locales from the command line,"
echo "in the current working directory."
echo
echo "Options:"
echo "--output-dir DIR, -o DIR"
echo " Create locales in the given directory [default: .]"
echo "--verbose Be more verbose"
echo
echo "Arguments:"
echo "LOCALE Zero or more locales in POSIX format,"
echo " such as en_US.UTF-8, be_BY@latin"
exit "$code"
}
getopt_temp="help"
getopt_temp="${getopt_temp},output-dir:,output-directory:"
getopt_temp="${getopt_temp},verbose"
getopt_temp="$(getopt -o 'o:' --long "$getopt_temp" -n "$me" -- "$@")"
eval "set -- $getopt_temp"
unset getopt_temp
while [ "$#" -gt 0 ]; do
case "$1" in
(-o | --output-dir | --output-directory)
opt_output_dir="$2"
shift 2
;;
(--help)
usage 0
# not reached
;;
(--verbose)
opt_verbose=yes
shift
;;
(--)
shift
break
;;
(-*)
log "Unknown option: $1"
usage 64 # EX_USAGE from sysexits.h
# not reached
;;
(*)
break
;;
esac
done
one_missing=
for var in \
LC_ADDRESS \
LC_CTYPE \
LC_COLLATE \
LC_IDENTIFICATION \
LC_MEASUREMENT \
LC_MESSAGES \
LC_MONETARY \
LC_NUMERIC \
LC_NAME \
LC_PAPER \
LC_TELEPHONE \
LC_TIME \
\
HOST_LC_ALL \
LANG \
LC_ALL \
; do
locale="$(eval "printf '%s' \${${var}-}")"
if [ -z "$locale" ] || ! can_generate "$locale"; then
continue
fi
if ! pressure-vessel-try-setlocale "$locale"; then
one_missing=yes
break
fi
done
for locale in en_US.UTF-8 "$@"; do
if ! can_generate "$locale" ; then
continue
fi
if ! pressure-vessel-try-setlocale "$locale"; then
verbose "Missing locale $locale"
one_missing=yes
break
fi
done
if [ -z "$one_missing" ]; then
verbose "No locales need to be generated"
exit 0
fi
# We have to generate all the locales we want, not just the ones that
# were missing, because they might have been in a locale archive,
# and setting LOCPATH disables use of the locale archive.
return_ex_cantcreat=
return_ex_config=
for var in \
LC_ADDRESS \
LC_CTYPE \
LC_COLLATE \
LC_IDENTIFICATION \
LC_MEASUREMENT \
LC_MESSAGES \
LC_MONETARY \
LC_NUMERIC \
LC_NAME \
LC_PAPER \
LC_TELEPHONE \
LC_TIME \
\
HOST_LC_ALL \
LANG \
LC_ALL \
; do
locale="$(eval "printf '%s' \${${var}-}")"
if [ -z "$locale" ] || ! can_generate "$locale" ; then
continue
fi
if ! [ -d "$locale" ]; then
verbose "Generating locale $locale..."
generate_locale "$locale" || return_ex_cantcreat=yes
fi
done
for locale in en_US.UTF-8 "$@"; do
if ! can_generate "$locale" ; then
continue
fi
if ! [ -d "$locale" ]; then
verbose "Generating locale $locale..."
generate_locale "$locale" || return_ex_cantcreat=yes
fi
done
if [ -n "$return_ex_config" ]; then
exit 78 # EX_CONFIG, "configuration error", from sysexits.h
elif [ -n "$return_ex_cantcreat" ]; then
exit 73 # EX_CANTCREATE, "can't create (user) output file"
else
exit 72 # EX_OSFILE, "OS file missing"
fi
# vim:set sw=4 sts=4 et: