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