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