1 #!/usr/bin/python 2 3 import sys 4 import gettext 5 import re 6 7 # List of supported languages 8 languages = sys.argv[1:] 9 10 # Escape special characters in C strings 11 def 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) 39 def 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 104 def 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. 134 translations = [("en", gettext.NullTranslations())] 135 for 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: 144 reLibintl_h = re.compile (r'#\s*include\s*<libintl.h>') 145 reDESC = re.compile (r'(\s*DRI_CONF_DESC\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$') 146 reDESC_BEGIN = re.compile (r'(\s*DRI_CONF_DESC_BEGIN\s*\(\s*)([a-z]+)(\s*,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$') 147 reENUM = re.compile (r'(\s*DRI_CONF_ENUM\s*\([^,]+,\s*)(gettext\s*\(\s*")(.*)("\s*\))(\s*\)[ \t]*\\?)$') 148 reDESC_END = re.compile (r'\s*DRI_CONF_DESC_END') 149 150 # Print a header 151 print \ 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. 158 template = file ("t_options.h", "r") 159 descMatches = [] 160 for 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 189 if len(descMatches) > 0: 190 sys.stderr.write ("Warning: unterminated description at end of file.\n") 191 expandMatches (descMatches, translations) 192