aif-ng/aif/system/locales.py

131 lines
5.2 KiB
Python
Raw Normal View History

2019-12-04 01:48:41 -05:00
import configparser
2019-12-11 04:32:07 -05:00
import copy
2019-12-04 01:48:41 -05:00
import io
import os
import re
import shutil
import subprocess
2019-12-05 18:04:51 -05:00
# TODO: time
2019-12-04 01:48:41 -05:00
_locale_re = re.compile(r'^(?!#\s|)$')
_locale_def_re = re.compile(r'([^.]*)[^@]*(.*)')
class Locale(object):
def __init__(self, chroot_base, locales_xml):
2019-12-04 01:48:41 -05:00
self.xml = locales_xml
self.chroot_base = chroot_base
2019-12-04 01:48:41 -05:00
self.syslocales = {}
self.userlocales = []
self.rawlocales = None
self._localevars = configparser.ConfigParser()
self._localevars.optionxform = str
self._localevars['BASE'] = {}
self._initVars()
def _initVars(self):
for l in self.xml.findall('locale'):
locale = l.text.strip()
self._localevars['BASE'][l.attrib['name'].strip()] = locale
if locale not in self.userlocales:
self.userlocales.append(locale)
if not self.userlocales:
self.userlocales = ['en_US', 'en_US.UTF-8']
return()
def _verify(self):
localegen = os.path.join(self.chroot_base, 'etc', 'locale.gen') # This *should* be brand new.
2019-12-04 01:48:41 -05:00
with open(localegen, 'r') as fh:
self.rawlocales = fh.read().splitlines()
for idx, line in enumerate(self.rawlocales[:]):
if _locale_re.search(line) or line.strip() == '':
continue
locale, charset = line.split()
locale = locale.replace('#', '')
self.syslocales[locale] = charset
if locale in self.userlocales:
# "Uncomment" the locale (self.writeConf() actually writes the change)
self.rawlocales[idx] = '{0} {1}'.format(locale, charset)
userl = set(self.userlocales)
sysl = set(self.syslocales.keys())
if (userl - sysl):
raise ValueError('non-existent locale specified')
return()
def writeConf(self):
2019-12-04 01:48:41 -05:00
# We basically recreate locale-gen in python here, more or less.
self._verify()
localegen = os.path.join(self.chroot_base, 'etc', 'locale.gen')
localedbdir = os.path.join(self.chroot_base, 'usr', 'lib', 'locale')
localesrcdir = os.path.join(self.chroot_base, 'usr', 'share', 'i18n')
2019-12-04 01:48:41 -05:00
with open(localegen, 'w') as fh:
fh.write('# Generated by AIF-NG.\n\n')
fh.write('\n'.join(self.rawlocales))
fh.write('\n')
# If only the locale DB wasn't in a hopelessly binary format.
for root, dirs, files in os.walk(localedbdir):
for f in files:
fpath = os.path.join(root, f)
os.remove(fpath)
for d in dirs:
dpath = os.path.join(root, d)
shutil.rmtree(dpath)
# TODO: logging
for locale in self.userlocales:
lpath = os.path.join(localesrcdir, 'locales', locale)
charset = self.syslocales[locale]
if os.path.isfile(lpath):
ldef_name = locale
else:
ldef_name = _locale_def_re.sub(r'\g<1>\g<2>', locale)
lpath = os.path.join(localesrcdir, 'locales', ldef_name)
2019-12-11 04:32:07 -05:00
env = copy.deepycopy(dict(os.environ))
2019-12-04 01:48:41 -05:00
env['I18NPATH'] = localesrcdir
subprocess.run(['localedef',
'--force',
# These are overridden by a prefix env var.
# '--inputfile={0}'.format(lpath),
# '--charmap={0}'.format(os.path.join(localesrcdir, 'charmaps', charset)),
'--inputfile={0}'.format(ldef_name),
'--charmap={0}'.format(charset),
'--alias-file={0}'.format(os.path.join(self.chroot_base,
2019-12-04 01:48:41 -05:00
'usr', 'share', 'locale', 'locale.alias')),
'--prefix={0}'.format(self.chroot_base),
2019-12-04 01:48:41 -05:00
locale],
env = env)
cfg = os.path.join(self.chroot_base, 'etc', 'locale.conf')
2019-12-04 01:48:41 -05:00
# And now we write the variables.
# We have to strip out the section from the ini.
cfgbuf = io.StringIO()
self._localevars.write(cfgbuf, space_around_delimiters = False)
cfgbuf.seek(0, 0)
with open(cfg, 'w') as fh:
for line in cfgbuf.readlines():
if line.startswith('[BASE]') or line.strip() == '':
continue
fh.write(line)
os.chmod(cfg, 0o0644)
os.chown(cfg, 0, 0)
return()
class Timezone(object):
def __init__(self, chroot_base, timezone):
self.tz = timezone.strip().replace('.', '/')
self.chroot_base = chroot_base
def _verify(self):
tzfilebase = os.path.join('usr', 'share', 'zoneinfo', self.tz)
tzfile = os.path.join(self.chroot_base, tzfilebase)
if not os.path.isfile(tzfile):
raise ValueError('Invalid timezone')
return(tzfilebase)
def apply(self):
tzsrcfile = os.path.join('/', self._verify())
tzdestfile = os.path.join(self.chroot_base, 'etc', 'localtime')
if os.path.isfile(tzdestfile):
os.remove(tzdestfile)
os.symlink(tzsrcfile, tzdestfile)
return()