v3d: Include commands to run the BCL and RCL in CLIF dumps.
[mesa.git] / src / util / xmlpool / gen_xmlpool.py
1
2 #
3 # Usage:
4 # gen_xmlpool.py /path/to/t_option.h localedir lang lang lang ...
5 #
6 # For each given language, this script expects to find a .mo file at
7 # `{localedir}/{language}/LC_MESSAGES/options.mo`.
8 #
9
10 from __future__ import print_function
11
12 import sys
13 import gettext
14 import re
15
16 # Path to t_options.h
17 template_header_path = sys.argv[1]
18
19 localedir = sys.argv[2]
20
21 # List of supported languages
22 languages = sys.argv[3:]
23
24 # Escape special characters in C strings
25 def escapeCString (s):
26 escapeSeqs = {'\a' : '\\a', '\b' : '\\b', '\f' : '\\f', '\n' : '\\n',
27 '\r' : '\\r', '\t' : '\\t', '\v' : '\\v', '\\' : '\\\\'}
28 # " -> '' is a hack. Quotes (") aren't possible in XML attributes.
29 # Better use Unicode characters for typographic quotes in option
30 # descriptions and translations.
31 i = 0
32 r = ''
33 while i < len(s):
34 # Special case: escape double quote with \u201c or \u201d, depending
35 # on whether it's an open or close quote. This is needed because plain
36 # double quotes are not possible in XML attributes.
37 if s[i] == '"':
38 if i == len(s)-1 or s[i+1].isspace():
39 # close quote
40 q = u'\u201c'
41 else:
42 # open quote
43 q = u'\u201d'
44 r = r + q
45 elif s[i] in escapeSeqs:
46 r = r + escapeSeqs[s[i]]
47 else:
48 r = r + s[i]
49 i = i + 1
50 return r
51
52 # Expand escape sequences in C strings (needed for gettext lookup)
53 def expandCString (s):
54 escapeSeqs = {'a' : '\a', 'b' : '\b', 'f' : '\f', 'n' : '\n',
55 'r' : '\r', 't' : '\t', 'v' : '\v',
56 '"' : '"', '\\' : '\\'}
57 i = 0
58 escape = False
59 hexa = False
60 octa = False
61 num = 0
62 digits = 0
63 r = ''
64 while i < len(s):
65 if not escape:
66 if s[i] == '\\':
67 escape = True
68 else:
69 r = r + s[i]
70 elif hexa:
71 if (s[i] >= '0' and s[i] <= '9') or \
72 (s[i] >= 'a' and s[i] <= 'f') or \
73 (s[i] >= 'A' and s[i] <= 'F'):
74 num = num * 16 + int(s[i],16)
75 digits = digits + 1
76 else:
77 digits = 2
78 if digits >= 2:
79 hexa = False
80 escape = False
81 r = r + chr(num)
82 elif octa:
83 if s[i] >= '0' and s[i] <= '7':
84 num = num * 8 + int(s[i],8)
85 digits = digits + 1
86 else:
87 digits = 3
88 if digits >= 3:
89 octa = False
90 escape = False
91 r = r + chr(num)
92 else:
93 if s[i] in escapeSeqs:
94 r = r + escapeSeqs[s[i]]
95 escape = False
96 elif s[i] >= '0' and s[i] <= '7':
97 octa = True
98 num = int(s[i],8)
99 if num <= 3:
100 digits = 1
101 else:
102 digits = 2
103 elif s[i] == 'x' or s[i] == 'X':
104 hexa = True
105 num = 0
106 digits = 0
107 else:
108 r = r + s[i]
109 escape = False
110 i = i + 1
111 return r
112
113 # Expand matches. The first match is always a DESC or DESC_BEGIN match.
114 # Subsequent matches are ENUM matches.
115 #
116 # DESC, DESC_BEGIN format: \1 \2=<lang> \3 \4=gettext(" \5=<text> \6=") \7
117 # ENUM format: \1 \2=gettext(" \3=<text> \4=") \5
118 def expandMatches (matches, translations, end=None):
119 assert len(matches) > 0
120 nTranslations = len(translations)
121 i = 0
122 # Expand the description+enums for all translations
123 for lang,trans in translations:
124 i = i + 1
125 # Make sure that all but the last line of a simple description
126 # are extended with a backslash.
127 suffix = ''
128 if len(matches) == 1 and i < len(translations) and \
129 not matches[0].expand (r'\7').endswith('\\'):
130 suffix = ' \\'
131 # Expand the description line. Need to use ugettext in order to allow
132 # non-ascii unicode chars in the original English descriptions.
133 text = escapeCString (trans.ugettext (unicode (expandCString (
134 matches[0].expand (r'\5')), "utf-8"))).encode("utf-8")
135 print(matches[0].expand (r'\1' + lang + r'\3"' + text + r'"\7') + suffix)
136 # Expand any subsequent enum lines
137 for match in matches[1:]:
138 text = escapeCString (trans.ugettext (unicode (expandCString (
139 match.expand (r'\3')), "utf-8"))).encode("utf-8")
140 print(match.expand (r'\1"' + text + r'"\5'))
141
142 # Expand description end
143 if end:
144 print(end, end='')
145
146 # Compile a list of translation classes to all supported languages.
147 # The first translation is always a NullTranslations.
148 translations = [("en", gettext.NullTranslations())]
149 for lang in languages:
150 try:
151 trans = gettext.translation ("options", localedir, [lang])
152 except IOError:
153 sys.stderr.write ("Warning: language '%s' not found.\n" % lang)
154 continue
155 translations.append ((lang, trans))
156
157 # Regular expressions:
158 reLibintl_h = re.compile (r'#\s*include\s*<libintl.h>')
159 reDESC = re.compile (r'(\s*DRI_CONF_DESC\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
160 reDESC_BEGIN = re.compile (r'(\s*DRI_CONF_DESC_BEGIN\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
161 reENUM = re.compile (r'(\s*DRI_CONF_ENUM\s*\([^,]+,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$')
162 reDESC_END = re.compile (r'\s*DRI_CONF_DESC_END')
163
164 # Print a header
165 print("/***********************************************************************\n" \
166 " *** THIS FILE IS GENERATED AUTOMATICALLY. DON'T EDIT! ***\n" \
167 " ***********************************************************************/")
168
169 # Process the options template and generate options.h with all
170 # translations.
171 template = file (template_header_path, "r")
172 descMatches = []
173 for line in template:
174 if len(descMatches) > 0:
175 matchENUM = reENUM .match (line)
176 matchDESC_END = reDESC_END.match (line)
177 if matchENUM:
178 descMatches.append (matchENUM)
179 elif matchDESC_END:
180 expandMatches (descMatches, translations, line)
181 descMatches = []
182 else:
183 sys.stderr.write (
184 "Warning: unexpected line inside description dropped:\n%s\n" \
185 % line)
186 continue
187 if reLibintl_h.search (line):
188 # Ignore (comment out) #include <libintl.h>
189 print("/* %s * commented out by gen_xmlpool.py */" % line)
190 continue
191 matchDESC = reDESC .match (line)
192 matchDESC_BEGIN = reDESC_BEGIN.match (line)
193 if matchDESC:
194 assert len(descMatches) == 0
195 expandMatches ([matchDESC], translations)
196 elif matchDESC_BEGIN:
197 assert len(descMatches) == 0
198 descMatches = [matchDESC_BEGIN]
199 else:
200 print(line, end='')
201
202 if len(descMatches) > 0:
203 sys.stderr.write ("Warning: unterminated description at end of file.\n")
204 expandMatches (descMatches, translations)