1#! /bin/sh
2#	$NetBSD: msg_xlat.sh,v 1.6 2021/10/11 18:46:34 rillig Exp $
3
4#-
5# Copyright (c) 2003 The NetBSD Foundation, Inc.
6# All rights reserved.
7#
8# This code is derived from software contributed to The NetBSD Foundation
9# by David Laight.
10#
11# Redistribution and use in source and binary forms, with or without
12# modification, are permitted provided that the following conditions
13# are met:
14# 1. Redistributions of source code must retain the above copyright
15#    notice, this list of conditions and the following disclaimer.
16# 2. Redistributions in binary form must reproduce the above copyright
17#    notice, this list of conditions and the following disclaimer in the
18#    documentation and/or other materials provided with the distribution.
19#
20# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30# POSSIBILITY OF SUCH DAMAGE.
31#
32
33PROG=$(basename "$0")
34usage()
35{
36	echo "Usage: $PROG [-ci] [-d msg_defs.h] [-f fmt_count]" >&2
37	exit 1
38}
39
40error()
41{
42	echo "$PROG: ERROR $@" >&2
43}
44
45IGNORE_MISSING_TRANSLATIONS=false
46count_fmtargs=false
47msg_defs=msg_defs.h
48
49while getopts cd:f:i f
50do
51	case "$f" in
52	c) count_fmtargs=true;;
53	d) msg_defs=$OPTARG;;
54	f) fmt_count=$OPTARG;;
55	i) IGNORE_MISSING_TRANSLATIONS=true;;
56	*) usage;;
57	esac
58done
59shift $(($OPTIND - 1))
60if [ "$#" -ne 0 ]; then usage; fi
61
62nl='
63'
64msg_long='((msg)(long)'
65close_paren=')'
66open_brace='{'
67close_brace='}'
68slash=/
69
70rval=0
71
72# save stdin while we read the other files
73exec 3<&0
74
75# Read existing list of format arg counts
76if [ -n "$fmt_count" ]; then
77	exec <$fmt_count || exit 2
78	while read name count
79	do
80		eval count_$name=\$count
81	done
82fi
83
84# Read header file and set up map of message names to numbers
85
86exec <$msg_defs || exit 2
87
88while read define MSG_name number rest
89do
90	if [ -z "$number" ] || [ -n "$rest" ]; then continue; fi
91	if [ "$define" != "#define" ]; then continue; fi
92
93	name="${MSG_name#MSG_}"
94	if [ "$name" = "${MSG_name}" ]; then continue; fi
95
96	msg_number="${number#$msg_long}"
97	if [ "$msg_number" = "$number" ]; then continue; fi
98	msg_number="${msg_number%$close_paren}"
99
100	eval $MSG_name=$msg_number
101	eval MSGNUM_$msg_number=\$MSG_name
102	# eval echo \$$MSG_name \$MSGNUM_$msg_number
103done
104
105last_msg_number="$msg_number"
106
107# Read message definition file and set up map of numbers to strings
108
109exec <&3 3<&-
110
111name=
112msg=
113while
114	IFS=
115	read -r line
116do
117	if [ -z "$name" ]; then
118		IFS=" 	"
119		set -- $line
120		if [ "$1" != message ]; then continue; fi
121		name="$2"
122		eval number=\$MSG_$name
123		if [ -z "$number" ]; then
124			error "unknown message \"$name\""
125			$IGNORE_MISSING_TRANSLATIONS || rval=1
126			number=unknown
127		fi
128		l=${line#*$open_brace}
129		if [ "$l" = "$line" ]; then continue; fi
130		line="{$l"
131	fi
132	if [ -z "$msg" ]; then
133		l=${line#$open_brace}
134		if [ "$l" = "$line" ]; then continue; fi
135		msg="$line"
136	else
137		msg="$msg$nl$line"
138	fi
139	m=${msg%$close_brace}
140	if [ "$m" = "$msg" ]; then
141		# Allow <tab>*/* comment */ (eg XXX translate)
142		m=${msg%%$close_brace*$slash[*]*[*]$slash}
143		if [ "$m" = "$msg" ]; then continue; fi
144	fi
145	# We need the %b to expand the \n that exist in the message file
146	msg=$(printf %bz "${m#$open_brace}")
147	msg=${msg%z}
148	eval old=\"\$MSGTEXT_$number\"
149	if [ -n "$old" ] &&  [ "$number" != unknown ]; then
150		error "Two translations for message \"$name\""
151		$IGNORE_MISSING_TRANSLATIONS || rval=1
152	fi
153	eval MSGTEXT_$number=\"\${msg}\"
154	# echo $number $msg
155	sv_name="$name"
156	sv_msg="$msg"
157	name=
158	msg=
159	if ! $count_fmtargs && [ -z "$fmt_count" ]; then continue; fi
160
161	IFS=%
162	set -- $sv_msg
163
164	# For our purposes, empty messages are the same as words without %
165	if [ $# -eq 0 ]; then set -- x; fi
166
167	if $count_fmtargs; then
168		echo $number $#
169		continue
170	fi
171	eval count=\${count_$number:-unknown}
172	if [ "$count" -ne $# ]; then
173		error "Wrong number of format specifiers in \"$sv_name\", got $#, expected $count"
174		$IGNORE_MISSING_TRANSLATIONS || rval=1
175	fi
176done
177unset IFS
178
179if $count_fmtargs; then exit $rval; fi
180
181# Output the total number of messages and the offset of each in the file.
182# Use ascii numbers because generating target-ordered binary numbers
183# is just a smidgen tricky in the shell.
184
185offset=$(( 8 + $last_msg_number * 8 + 8 ))
186printf 'MSGTXTS\0%-7d\0' $last_msg_number
187
188msgnum=0
189while
190	msgnum=$(( $msgnum + 1 ))
191	[ "$msgnum" -le "$last_msg_number" ]
192do
193	eval msg=\${MSGTEXT_$msgnum}
194	if [ -z "$msg" ]; then
195		eval error "No translation for message \$MSGNUM_$msgnum"
196		printf '%-7d\0' 0
197		$IGNORE_MISSING_TRANSLATIONS || rval=1
198		continue
199	fi
200	printf '%-7d\0' $offset
201	offset=$(( $offset + ${#msg} + 1 ))
202done
203
204# Finally output and null terminate the messages.
205
206msgnum=0
207while
208	msgnum=$(( $msgnum + 1 ))
209	[ "$msgnum" -le "$last_msg_number" ]
210do
211	eval msg=\${MSGTEXT_$msgnum}
212	if [ -z "$msg" ]; then continue; fi
213	printf '%s\0' "$msg"
214done
215
216exit $rval
217