4 # gen_xmlpool.py /path/to/t_option.h localedir lang lang lang ...
6 # For each given language, this script expects to find a .mo file at
7 # `{localedir}/{language}/LC_MESSAGES/options.mo`.
10 from __future__
import print_function
18 parser
= argparse
.ArgumentParser()
19 parser
.add_argument('template')
20 parser
.add_argument('localedir')
21 parser
.add_argument('languages', nargs
='*')
22 args
= parser
.parse_args()
24 if sys
.version_info
< (3, 0):
25 gettext_method
= 'ugettext'
27 gettext_method
= 'gettext'
29 # Escape special characters in C strings
30 def escapeCString (s
):
31 escapeSeqs
= {'\a' : '\\a', '\b' : '\\b', '\f' : '\\f', '\n' : '\\n',
32 '\r' : '\\r', '\t' : '\\t', '\v' : '\\v', '\\' : '\\\\'}
33 # " -> '' is a hack. Quotes (") aren't possible in XML attributes.
34 # Better use Unicode characters for typographic quotes in option
35 # descriptions and translations.
39 # Special case: escape double quote with \u201c or \u201d, depending
40 # on whether it's an open or close quote. This is needed because plain
41 # double quotes are not possible in XML attributes.
43 if i
== len(s
)-1 or s
[i
+1].isspace():
50 elif s
[i
] in escapeSeqs
:
51 r
= r
+ escapeSeqs
[s
[i
]]
57 # Expand escape sequences in C strings (needed for gettext lookup)
58 def expandCString (s
):
59 escapeSeqs
= {'a' : '\a', 'b' : '\b', 'f' : '\f', 'n' : '\n',
60 'r' : '\r', 't' : '\t', 'v' : '\v',
61 '"' : '"', '\\' : '\\'}
76 if (s
[i
] >= '0' and s
[i
] <= '9') or \
77 (s
[i
] >= 'a' and s
[i
] <= 'f') or \
78 (s
[i
] >= 'A' and s
[i
] <= 'F'):
79 num
= num
* 16 + int(s
[i
],16)
88 if s
[i
] >= '0' and s
[i
] <= '7':
89 num
= num
* 8 + int(s
[i
],8)
98 if s
[i
] in escapeSeqs
:
99 r
= r
+ escapeSeqs
[s
[i
]]
101 elif s
[i
] >= '0' and s
[i
] <= '7':
108 elif s
[i
] == 'x' or s
[i
] == 'X':
118 # Expand matches. The first match is always a DESC or DESC_BEGIN match.
119 # Subsequent matches are ENUM matches.
121 # DESC, DESC_BEGIN format: \1 \2=<lang> \3 \4=gettext(" \5=<text> \6=") \7
122 # ENUM format: \1 \2=gettext(" \3=<text> \4=") \5
123 def expandMatches (matches
, translations
, end
=None):
124 assert len(matches
) > 0
125 nTranslations
= len(translations
)
127 # Expand the description+enums for all translations
128 for lang
,trans
in translations
:
130 # Make sure that all but the last line of a simple description
131 # are extended with a backslash.
133 if len(matches
) == 1 and i
< len(translations
) and \
134 not matches
[0].expand (r
'\7').endswith('\\'):
136 text
= escapeCString (getattr(trans
, gettext_method
) (expandCString (
137 matches
[0].expand (r
'\5'))))
138 text
= (matches
[0].expand (r
'\1' + lang
+ r
'\3"' + text
+ r
'"\7') + suffix
)
140 # In Python 2, stdout expects encoded byte strings, or else it will
141 # encode them with the ascii 'codec'
142 if sys
.version_info
.major
== 2:
143 text
= text
.encode('utf-8')
147 # Expand any subsequent enum lines
148 for match
in matches
[1:]:
149 text
= escapeCString (getattr(trans
, gettext_method
) (expandCString (
150 match
.expand (r
'\3'))))
151 text
= match
.expand (r
'\1"' + text
+ r
'"\5')
153 # In Python 2, stdout expects encoded byte strings, or else it will
154 # encode them with the ascii 'codec'
155 if sys
.version_info
.major
== 2:
156 text
= text
.encode('utf-8')
160 # Expand description end
164 # Compile a list of translation classes to all supported languages.
165 # The first translation is always a NullTranslations.
166 translations
= [("en", gettext
.NullTranslations())]
167 for lang
in args
.languages
:
169 trans
= gettext
.translation ("options", args
.localedir
, [lang
])
171 sys
.stderr
.write ("Warning: language '%s' not found.\n" % lang
)
173 translations
.append ((lang
, trans
))
175 # Regular expressions:
176 reLibintl_h
= re
.compile (r
'#\s*include\s*<libintl.h>')
177 reDESC
= re
.compile (r
'(\s*DRI_CONF_DESC\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
178 reDESC_BEGIN
= re
.compile (r
'(\s*DRI_CONF_DESC_BEGIN\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
179 reENUM
= re
.compile (r
'(\s*DRI_CONF_ENUM\s*\([^,]+,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
180 reDESC_END
= re
.compile (r
'\s*DRI_CONF_DESC_END')
183 print("/***********************************************************************\n" \
184 " *** THIS FILE IS GENERATED AUTOMATICALLY. DON'T EDIT! ***\n" \
185 " ***********************************************************************/")
187 # Process the options template and generate options.h with all
189 template
= io
.open (args
.template
, mode
="rt", encoding
='utf-8')
191 for line
in template
:
192 if len(descMatches
) > 0:
193 matchENUM
= reENUM
.match (line
)
194 matchDESC_END
= reDESC_END
.match (line
)
196 descMatches
.append (matchENUM
)
198 expandMatches (descMatches
, translations
, line
)
202 "Warning: unexpected line inside description dropped:\n%s\n" \
205 if reLibintl_h
.search (line
):
206 # Ignore (comment out) #include <libintl.h>
207 print("/* %s * commented out by gen_xmlpool.py */" % line
)
209 matchDESC
= reDESC
.match (line
)
210 matchDESC_BEGIN
= reDESC_BEGIN
.match (line
)
212 assert len(descMatches
) == 0
213 expandMatches ([matchDESC
], translations
)
214 elif matchDESC_BEGIN
:
215 assert len(descMatches
) == 0
216 descMatches
= [matchDESC_BEGIN
]
218 # In Python 2, stdout expects encoded byte strings, or else it will
219 # encode them with the ascii 'codec'
220 if sys
.version_info
.major
== 2:
221 line
= line
.encode('utf-8')
227 if len(descMatches
) > 0:
228 sys
.stderr
.write ("Warning: unterminated description at end of file.\n")
229 expandMatches (descMatches
, translations
)