1#! /bin/sh
2# Wrapper around gettext for programs using the msgid convention.
3# Copyright (C) 1998-2020 Free Software Foundation, Inc.
4
5# Written by Paul Eggert <eggert@twinsun.com>.
6# Revised by Zack Weinberg <zackw@stanford.edu> for no-POTFILES operation.
7
8# This file is part of GCC.
9
10# GCC is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 3, or (at your option)
13# any later version.
14
15# GCC is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19
20# You should have received a copy of the GNU General Public License
21# along with GCC; see the file COPYING3.  If not see
22# <http://www.gnu.org/licenses/>.
23
24BUGURL="https://gcc.gnu.org/bugs/"
25
26# Always operate in the C locale.
27LANG=C
28LANGUAGE=C
29LC_ALL=C
30export LANG LANGUAGE LC_ALL
31
32# Set AWK if environment has not already set it.
33AWK=${AWK-awk}
34
35# The arguments to this wrapper are: the program to execute, the
36# name of the "package", and the path to the source directory.
37
38if [ $# -ne 3 ]
39then	echo "usage: $0 <xgettext> <package> <srcdir>"
40	exit 1
41fi
42
43xgettext=$1
44package=$2
45srcdir=$3
46
47case `$xgettext --version | sed -e 1q | sed -e 's/^\([^0-9]*\)//'` in
48  0.14.[5-9]* | 0.14.[1-9][0-9]* | 0.1[5-9]* | 0.[2-9][0-9]* | [1-9].*) : ;;
49  *) echo "$xgettext is too old.  GNU xgettext 0.14.5 is required"
50     exit 1 ;;
51esac
52
53nl='
54'
55
56set -e
57
58# Create temporary directory for scratch files.
59T=exg$$.d
60mkdir $T
61trap "rm -r $T" 0
62
63pwd=`${PWDCMD-pwd}`
64kopt=$pwd/$T/keyword-options
65kopt2=$pwd/$T/keyword2-options
66emsg=$pwd/$T/emsgids.c
67posr=$pwd/$T/po-sources
68posrcxx=$pwd/$T/po-cxx-sources
69pottmp1=$pwd/$T/tmp1.pot
70pottmp2=$pwd/$T/tmp2.pot
71pottmp3=$pwd/$T/tmp3.pot
72pottmp=$pwd/$T/tmp.pot
73
74# Locate files to scan.  We scan the following directories:
75#  $srcdir
76#  $srcdir/c-family
77#  $srcdir/common
78#  $srcdir/common/config
79#  $srcdir/common/config/*
80#  $srcdir/config
81#  $srcdir/config/*
82#  all subdirectories of $srcdir containing a config-lang.in file, and
83#    all subdirectories of those directories.
84# Within those directories, we examine all .c, .cc, .h, and .def files.
85# However, any files listed in $srcdir/po/EXCLUDE are not examined.
86#
87# Then generate keyword options for xgettext, by scanning for declarations
88# of functions whose parameter names end in "msgid".
89#
90# Finally, generate a source file containing all %e and %n strings from
91# driver specs, so those can be translated too.
92#
93# All in one huge awk script.
94
95echo "scanning for keywords, %e and %n strings..." >&2
96
97( cd $srcdir
98  lang_subdirs=`echo */config-lang.in */*/config-lang.in | sed -e 's|/config-lang\.in||g'`
99  { for dir in "" c-family/ common/ common/config/ common/config/*/ \
100      config/ config/*/ \
101      `find $lang_subdirs -type d -print | fgrep -v .svn | sort | sed -e 's|$|/|'`
102    do  for glob in '*.c' '*.cc' '*.h' '*.def'
103        do  eval echo $dir$glob
104        done
105    done;
106  } | tr ' ' "$nl" | grep -v '\*' |
107  $AWK -v excl=po/EXCLUDES -v posr=$posr -v posrcxx=$posrcxx -v kopt=$kopt -v kopt2=$kopt2 -v emsg=$emsg '
108function keyword_option(line) {
109    paren_index = index(line, "(")
110    name = substr(line, 1, paren_index - 1)
111    sub(/[^0-9A-Z_a-z]*$/, "", name)
112    sub(/[	 ]+PARAMS/, "", name)
113    sub(/[	 ]+VPARAMS/, "", name)
114    sub(/.*[^0-9A-Z_a-z]/, "", name)
115
116    args = substr(line, paren_index)
117    sub(/msgid[,\)].*/, "", args)
118    for (n = 1; sub(/^[^,]*,/, "", args); n++) {
119	continue
120    }
121    format=""
122    if (args ~ /g$/)
123    	format="gcc-internal-format"
124    else if (args ~ /noc$/)
125        format="no-c-format"
126    else if (args ~ /c$/)
127    	format="c-format"
128
129    if (n == 1) { keyword = "--keyword=" name }
130    else {
131       keyword = "--keyword=" name ":" n
132       if (name ~ /_n$/)
133         keyword = keyword "," (n + 1)
134    }
135    if (format) {
136        keyword=keyword "\n--flag=" name ":" n ":" format
137        if (name ~ /_n$/)
138          keyword = keyword "\n--flag=" name ":" (n + 1) ":" format
139    }
140
141    if (! keyword_seen[name]) {
142	if (format == "gcc-internal-format")
143		print keyword > kopt2
144	else
145		print keyword > kopt
146    	keyword_seen[name] = keyword
147    } else if (keyword_seen[name] != keyword) {
148	printf("%s used incompatibly as both %s and %s\n",
149	       name, keyword_seen[name], keyword)
150	exit (1)
151    }
152}
153
154function spec_error_string (line) {
155    if (index(line, "%e") != 0 && index(line, "%n") != 0) return
156    while ((percent_index = index(line, "%e")) != 0 ||
157	   (percent_index = index(line, "%n")) != 0) {
158	line = substr(line, percent_index + 2)
159
160	bracket_index = index(line, "}")
161	newline_index = index(line, "\\n")
162
163	quote_index = index(line, "\"")
164	if (bracket_index == 0 && newline_index == 0) return
165
166	if (bracket_index != 0) {
167	  if (quote_index != 0 && bracket_index > quote_index) return
168	  msgid = substr(line, 1, bracket_index - 1)
169	  line = substr(line, bracket_index + 1)
170	}
171	else if (newline_index != 0) {
172	  if (quote_index != 0 && quote_index > newline_index) return
173	  msgid = substr(line, 1, newline_index - 1)
174	  line = substr(line, newline_index + 1)
175	}
176
177	if (index(msgid, "%") != 0) continue
178
179	if ((newline_index = index(msgid, "\\n")) != 0)
180	  msgid = substr(msgid, 1, newline_index - 1)
181	printf("#line %d \"%s\"\n", lineno, file) > emsg
182	printf("_(\"%s\")\n", msgid) > emsg
183    }
184}
185
186BEGIN {
187  while ((getline < excl) > 0) {
188    if ($0 ~ /^#/ || $0 ~ /^[ 	]*$/)
189      continue
190    excludes[$1] = 1
191  }
192}
193
194{ if (!($0 in excludes)) {
195    if ($0 ~ /.cc$/) {
196      print > posrcxx
197    } else {
198      print > posr
199    }
200    files[NR] = $0
201  }
202}
203
204END {
205    for (f in files) {
206	file = files[f]
207	lineno = 1
208	while (getline < file) {
209	    if (/^(#[ 	]*define[ 	]*)?[A-Za-z_].*\(.*msgid[,\)]/) {
210		keyword_option($0)
211	    } else if (/^(#[   ]*define[       ]*)?[A-Za-z_].*(\(|\(.*,)$/) {
212		name_line = $0
213		while (getline < file) {
214		  lineno++
215		  if (/msgid[,\)]/){
216		    keyword_option(name_line $0)
217		    break
218		  } else if (/,$/) {
219		      name_line = name_line $0
220		      continue
221		  } else break
222		}
223	    } else if (/%e/ || /%n/) {
224		spec_error_string($0)
225	    }
226	    lineno++
227	}
228    }
229    print emsg > posr
230    print "--keyword=__opt_help_text\n--flag=__opt_help_text:1:no-c-format" >> kopt
231}'
232) || exit
233
234echo "scanning option files..." >&2
235
236( cd $srcdir; find . -name '*.opt' -print |
237  $AWK '{
238    file = $1
239    lineno = 1
240    field = 0
241    while (getline < file) {
242	if (/^[ \t]*(;|$)/ || !/^[^ \t]/) {
243	    if (field > 2)
244		printf("__opt_help_text(\"%s\")\n", line)
245	    field = 0
246	} else {
247	    if ((field == 1) && /MissingArgError/) {
248		line = $0
249		sub(".*MissingArgError\\(", "", line)
250		if (line ~ "^{") {
251			sub("^{", "", line)
252			sub("}\\).*", "", line)
253		} else
254			sub("\\).*", "", line)
255		printf("#line %d \"%s\"\n", lineno, file)
256		printf("error(\"%s\")\n", line)
257	    }
258	    if ((field == 1) && /UnknownError/) {
259		line = $0
260		sub(".*UnknownError\\(", "", line)
261		if (line ~ "^{") {
262			sub("^{", "", line)
263			sub("}\\).*", "", line)
264		} else
265			sub("\\).*", "", line)
266		printf("#line %d \"%s\"\n", lineno, file)
267		printf("error(\"%s\")\n", line)
268	    }
269	    if ((field == 1) && /Warn\(/) {
270		line = $0
271		sub(".*Warn\\(", "", line)
272		if (line ~ "^{") {
273			sub("^{", "", line)
274			sub("}\\).*", "", line)
275		} else
276			sub("\\).*", "", line)
277		printf("#line %d \"%s\"\n", lineno, file)
278		printf("warning(0, \"%s\")\n", line)
279	    }
280	    if (field == 2) {
281		line = $0
282		printf("#line %d \"%s\"\n", lineno, file)
283	    } else if (field > 2) {
284		line = line " " $0
285	    }
286	    field++;
287	}
288	lineno++;
289    }
290    if (field > 2)
291	printf("__opt_help_text(\"%s\")\n", line)
292  }') >> $emsg
293
294# Run the xgettext commands, with temporary added as a file to scan.
295echo "running xgettext..." >&2
296$xgettext --default-domain=$package --directory=$srcdir \
297	  --add-comments `cat $kopt` --files-from=$posr \
298	  --copyright-holder="Free Software Foundation, Inc." \
299	  --msgid-bugs-address="$BUGURL" \
300	  --language=c -o $pottmp1
301if test -s $posrcxx; then
302  $xgettext --default-domain=$package --directory=$srcdir \
303	    --add-comments `cat $kopt` --files-from=$posrcxx \
304	    --copyright-holder="Free Software Foundation, Inc." \
305	    --msgid-bugs-address="$BUGURL" \
306	    --language=c++ -o $pottmp2
307else
308  echo > $pottmp2
309fi
310$xgettext --default-domain=$package --directory=$srcdir \
311	  --add-comments --keyword= `cat $kopt2` --files-from=$posr \
312	  --copyright-holder="Free Software Foundation, Inc." \
313	  --msgid-bugs-address="$BUGURL" \
314	  --language=GCC-source -o $pottmp3
315$xgettext --default-domain=$package \
316	  --add-comments $pottmp1 $pottmp2 $pottmp3 \
317	  --copyright-holder="Free Software Foundation, Inc." \
318	  --msgid-bugs-address="$BUGURL" \
319	  --language=PO -o $pottmp
320# Remove local paths from .pot file.
321sed "s:$srcdir/::g;s:$pwd/::g;" <$pottmp >po/$package.pot
322