1#!/usr/bin/ksh93
2export PATH=/usr/ast/bin:/usr/xpg6/bin:/usr/xpg4/bin:/usr/bin:${PATH}
3builtin date
4########################################################################
5#                                                                      #
6#               This software is part of the ast package               #
7#          Copyright (c) 2000-2011 AT&T Intellectual Property          #
8#                      and is licensed under the                       #
9#                 Eclipse Public License, Version 1.0                  #
10#                    by AT&T Intellectual Property                     #
11#                                                                      #
12#                A copy of the License is available at                 #
13#          http://www.eclipse.org/org/documents/epl-v10.html           #
14#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
15#                                                                      #
16#              Information and Software Systems Research               #
17#                            AT&T Research                             #
18#                           Florham Park NJ                            #
19#                                                                      #
20#                 Glenn Fowler <gsf@research.att.com>                  #
21#                                                                      #
22########################################################################
23: C language message catalog compiler
24
25# NOTE: all variable names match __*__ to avoid clash with msgcpp def vars
26
27__command__=msgcc
28integer __similar__=30
29
30case `(getopts '[-][123:xyz]' opt --xyz; echo 0$opt) 2>/dev/null` in
310123)	ARGV0="-a $__command__"
32	USAGE=$'
33[-?
34@(#)$Id: msgcc (AT&T Labs Research) 2010-10-20 $
35]
36'$USAGE_LICENSE$'
37[+NAME?msgcc - C language message catalog compiler]
38[+DESCRIPTION?\bmsgcc\b is a C language message catalog compiler. It accepts
39	\bcc\b(1) style options and arguments. A \bmsgcpp\b(1) \b.mso\b file
40	is generated for each input \b.c\b file. If the \b-c\b option is not
41	specified then a \bgencat\b(1) format \b.msg\b file is generated from
42	the input \b.mso\b and \b.msg\b files. If \b-c\b is not specified then
43	a \b.msg\b suffix is appended to the \b-o\b \afile\a if it doesn\'t
44	already have a suffix. The default output is \ba.out.msg\b if \b-c\b
45	and \b-o\b are not specified.]
46[+?If \b-M-new\b is not specified then messages are merged with those in the
47	pre-existing \b-o\b file.]
48[M?Set a \bmsgcc\b specific \aoption\a. \aoption\a may be:]:[-option]{
49	[+mkmsgs?The \b-o\b file is assumed to be in \bmkmsgs\b(1) format.]
50	[+new?Create a new \b-o\b file.]
51	[+preserve?Messages in the \b-o\b file that are not in new
52		\b.msg\b file arguments are preserved. The default is to
53		either reuse the message numbers with new message text that
54		is similar to the old or to delete the message text, leaving
55		an unused message number.]
56	[+set=\anumber\a?Set the message set number to \anumber\a. The default
57		is \b1\b.]
58	[+similar=\anumber\a?The message text similarity measure threshold.
59		The similarity measure between \aold\a and \anew\a message
60		text is 100*(2*gzip(\aold\a+\anew\a)/(gzip(\aold\a)+gzip(\anew\a))-1),
61		where gzip(\ax\a) is the size of text \ax\a when compressed by
62		\bgzip\b(1). The default threshold is '$__similar__$'. A
63		threshold of \b0\b turns off message replacement, but unused
64		old messages are still deleted. Use \b-M-preserve\b to preserve
65		all old messages.]
66	[+verbose?Trace similar message replacements on the standard error.]
67}
68
69file ...
70
71[+SEE ALSO?\bcc\b(1), \bcpp\b(1), \bgencat\b(1), \bmsggen\b(1),
72	\bmsgcpp\b(1), \bmsgcvt\b(1)]
73'
74	;;
75*)	ARGV0=""
76	USAGE="M:[-option] [ cc-options ] file ..."
77	;;
78esac
79
80usage()
81{
82	OPTIND=0
83	getopts $ARGV0 "$USAGE" OPT '-?'
84	exit 2
85}
86
87keys()
88{
89	$1 --??keys -- 2>&1 | grep '^".*"$'
90}
91
92typeset -A __index__
93typeset __keep__ __text__ __drop__ __oz__ __nz__ __z__ __hit__ __hit_i__
94typeset __compile__ __debug__ __mkmsgs__ __preprocess__
95typeset __merge__=1 __preserve__ __verbose__
96integer __i__=0 __args__=0 __code__=0 __files__=0 __max__=0 __num__=0 __skip__=0
97integer __set__=1 __sources__=0 __cmds__=0 __ndrop__=0 __new__=0 __old__=0
98__out__=a.out.msg
99__OUT__=
100
101case " $* " in
102*" --"*|*" -?"*)
103	while	getopts $ARGV0 "$USAGE" OPT
104	do	case $OPT in
105		*)	break ;;
106		esac
107	done
108	;;
109esac
110while	:
111do	case $# in
112	0)	break ;;
113	esac
114	__arg__=$1
115	case $__arg__ in
116	-c)	__compile__=1
117		;;
118	-[DIU]*)__argv__[__args__]=$__arg__
119		(( __args__++ ))
120		;;
121	-E)	__preprocess__=1
122		;;
123	-M-debug)
124		__debug__=1
125		;;
126	-M-mkmsgs)
127		__mkmsgs__=1
128		;;
129	-M-new)	__merge__=
130		;;
131	-M-perserve)
132		__preserve__=1
133		;;
134	-M-set=*)
135		__set__=$(msggen -s ${__arg__#*=}.1)
136		;;
137	-M-similar=*)
138		__similar__=${__arg__#*=}
139		;;
140	-M-verbose)
141		__verbose__=1
142		;;
143	-o)	case $# in
144		1)	print -u2 $"$__command__: output argument expected"
145			exit 1
146			;;
147		esac
148		shift
149		__out__=${1%.*}.msg
150		__OUT__=$1
151		;;
152	[-+]*|*.[aAlLsS]*)
153		;;
154	*.[cCiI]*|*.[oO]*)
155		case $__arg__ in
156		*.[oO]*);;
157		*)	__srcv__[__files__]=$__arg__
158			(( __sources__++ ))
159			;;
160		esac
161		__arg__=${__arg__##*/}
162		__arg__=${__arg__%.*}.mso
163		__objv__[__files__]=$__arg__
164		(( __files__++ ))
165		;;
166	*.ms[go])
167		__objv__[__files__]=$__arg__
168		(( __files__++ ))
169		;;
170	*)	__cmdv__[__cmds__]=$__arg__
171		(( __cmds__++ ))
172		;;
173	esac
174	shift
175done
176__arg__=${__out__##*/}
177__arg__=${__arg__%.msg}
178if	[[ -x $__arg__ ]]
179then	__cmdv__[__cmds__]=$__arg__
180	(( __cmds__++ ))
181fi
182
183# generate the .mso files
184
185if	[[ $__OUT__ && $__compile__ ]]
186then	__objv__[0]=$__OUT__
187fi
188
189if	(( __sources__ ))
190then	for (( __i__=0; __i__<=__files__; __i__++ ))
191	do	if	[[ ${__srcv__[__i__]} ]]
192		then	if	(( __sources__ > 1 ))
193			then	print "${__srcv__[__i__]}:"
194			fi
195			if	[[ $__preprocess__ ]]
196			then	msgcpp "${__argv__[@]}" "${__srcv__[__i__]}"
197			else	msgcpp "${__argv__[@]}" "${__srcv__[__i__]}" > "${__objv__[__i__]}"
198			fi
199		fi
200	done
201fi
202
203# combine the .mso and .msg files
204
205if	[[ ! $__compile__ && ! $__preprocess__ ]]
206then	if	[[ $__merge__ && -r $__out__ ]]
207	then	__tmp__=$__out__.tmp
208		trap '__code__=$?; rm -f ${__tmp__}*; exit $__code__' 0 1 2
209		while	read -r __line__
210		do	if	(( $__skip__ ))
211			then	if	[[ $__line__ == '%}'* ]]
212				then	__skip__=0
213				fi
214				continue
215			fi
216			if	[[ $__mkmsgs__ && $__line__ == '%{'* ]]
217			then	__skip__=1
218				continue
219			fi
220			if	[[ $__mkmsgs__ ]]
221			then	if	[[ $__line__ == '%#'*';;'* ]]
222				then	__line__=${__line__#'%#'}
223					__num__=${__line__%';;'*}
224					read -r __line__
225				elif	[[ $__line__ == %* ]]
226				then	continue
227				else	print -u2 $"$__command__: unrecognized line=$__line__"
228					__code__=1
229				fi
230			else	case $__line__ in
231				+([0-9])' '*)
232					__num__=${__line__%%' '*}
233					__line__=${__line__#*'"'}
234					__line__=${__line__%'"'}
235					;;
236				*)	continue
237					;;
238				esac
239			fi
240			__index__["$__line__"]=$__num__
241			__text__[$__num__]=$__line__
242			if	(( __max__ < __num__ ))
243			then	(( __max__=__num__ ))
244			fi
245		done < $__out__
246		(( __new__=__max__+1 ))
247	else	__tmp__=$__out__
248		(( __new__=1 ))
249	fi
250	if	(( __code__ ))
251	then	exit $__code__
252	fi
253	exec 1>$__tmp__ 9>&1
254	print -r -- '$'" ${__out__%.msg} message catalog"
255	print -r -- '$translation'" $__command__ $(date +%Y-%m-%d)"
256	print -r -- '$set'" $__set__"
257	print -r -- '$quote "'
258	sort -u "${__objv__[@]}" | {
259		__raw__=
260		while	read -r __line__
261		do	__op__=${__line__%% *}
262			__line__=${__line__#* }
263			case $__op__ in
264			cmd)	__a1__=${__line__%% *}
265				case $__a1__ in
266				dot_cmd)	__a1__=. ;;
267				esac
268				keys $__a1__
269				;;
270			def)	__a1__=${__line__%% *}
271				__a2__=${__line__#* }
272				eval $__a1__='$'__a2__
273				;;
274			str)	print -r -- "$__line__"
275				;;
276			raw)	__raw__=$__raw__$'\n'$__line__
277				;;
278			var)	__a1__=${__line__%% *}
279				__a2__=${__line__#* }
280				case $__a1__ in
281				[[:digit:]]*)
282					eval __v__='$'$__a2__
283					__v__='"'${__v__:__a1__+1}
284					;;
285				*)	eval __v__='$'$__a1__
286					;;
287				esac
288				if	[[ $__v__ == '"'*'"' ]]
289				then	print -r -- "$__v__"
290				fi
291				;;
292			[[:digit:]]*)
293				[[ $__preserve__ ]] && print -r -- "$__line__"
294				;;
295			'$')	print -r -u9 $__op__ include $__line__
296				;;
297			esac
298		done
299		for (( __i__=0; __i__ < __cmds__; __i__++ ))
300		do	keys ${__cmdv__[__i__]}
301		done
302		[[ $__raw__ ]] && print -r "${__raw__#?}" | sed -e 's/^"//' -e 's/"$//' -e 's/\\/&&/g' -e 's/"/\\"/g' -e 's/.*/$RAW$"&"/'
303	} | {
304		__num__=1
305		while	read -r __line__
306		do	case $__line__ in
307			'$RAW$'*)
308				;;
309			'$'[\ \	]*)
310				print -r -- "$__line__"
311				continue
312				;;
313			'$'*|*"@(#)"*|*"<"*([[:word:] .-])"@"*([[:word:] .-])">"*([ 	])'"'|"http://"*)
314				continue
315				;;
316			*[[:alpha:]][[:alpha:]]*)
317				;;
318			*)	continue
319				;;
320			esac
321			__line__=${__line__#*'"'}
322			__line__=${__line__%'"'}
323			if	[[ $__line__ ]]
324			then	if	[[ ${__index__["$__line__"]} ]]
325				then	if [[ ! $__preserve__ ]]
326					then	__num__=${__index__["$__line__"]}
327						__keep__[$__num__]=1
328					fi
329				else	while	 [[ ${__text__[$__num__]} ]]
330					do	(( __num__++ ))
331					done
332					if	(( __max__ < __num__ ))
333					then	(( __max__=__num__ ))
334					fi
335					if	[[ ! $__preserve__ ]]
336					then	 __keep__[$__num__]=1
337					fi
338					__text__[$__num__]=$__line__
339					__index__["$__line__"]=$__num__
340					(( __num__++ ))
341				fi
342			fi
343		done
344		if	(( __max__ < __num__ ))
345		then	(( __max__=__num__ ))
346		fi
347		if [[ $__debug__ ]]
348		then	for (( __num__=1; __num__<=__max__; __num__++ ))
349			do	if	[[ ${__text__[$__num__]} ]]
350				then	if	(( __num__ > __new__ ))
351					then	if	[[ ! ${__keep__[$__num__]} ]]
352						then	print -r -u2 -- $__num__ HUH '"'"${__text__[$__num__]}"'"'
353						else	print -r -u2 -- $__num__ NEW '"'"${__text__[$__num__]}"'"'
354						fi
355					elif	[[ ${__keep__[$__num__]} ]]
356					then	print -r -u2 -- $__num__ OLD '"'"${__text__[$__num__]}"'"'
357					else	print -r -u2 -- $__num__ XXX '"'"${__text__[$__num__]}"'"'
358					fi
359				fi
360			done
361			exit 0
362		fi
363		# check for replacements
364		if	[[ ! $__preserve__ ]]
365		then	for (( __num__=1; __num__<__new__; __num__++ ))
366			do	if	[[ ${__text__[$__num__]} && ! ${__keep__[$__num__]} ]]
367				then	(( __ndrop__++ ))
368					__drop__[__ndrop__]=$__num__
369				fi
370			done
371			[[ $__verbose__ ]] && print -u2 $__command__: old:1-$((__new__-1)) new:$__new__-$__max__ drop $__ndrop__ add $((__max__-__new__+1))
372			if	(( __ndrop__ ))
373			then	for (( __i__=1; __i__<=__ndrop__; __i__++ ))
374				do	(( __old__=${__drop__[$__i__]} ))
375					__oz__[__i__]=$(print -r -- "\"${__text__[$__old__]}\"" | gzip | wc -c)
376				done
377				for (( __num__=__new__; __num__<=__max__; __num__++ ))
378				do	[[ ${__text__[$__num__]} ]] || continue
379					__nz__=$(print -r -- "\"${__text__[$__num__]}\"" | gzip | wc -c)
380					__hit__=0
381					(( __bz__=__similar__ ))
382					for (( __i__=1; __i__<=__ndrop__; __i__++ ))
383					do	if	(( __old__=${__drop__[$__i__]} ))
384						then	__z__=$(print -r -- "\"${__text__[$__old__]}\"""\"${__text__[$__num__]}\"" | gzip | wc -c)
385							(( __z__ = (__z__ * 200 / (${__oz__[__i__]} + $__nz__)) - 100 ))
386							if	(( __z__ < __bz__ ))
387							then	(( __bz__=__z__ ))
388								(( __hit__=__old__ ))
389								(( __hit_i__=__i__ ))
390							fi
391						fi
392					done
393					if	(( __hit__ ))
394					then	[[ $__verbose__ ]] && print -u2 $__command__: $__hit__ $__num__ $__bz__
395						__text__[$__hit__]=${__text__[$__num__]}
396						__keep__[$__hit__]=1
397						__drop__[$__hit_i__]=0
398						__text__[$__num__]=
399						__keep__[$__num__]=
400					fi
401				done
402			fi
403		fi
404		# final output
405		for (( __num__=1; __num__<=__max__; __num__++ ))
406		do	if	[[ ${__text__[$__num__]} && ( $__preserve__ || ${__keep__[$__num__]} ) ]]
407			then	print -r -- $__num__ "\"${__text__[$__num__]}\""
408			fi
409		done
410	}
411	if [[ $__tmp__ != $__out__ ]]
412	then	grep -v '^\$' $__tmp__ > ${__tmp__}n
413		[[ -f $__out__ ]] && grep -v '^\$' $__out__ > ${__tmp__}o
414		cmp -s ${__tmp__}n ${__tmp__}o || {
415			[[ -f $__out__ ]] && mv $__out__ $__out__.old
416			mv $__tmp__ $__out__
417		}
418	fi
419fi
420exit $__code__
421