Home | History | Annotate | Download | only in xmlpool
      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