1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#           Copyright (c) 2000-2007 AT&T Knowledge Ventures            #
5#                      and is licensed under the                       #
6#                  Common Public License, Version 1.0                  #
7#                      by AT&T Knowledge Ventures                      #
8#                                                                      #
9#                A copy of the License is available at                 #
10#            http://www.opensource.org/licenses/cpl1.0.txt             #
11#         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         #
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) 2002-09-15 $
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 thresshold.
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 threshhold is '$__similar__$'. A
60		threshhold 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__cmdv__[__cmds__]=${__out__%.msg}
174(( __cmds__++ ))
175
176# generate the .mso files
177
178if	[[ $__OUT__ && $__compile__ ]]
179then	__objv__[0]=$__OUT__
180fi
181
182if	(( __sources__ ))
183then	for (( __i__=0; __i__<=__files__; __i__++ ))
184	do	if	[[ ${__srcv__[__i__]} ]]
185		then	if	(( __sources__ > 1 ))
186			then	print "${__srcv__[__i__]}:"
187			fi
188			if	[[ $__preprocess__ ]]
189			then	msgcpp "${__argv__[@]}" "${__srcv__[__i__]}"
190			else	msgcpp "${__argv__[@]}" "${__srcv__[__i__]}" > "${__objv__[__i__]}"
191			fi
192		fi
193	done
194fi
195
196# combine the .mso and .msg files
197
198if	[[ ! $__compile__ && ! $__preprocess__ ]]
199then	if	[[ $__merge__ && -r $__out__ ]]
200	then	__tmp__=$__out__.tmp
201		trap '__code__=$?; rm -f ${__tmp__}*; exit $__code__' 0 1 2
202		while	read -r __line__
203		do	if	(( $__skip__ ))
204			then	if	[[ $__line__ == '%}'* ]]
205				then	__skip__=0
206				fi
207				continue
208			fi
209			if	[[ $__mkmsgs__ && $__line__ == '%{'* ]]
210			then	__skip__=1
211				continue
212			fi
213			if	[[ $__mkmsgs__ ]]
214			then	if	[[ $__line__ == '%#'*';;'* ]]
215				then	__line__=${__line__#'%#'}
216					__num__=${__line__%';;'*}
217					read -r __line__
218				elif	[[ $__line__ == %* ]]
219				then	continue
220				else	print -u2 $"$__command__: unrecognized line=$__line__"
221					__code__=1
222				fi
223			else	case $__line__ in
224				+([0-9])' '*)
225					__num__=${__line__%%' '*}
226					__line__=${__line__#*'"'}
227					__line__=${__line__%'"'}
228					;;
229				*)	continue
230					;;
231				esac
232			fi
233			__index__["$__line__"]=$__num__
234			__text__[$__num__]=$__line__
235			if	(( __max__ < __num__ ))
236			then	(( __max__=__num__ ))
237			fi
238		done < $__out__
239		(( __new__=__max__+1 ))
240	else	__tmp__=$__out__
241		(( __new__=1 ))
242	fi
243	if	(( __code__ ))
244	then	exit $__code__
245	fi
246	exec 1>$__tmp__ 9>&1
247	print -r -- '$'" ${__out__%.msg} message catalog"
248	print -r -- '$translation'" $__command__ $(date +%Y-%m-%d)"
249	print -r -- '$set'" $__set__"
250	print -r -- '$quote "'
251	sort -u "${__objv__[@]}" | {
252		while	read -r __line__
253		do	__op__=${__line__%% *}
254			__line__=${__line__#* }
255			case $__op__ in
256			cmd)	__a1__=${__line__%% *}
257				case $__a1__ in
258				dot_cmd)	__a1__=. ;;
259				esac
260				keys $__a1__
261				;;
262			def)	__a1__=${__line__%% *}
263				__a2__=${__line__#* }
264				eval $__a1__='$'__a2__
265				;;
266			str)	print -r -- "$__line__"
267				;;
268			var)	__a1__=${__line__%% *}
269				__a2__=${__line__#* }
270				case $__a1__ in
271				[[:digit:]]*)
272					eval __v__='$'$__a2__
273					__v__='"'${__v__:__a1__+1}
274					;;
275				*)	eval __v__='$'$__a1__
276					;;
277				esac
278				if	[[ $__v__ == '"'*'"' ]]
279				then	print -r -- "$__v__"
280				fi
281				;;
282			[[:digit:]]*)
283				[[ $__preserve__ ]] && print -r -- "$__line__"
284				;;
285			'$')	print -r -u9 $__op__ include $__line__
286				;;
287			esac
288		done
289		for (( __i__=0; __i__ < __cmds__; __i__++ ))
290		do	keys ${__cmdv__[__i__]}
291		done
292	} | {
293		__num__=1
294		while	read -r __line__
295		do	case $__line__ in
296			'$'[\ \	]*)
297				print -r -- "$__line__"
298				continue
299				;;
300			'$'*|*"@(#)"*|*"<"*([[:word:] .-])"@"*([[:word:] .-])">"*([ 	])'"'|"http://"*)
301				continue
302				;;
303			*[[:alpha:]][[:alpha:]]*)
304				__line__=${__line__#*'"'}
305				__line__=${__line__%'"'}
306				if	[[ $__line__ ]]
307				then	if	[[ ${__index__["$__line__"]} ]]
308					then	if [[ ! $__preserve__ ]]
309						then	__num__=${__index__["$__line__"]}
310							__keep__[$__num__]=1
311						fi
312					else	while	 [[ ${__text__[$__num__]} ]]
313						do	(( __num__++ ))
314						done
315						if	(( __max__ < __num__ ))
316						then	(( __max__=__num__ ))
317						fi
318						if	[[ ! $__preserve__ ]]
319						then	 __keep__[$__num__]=1
320						fi
321						__text__[$__num__]=$__line__
322						__index__["$__line__"]=$__num__
323						(( __num__++ ))
324					fi
325				fi
326				;;
327			esac
328		done
329		if	(( __max__ < __num__ ))
330		then	(( __max__=__num__ ))
331		fi
332		if [[ $__debug__ ]]
333		then	for (( __num__=1; __num__<=__max__; __num__++ ))
334			do	if	[[ ${__text__[$__num__]} ]]
335				then	if	(( __num__ > __new__ ))
336					then	if	[[ ! ${__keep__[$__num__]} ]]
337						then	print -r -u2 -- $__num__ HUH '"'"${__text__[$__num__]}"'"'
338						else	print -r -u2 -- $__num__ NEW '"'"${__text__[$__num__]}"'"'
339						fi
340					elif	[[ ${__keep__[$__num__]} ]]
341					then	print -r -u2 -- $__num__ OLD '"'"${__text__[$__num__]}"'"'
342					else	print -r -u2 -- $__num__ XXX '"'"${__text__[$__num__]}"'"'
343					fi
344				fi
345			done
346			exit 0
347		fi
348		# check for replacements
349		if	[[ ! $__preserve__ ]]
350		then	for (( __num__=1; __num__<__new__; __num__++ ))
351			do	if	[[ ${__text__[$__num__]} && ! ${__keep__[$__num__]} ]]
352				then	(( __ndrop__++ ))
353					__drop__[__ndrop__]=$__num__
354				fi
355			done
356			[[ $__verbose__ ]] && print -u2 $__command__: old:1-$((__new__-1)) new:$__new__-$__max__ drop $__ndrop__ add $((__max__-__new__+1))
357			if	(( __ndrop__ ))
358			then	for (( __i__=1; __i__<=__ndrop__; __i__++ ))
359				do	(( __old__=${__drop__[$__i__]} ))
360					__oz__[__i__]=$(print -r -- "\"${__text__[$__old__]}\"" | gzip | wc -c)
361				done
362				for (( __num__=__new__; __num__<=__max__; __num__++ ))
363				do	[[ ${__text__[$__num__]} ]] || continue
364					__nz__=$(print -r -- "\"${__text__[$__num__]}\"" | gzip | wc -c)
365					__hit__=0
366					(( __bz__=__similar__ ))
367					for (( __i__=1; __i__<=__ndrop__; __i__++ ))
368					do	if	(( __old__=${__drop__[$__i__]} ))
369						then	__z__=$(print -r -- "\"${__text__[$__old__]}\"""\"${__text__[$__num__]}\"" | gzip | wc -c)
370							(( __z__ = (__z__ * 200 / (${__oz__[__i__]} + $__nz__)) - 100 ))
371							if	(( __z__ < __bz__ ))
372							then	(( __bz__=__z__ ))
373								(( __hit__=__old__ ))
374								(( __hit_i__=__i__ ))
375							fi
376						fi
377					done
378					if	(( __hit__ ))
379					then	[[ $__verbose__ ]] && print -u2 $__command__: $__hit__ $__num__ $__bz__
380						__text__[$__hit__]=${__text__[$__num__]}
381						__keep__[$__hit__]=1
382						__drop__[$__hit_i__]=0
383						__text__[$__num__]=
384						__keep__[$__num__]=
385					fi
386				done
387			fi
388		fi
389		# final output
390		for (( __num__=1; __num__<=__max__; __num__++ ))
391		do	if	[[ ${__text__[$__num__]} && ( $__preserve__ || ${__keep__[$__num__]} ) ]]
392			then	print -r -- $__num__ "\"${__text__[$__num__]}\""
393			fi
394		done
395	}
396	if [[ $__tmp__ != $__out__ ]]
397	then	grep -v '^\$' $__tmp__ > ${__tmp__}n
398		[[ -f $__out__ ]] && grep -v '^\$' $__out__ > ${__tmp__}o
399		cmp -s ${__tmp__}n ${__tmp__}o || {
400			[[ -f $__out__ ]] && mv $__out__ $__out__.old
401			mv $__tmp__ $__out__
402		}
403	fi
404fi
405exit $__code__
406