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