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