diff --git a/src/modules/localecfg/main.py b/src/modules/localecfg/main.py index d44d7da2b..62a00b738 100644 --- a/src/modules/localecfg/main.py +++ b/src/modules/localecfg/main.py @@ -7,6 +7,7 @@ # Copyright 2015, Philip Müller # Copyright 2016, Teo Mrnjavac # Copyright 2018, AlmAck +# Copyright 2018, Adriaan de Groot # # Calamares is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,14 +23,91 @@ # along with Calamares. If not, see . import os +import re import shutil -import libcalamares +RE_IS_COMMENT = re.compile("^ *#") +def is_comment(line): + """ + Does the @p line look like a comment? Whitespace, followed by a # + is a comment-only line. + """ + return bool(RE_IS_COMMENT.match(line)) + +RE_TRAILING_COMMENT = re.compile("#.*$") +RE_REST_OF_LINE = re.compile("\\s.*$") +def extract_locale(line): + """ + Extracts a locale from the @p line, and returns a pair of + (extracted-locale, uncommented line). The locale is the + first word of the line after uncommenting (in the human- + readable text explanation at the top of most /etc/locale.gen + files, the locales may be bogus -- either "" or e.g. "Configuration") + """ + # Remove leading spaces and comment signs + line = RE_IS_COMMENT.sub("", line) + uncommented = line.strip() + fields = RE_TRAILING_COMMENT.sub("", uncommented).strip().split() + if len(fields) != 2: + # Not exactly two fields, can't be a proper locale line + return "", uncommented + else: + # Drop all but first field + locale = RE_REST_OF_LINE.sub("", uncommented) + return locale, uncommented + + +def rewrite_locale_gen(srcfilename, destfilename, locale_conf): + """ + Copies a locale.gen file from @p srcfilename to @p destfilename + (this may be the same name), enabling those locales that can + be found in the map @p locale_conf. Also always enables en_US.UTF-8. + """ + en_us_locale = 'en_US.UTF-8' + + # Get entire source-file contents + text = [] + with open(srcfilename, "r") as gen: + text = gen.readlines() + + # we want unique values, so locale_values should have 1 or 2 items + locale_values = set(locale_conf.values()) + locale_values.add(en_us_locale) # Always enable en_US as well + + enabled_locales = {} + seen_locales = set() + + # Write source out again, enabling some + with open(destfilename, "w") as gen: + for line in text: + c = is_comment(line) + locale, uncommented = extract_locale(line) + + # Non-comment lines are preserved, and comment lines + # may be enabled if they match a desired locale + if not c: + seen_locales.add(locale) + else: + for locale_value in locale_values: + if locale.startswith(locale_value): + enabled_locales[locale] = uncommented + gen.write(line) + + gen.write("\n###\n#\n# Locales enabled by Calamares\n") + for locale, line in enabled_locales.items(): + if locale not in seen_locales: + gen.write(line + "\n") + seen_locales.add(locale) + + for locale in locale_values: + if locale not in seen_locales: + gen.write("# Missing: %s\n" % locale) def run(): """ Create locale """ - en_us_locale = 'en_US.UTF-8' + import libcalamares + locale_conf = libcalamares.globalstorage.value("localeConf") if not locale_conf: @@ -47,50 +125,36 @@ def run(): } install_path = libcalamares.globalstorage.value("rootMountPoint") + target_locale_gen = "{!s}/etc/locale.gen".format(install_path) + target_locale_gen_bak = target_locale_gen + ".bak" + target_locale_conf_path = "{!s}/etc/locale.conf".format(install_path) + target_etc_default_path = "{!s}/etc/default".format(install_path) # restore backup if available - if os.path.exists('/etc/locale.gen.bak'): - shutil.copy2("{!s}/etc/locale.gen.bak".format(install_path), - "{!s}/etc/locale.gen".format(install_path)) - - # run locale-gen if detected + if os.path.exists(target_locale_gen_bak): + shutil.copy2(target_locale_gen_bak, target_locale_gen) + libcalamares.utils.debug("Restored backup {!s} -> {!s}" + .format(target_locale_gen_bak).format(target_locale_gen)) + + # run locale-gen if detected; this *will* cause an exception + # if the live system has locale.gen, but the target does not: + # in that case, fix your installation filesystem. if os.path.exists('/etc/locale.gen'): - text = [] - - with open("{!s}/etc/locale.gen".format(install_path), "r") as gen: - text = gen.readlines() - - # we want unique values, so locale_values should have 1 or 2 items - locale_values = set(locale_conf.values()) - - with open("{!s}/etc/locale.gen".format(install_path), "w") as gen: - for line in text: - # always enable en_US - if line.startswith("#" + en_us_locale): - # uncomment line - line = line[1:].lstrip() - - for locale_value in locale_values: - if line.startswith("#" + locale_value): - # uncomment line - line = line[1:].lstrip() - - gen.write(line) - + rewrite_locale_gen(target_locale_gen, target_locale_gen, locale_conf) libcalamares.utils.target_env_call(['locale-gen']) - print('locale.gen done') + libcalamares.utils.debug('{!s} done'.format(target_locale_gen)) # write /etc/locale.conf - locale_conf_path = os.path.join(install_path, "etc/locale.conf") - with open(locale_conf_path, "w") as lcf: + with open(target_locale_conf_path, "w") as lcf: for k, v in locale_conf.items(): lcf.write("{!s}={!s}\n".format(k, v)) + libcalamares.utils.debug('{!s} done'.format(target_locale_conf_path)) # write /etc/default/locale if /etc/default exists and is a dir - etc_default_path = os.path.join(install_path, "etc/default") - if os.path.isdir(etc_default_path): - with open(os.path.join(etc_default_path, "locale"), "w") as edl: + if os.path.isdir(target_etc_default_path): + with open(os.path.join(target_etc_default_path, "locale"), "w") as edl: for k, v in locale_conf.items(): edl.write("{!s}={!s}\n".format(k, v)) + libcalamares.utils.debug('{!s} done'.format(target_etc_default_path)) return None diff --git a/src/modules/localecfg/module.desc b/src/modules/localecfg/module.desc index 89baab7ad..815480562 100644 --- a/src/modules/localecfg/module.desc +++ b/src/modules/localecfg/module.desc @@ -1,3 +1,6 @@ +# Enable the configured locales (those set by the user on the +# user page) in /etc/locale.gen, if they are available in the +# target system. --- type: "job" name: "localecfg"