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