1#! /bin/sh
2#
3# SPDX-License-Identifier: BSD-2-Clause
4#
5# Copyright (c) 2018-2023 Gavin D. Howard and contributors.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are met:
9#
10# * Redistributions of source code must retain the above copyright notice, this
11#   list of conditions and the following disclaimer.
12#
13# * Redistributions in binary form must reproduce the above copyright notice,
14#   this list of conditions and the following disclaimer in the documentation
15#   and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27# POSSIBILITY OF SUCH DAMAGE.
28#
29
30# Just print the usage and exit with an error.
31# @param 1  A message to print.
32usage() {
33	if [ $# -eq 1 ]; then
34		printf '%s\n' "$1"
35	fi
36	printf "usage: %s [-l] NLSPATH main_exec [DESTDIR]\n" "$0" 1>&2
37	exit 1
38}
39
40# Run gencat on one file.
41# @param loc   The location of the resulting cat file.
42# @param file  The file to use as the source for the cat file.
43gencatfile() {
44
45	_gencatfile_loc="$1"
46	shift
47
48	_gencatfile_file="$1"
49	shift
50
51	mkdir -p $(dirname "$_gencatfile_loc")
52	gencat "$_gencatfile_loc" "$_gencatfile_file" > /dev/null 2>&1
53}
54
55# Return an exit code based on whether a locale exists.
56# @param locales  The list of locales.
57# @param locale   The locale to search for.
58# @param destdir  The DESTDIR that locales should be installed to.
59localeexists() {
60
61	_localeexists_locales="$1"
62	shift
63
64	_localeexists_locale="$1"
65	shift
66
67	_localeexists_destdir="$1"
68	shift
69
70	if [ "$_localeexists_destdir" != "" ]; then
71		_localeexists_char="@"
72		_localeexists_locale="${_localeexists_locale%%_localeexists_char*}"
73		_localeexists_char="."
74		_localeexists_locale="${_localeexists_locale##*$_localeexists_char}"
75	fi
76
77	test ! -z "${_localeexists_locales##*$_localeexists_locale*}"
78	return $?
79}
80
81# Split a path into its components. They will be separated by newlines, so paths
82# cannot have newlines in them.
83# @param path  The path to split.
84splitpath() {
85
86	_splitpath_path="$1"
87	shift
88
89	if [ "$_splitpath_path" = "${_splitpath_path#/}" ]; then
90		printf 'Must use absolute paths\n'
91		exit 1
92	fi
93
94	if [ "${_splitpath_path#\n*}" != "$_splitpath_path" ]; then
95		exit 1
96	fi
97
98	_splitpath_list=""
99	_splitpath_item=""
100
101	while [ "$_splitpath_path" != "/" ]; do
102		_splitpath_item=$(basename "$_splitpath_path")
103		_splitpath_list=$(printf '\n%s%s' "$_splitpath_item" "$_splitpath_list")
104		_splitpath_path=$(dirname "$_splitpath_path")
105	done
106
107	if [ "$_splitpath_list" != "/" ]; then
108		_splitpath_list="${_splitpath_list#?}"
109	fi
110
111	printf '%s' "$_splitpath_list"
112}
113
114# Generate a relative path from one path to another.
115# @param path1  The target path.
116# @param path2  The other path.
117relpath() {
118
119	_relpath_path1="$1"
120	shift
121
122	_relpath_path2="$1"
123	shift
124
125	# Very carefully set IFS in a portable way. No, you cannot do IFS=$'\n'.
126	_relpath_nl=$(printf '\nx')
127	_relpath_nl="${_relpath_nl%x}"
128
129	_relpath_splitpath1=`splitpath "$_relpath_path1"`
130	_relpath_splitpath2=`splitpath "$_relpath_path2"`
131
132	_relpath_path=""
133	_relpath_temp1="$_relpath_splitpath1"
134
135	IFS="$_relpath_nl"
136
137	# What this function does is find the parts that are the same and then
138	# calculates the difference based on how many folders up and down you must
139	# go.
140
141	# This first loop basically removes the parts that are the same between
142	# them.
143	for _relpath_part in $_relpath_temp1; do
144
145		_relpath_temp2="${_relpath_splitpath2#$_relpath_part$_relpath_nl}"
146
147		if [ "$_relpath_temp2" = "$_relpath_splitpath2" ]; then
148			break
149		fi
150
151		_relpath_splitpath2="$_relpath_temp2"
152		_relpath_splitpath1="${_relpath_splitpath1#$_relpath_part$_relpath_nl}"
153
154	done
155
156	# Go up the appropriate number of times.
157	for _relpath_part in $_relpath_splitpath2; do
158		_relpath_path="../$_relpath_path"
159	done
160
161	_relpath_path="${_relpath_path%../}"
162
163	# Go down the appropriate number of times.
164	for _relpath_part in $_relpath_splitpath1; do
165		_relpath_path="$_relpath_path$_relpath_part/"
166	done
167
168	_relpath_path="${_relpath_path%/}"
169
170	unset IFS
171
172	printf '%s\n' "$_relpath_path"
173}
174
175script="$0"
176scriptdir=$(dirname "$script")
177
178. "$scriptdir/functions.sh"
179
180# Set a default.
181all_locales=0
182
183# Process command-line args.
184while getopts "l" opt; do
185
186	case "$opt" in
187		l) all_locales=1 ;;
188		?) usage "Invalid option: $opt" ;;
189	esac
190
191done
192shift $(($OPTIND - 1))
193
194test "$#" -ge 2 || usage "Must have at least two arguments"
195
196nlspath="$1"
197shift
198
199main_exec="$1"
200shift
201
202if [ "$#" -ge 1 ]; then
203	destdir="$1"
204	shift
205else
206	destdir=""
207fi
208
209# Uninstall locales first.
210"$scriptdir/locale_uninstall.sh" "$nlspath" "$main_exec" "$destdir"
211
212locales_dir="$scriptdir/../locales"
213
214# What this does is if installing to a package, it installs all locales that
215# match supported charsets instead of installing all directly supported locales.
216if [ "$destdir" = "" ]; then
217	locales=$(locale -a)
218else
219	locales=$(locale -m)
220fi
221
222# For each relevant .msg file, run gencat.
223for file in $locales_dir/*.msg; do
224
225	locale=$(basename "$file" ".msg")
226
227	# If we are not installing all locales, there's a possibility we need to
228	# skip this one.
229	if [ "$all_locales" -eq 0 ]; then
230
231		# Check if the locale exists and if not skip.
232		localeexists "$locales" "$locale" "$destdir"
233		err="$?"
234
235		if [ "$err" -eq 0 ]; then
236			continue
237		fi
238	fi
239
240	# We skip the symlinks for now.
241	if [ -L "$file" ]; then
242		continue
243	fi
244
245	printf 'Installing %s...' "$locale"
246
247	# Generate the proper location for the cat file.
248	loc=$(gen_nlspath "$destdir/$nlspath" "$locale" "$main_exec")
249
250	gencatfile "$loc" "$file"
251
252	printf 'done\n'
253
254done
255
256# Now that we have done the non-symlinks, it's time to do the symlinks. Think
257# that this second loop is unnecessary and that you can combine the two? Well,
258# make sure that when you figure out you are wrong that you add to this comment
259# with your story. Fortunately for me, I learned fast.
260for file in $locales_dir/*.msg; do
261
262	locale=$(basename "$file" ".msg")
263
264	# Do the same skip as the above loop.
265	if [ "$all_locales" -eq 0 ]; then
266
267		localeexists "$locales" "$locale" "$destdir"
268		err="$?"
269
270		if [ "$err" -eq 0 ]; then
271			continue
272		fi
273	fi
274
275	# Generate the proper location for the cat file.
276	loc=$(gen_nlspath "$destdir/$nlspath" "$locale" "$main_exec")
277
278	# Make sure the directory exists.
279	mkdir -p $(dirname "$loc")
280
281	# Make sure to skip non-symlinks; they are already done.
282	if [ -L "$file" ]; then
283
284		printf 'Linking %s...' "$locale"
285
286		# This song and dance is because we want to generate relative symlinks.
287		# They take less space, but also, they are more resilient to being
288		# moved.
289		link=$(readlink "$file")
290		linkdir=$(dirname "$file")
291		locale=$(basename "$link" .msg)
292		linksrc=$(gen_nlspath "$nlspath" "$locale" "$main_exec")
293		relloc="${loc##$destdir/}"
294		rel=$(relpath "$linksrc" "$relloc")
295
296		# If the target file doesn't exist (because it's for a locale that is
297		# not installed), generate it anyway. It's easier this way.
298		if [ ! -f "$destdir/$linksrc" ]; then
299			gencatfile "$destdir/$linksrc" "$linkdir/$link"
300		fi
301
302		# Finally, symlink to the install of the generated cat file that
303		# corresponds to the correct msg file.
304		ln -fs "$rel" "$loc"
305
306		printf 'done\n'
307	fi
308
309done
310