"""This module allows python programs to use GNU gettext message catalogs.

Modified by Red Hat, Inc for use with anaconda installer.
Original file information follows:

Author: James Henstridge <[email protected]>
(This is loosely based on gettext.pl in the GNU gettext distribution)

The best way to use it is like so:
   import gettext
   gettext.bindtextdomain(PACKAGE, LOCALEDIR)
   gettext.textdomain(PACKAGE)
   _ = gettext.gettext
   print _('Hello World')

where PACKAGE is the domain for this package, and LOCALEDIR is usually
'$prefix/share/locale' where $prefix is the install prefix.

If you have more than one catalog to use, you can directly create catalog
objects.  These objects are created as so:
   import gettext
   cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR)
   _ = cat.gettext
   print _('Hello World')

The catalog object can also be accessed as a dictionary (ie cat['hello']).

There are also some experimental features.  You can add to the catalog, just
as you would with a normal dictionary.  When you are finished, you can call
its save method, which will create a new .mo file containing all the
translations:
   import gettext
   cat = Catalog()
   cat['Hello'] = 'konichiwa'
   cat.save('./tmp.mo')

Once you have written an internationalized program, you can create a .po file
for it with "xgettext --keyword=_ fillename ...".  Then do the translation and
compile it into a .mo file, ready for use with this module.  Note that you
will have to use C style strings (ie. use double quotes) for proper string
extraction.
"""
import os, string, iutil, gzread

prefix = '/usr/local'
localedir = prefix + '/share/locale'

def _expandLang(str):
       langs = [str]
       # remove charset ...
       if '.' in str:
               langs.append(string.split(str, '.')[0])
       # also add 2 character language code ...
       if len(str) > 2:
               langs.append(str[:2])
       return langs

lang = []
for env in 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG':
       if os.environ.has_key(env):
               lang = string.split(os.environ[env], ':')
               lang = map(_expandLang, lang)
               lang = reduce(lambda a, b: a + b, lang)
               break
if 'C' not in lang:
       lang.append('C')

# remove duplicates
i = 0
while i < len(lang):
       j = i + 1
       while j < len(lang):
               if lang[i] == lang[j]:
                       del lang[j]
               else:
                       j = j + 1
       i = i + 1
del i, j

if os.environ.has_key('PY_XGETTEXT'):
       xgettext = os.environ['PY_XGETTEXT']
else:
       xgettext = None

if iutil.getArch() == 'sparc':
       _gettext_byteorder = 'msb'
else:
       _gettext_byteorder = 'lsb'

del os, string, iutil

error = 'gettext.error'

def _lsbStrToInt(str):
       return ord(str[0]) + \
              (ord(str[1]) << 8) + \
              (ord(str[2]) << 16) + \
              (ord(str[3]) << 24)
def _intToLsbStr(int):
       return chr(int         & 0xff) + \
              chr((int >> 8)  & 0xff) + \
              chr((int >> 16) & 0xff) + \
              chr((int >> 24) & 0xff)
def _msbStrToInt(str):
       return ord(str[3]) + \
              (ord(str[2]) << 8) + \
              (ord(str[1]) << 16) + \
              (ord(str[0]) << 24)
def _intToMsbStr(int):
       return chr((int >> 24) & 0xff) + \
              chr((int >> 16) & 0xff) + \
              chr((int >> 8) & 0xff) + \
              chr(int & 0xff)
def _StrToInt(str):
       if _gettext_byteorder == 'msb':
               return _msbStrToInt(str)
       else:
               return _lsbStrToInt(str)
def _intToStr(int):
       if _gettext_byteorder == 'msb':
               return _intToMsbStr(str)
       else:
               return _intToLsbStr(str)

def _getpos(levels = 0):
       """Returns the position in the code where the function was called.
       The function uses some knowledge about python stack frames."""
       import sys
       # get access to the stack frame by generating an exception.
       try:
               raise RuntimeError
       except RuntimeError:
               frame = sys.exc_traceback.tb_frame
       frame = frame.f_back # caller's frame
       while levels > 0:
               frame = frame.f_back
               levels = levels - 1
       return (frame.f_globals['__name__'],
               frame.f_code.co_name,
               frame.f_lineno)

class Catalog:
       def __init__(self, domain=None, localedir=localedir):
               self.domain = domain
               self.localedir = localedir
               self.cat = {}
               if not domain: return
               for self.lang in lang:
                       if self.lang == 'C':
                               return
                       catalog = "%s/%s/LC_MESSAGES/%s.mo" % (
                               localedir, self.lang, domain)
                       try:
                               f = gzread.open(catalog)
                               buffer = f.read()
                               f.close()
                               del f
                               break
                       except IOError:
                               pass
               else:
                       return # assume C locale

               if _StrToInt(buffer[:4]) != 0x950412de:
                       # magic number doesn't match
                       raise error, 'Bad magic number in %s' % (catalog,)

               self.revision = _StrToInt(buffer[4:8])
               nstrings = _StrToInt(buffer[8:12])
               origTabOffset  = _StrToInt(buffer[12:16])
               transTabOffset = _StrToInt(buffer[16:20])
               for i in range(nstrings):
                       origLength = _StrToInt(buffer[origTabOffset:
                                                     origTabOffset+4])
                       origOffset = _StrToInt(buffer[origTabOffset+4:
                                                     origTabOffset+8])
                       origTabOffset = origTabOffset + 8
                       origStr = buffer[origOffset:origOffset+origLength]

                       transLength = _StrToInt(buffer[transTabOffset:
                                                      transTabOffset+4])
                       transOffset = _StrToInt(buffer[transTabOffset+4:
                                                      transTabOffset+8])
                       transTabOffset = transTabOffset + 8
                       transStr = buffer[transOffset:transOffset+transLength]

                       self.cat[origStr] = transStr

       def gettext(self, string):
               """Get the translation of a given string"""
               if self.cat.has_key(string):
                       return self.cat[string]
               else:
                       return string
       # allow catalog access as cat(str) and cat[str] and cat.gettext(str)
       __getitem__ = gettext
       __call__ = gettext

       # this is experimental code for producing mo files from Catalog objects
       def __setitem__(self, string, trans):
               """Set the translation of a given string"""
               self.cat[string] = trans
       def save(self, file):
               """Create a .mo file from a Catalog object"""
               try:
                       f = open(file, "wb")
               except IOError:
                       raise error, "can't open " + file + " for writing"
               f.write(_intToStr(0x950412de))    # magic number
               f.write(_intToStr(0))             # revision
               f.write(_intToStr(len(self.cat))) # nstrings

               oIndex = []; oData = ''
               tIndex = []; tData = ''
               for orig, trans in self.cat.items():
                       oIndex.append((len(orig), len(oData)))
                       oData = oData + orig + '\0'
                       tIndex.append((len(trans), len(tData)))
                       tData = tData + trans + '\0'
               oIndexOfs = 20
               tIndexOfs = oIndexOfs + 8 * len(oIndex)
               oDataOfs = tIndexOfs + 8 * len(tIndex)
               tDataOfs = oDataOfs + len(oData)
               f.write(_intToStr(oIndexOfs))
               f.write(_intToStr(tIndexOfs))
               for length, offset in oIndex:
                       f.write(_intToStr(length))
                       f.write(_intToStr(offset + oDataOfs))
               for length, offset in tIndex:
                       f.write(_intToStr(length))
                       f.write(_intToStr(offset + tDataOfs))
               f.write(oData)
               f.write(tData)

_cat = None
_cats = {}

if xgettext:
       class Catalog:
               def __init__(self, domain, localedir):
                       self.domain = domain
                       self.localedir = localedir
                       self._strings = {}
               def gettext(self, string):
                       # there is always one level of redirection for calls
                       # to this function
                       pos = _getpos(2) # get this function's caller
                       if self._strings.has_key(string):
                               if pos not in self._strings[string]:
                                       self._strings[string].append(pos)
                       else:
                               self._strings[string] = [pos]
                       return string
               __getitem__ = gettext
               __call__ = gettext
               def __setitem__(self, item, data):
                       pass
               def save(self, file):
                       pass
               def output(self, fp):
                       import string
                       fp.write('# POT file for domain %s\n' % (self.domain,))
                       for str in self._strings.keys():
                               pos = map(lambda x: "%s(%s):%d" % x,
                                         self._strings[str])
                               pos.sort()
                               length = 80
                               for p in pos:
                                       if length + len(p) > 74:
                                               fp.write('\n#:')
                                               length = 2
                                       fp.write(' ')
                                       fp.write(p)
                                       length = length + 1 + len(p)
                               fp.write('\n')
                               if '\n' in str:
                                       fp.write('msgid ""\n')
                                       lines = string.split(str, '\n')
                                       lines = map(lambda x:
                                                   '"%s\\n"\n' % (x,),
                                                   lines[:-1]) + \
                                                   ['"%s"\n' % (lines[-1],)]
                                       fp.writelines(lines)
                               else:
                                       fp.write('msgid "%s"\n' % (str,))
                               fp.write('msgstr ""\n')

       import sys
       if hasattr(sys, 'exitfunc'):
               _exitchain = sys.exitfunc
       else:
               _exitchain = None
       def exitfunc(dir=xgettext, _exitchain=_exitchain):
               # actually output all the .pot files.
               import os
               for file in _cats.keys():
                       fp = open(os.path.join(dir, file + '.pot'), 'w')
                       cat = _cats[file]
                       cat.output(fp)
                       fp.close()
               if _exitchain: _exitchain()
       sys.exitfunc = exitfunc
       del sys, exitfunc, _exitchain, xgettext

def bindtextdomain(domain, localedir=localedir):
       global _cat
       if not _cats.has_key(domain):
               _cats[domain] = Catalog(domain, localedir)
       if not _cat: _cat = _cats[domain]

def textdomain(domain):
       global _cat
       if not _cats.has_key(domain):
               _cats[domain] = Catalog(domain)
       _cat = _cats[domain]

def gettext(string):
       if _cat == None: raise error, "No catalog loaded"
       return _cat.gettext(string)

_ = gettext

def dgettext(domain, string):
       if domain is None:
               return gettext(string)
       if not _cats.has_key(domain):
               raise error, "Domain '" + domain + "' not loaded"
       return _cats[domain].gettext(string)

def test():
       import sys
       global localedir
       if len(sys.argv) not in (2, 3):
               print "Usage: %s DOMAIN [LOCALEDIR]" % (sys.argv[0],)
               sys.exit(1)
       domain = sys.argv[1]
       if len(sys.argv) == 3:
               bindtextdomain(domain, sys.argv[2])
       textdomain(domain)
       info = gettext('')  # this is where special info is often stored
       if info:
               print "Info for domain %s, lang %s." % (domain, _cat.lang)
               print info
       else:
               print "No info given in mo file."

def getlangs():
       global lang
       return lang

def setlangs(newlang):
       global lang
       lang = newlang

if __name__ == '__main__':
       test()