aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Górny <mgorny@gentoo.org>2015-11-11 22:09:26 +0100
committerMichał Górny <mgorny@gentoo.org>2015-11-15 22:18:45 +0100
commitdaeb752c842e892a6055d686173ec9ab0f9961de (patch)
tree3178ae8d4d12d6ed89ea9969026caf678fbe01f1
parent__eapi6_src_prepare: Silence 'declare -p PATCHES' errors (diff)
downloadportage-daeb752c842e892a6055d686173ec9ab0f9961de.tar.gz
portage-daeb752c842e892a6055d686173ec9ab0f9961de.tar.bz2
portage-daeb752c842e892a6055d686173ec9ab0f9961de.zip
Warn if LC_CTYPE does not transform ASCII chars like POSIX
Output a warning if LC_CTYPE is set to a value that causes libc toupper() and/or tolower() conversions not apply correctly to printable ASCII characters.
-rw-r--r--pym/_emerge/actions.py3
-rw-r--r--pym/portage/util/locale.py90
2 files changed, 93 insertions, 0 deletions
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
index 7f1cb5921..e03e8d463 100644
--- a/pym/_emerge/actions.py
+++ b/pym/_emerge/actions.py
@@ -27,6 +27,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
'portage.debug',
'portage.news:count_unread_news,display_news_notifications',
'portage.util._get_vm_info:get_vm_info',
+ 'portage.util.locale:check_locale',
'portage.emaint.modules.sync.sync:SyncRepos',
'_emerge.chk_updated_cfg_files:chk_updated_cfg_files',
'_emerge.help:help@emerge_help',
@@ -2467,6 +2468,8 @@ def validate_ebuild_environment(trees):
for line in textwrap.wrap(msg, 65):
out.ewarn(line)
+ check_locale()
+
def check_procfs():
procfs_path = '/proc'
if platform.system() not in ("Linux",) or \
diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py
new file mode 100644
index 000000000..12161eb6c
--- /dev/null
+++ b/pym/portage/util/locale.py
@@ -0,0 +1,90 @@
+#-*- coding:utf-8 -*-
+# Copyright 2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+"""
+Function to check whether the current used LC_CTYPE handles case
+transformations of ASCII characters in a way compatible with the POSIX
+locale.
+"""
+from __future__ import unicode_literals
+
+import logging
+import os
+import textwrap
+import traceback
+
+from portage.util import writemsg_level
+from portage.util._ctypes import find_library, LoadLibrary
+
+
+def _check_locale():
+ """
+ The inner locale check function.
+ """
+
+ libc_fn = find_library("c")
+ if libc_fn is None:
+ return None
+ libc = LoadLibrary(libc_fn)
+ if libc is None:
+ return None
+
+ lc = list(range(ord('a'), ord('z')+1))
+ uc = list(range(ord('A'), ord('Z')+1))
+ rlc = [libc.tolower(c) for c in uc]
+ ruc = [libc.toupper(c) for c in lc]
+
+ if lc != rlc or uc != ruc:
+ msg = ("WARNING: The LC_CTYPE variable is set to a locale " +
+ "that specifies transformation between lowercase " +
+ "and uppercase ASCII characters that is different than " +
+ "the one specified by POSIX locale. This can break " +
+ "ebuilds and cause issues in programs that rely on " +
+ "the common character conversion scheme. " +
+ "Please consider enabling another locale (such as " +
+ "en_US.UTF-8) in /etc/locale.gen and setting it " +
+ "as LC_CTYPE in make.conf.")
+ msg = [l for l in textwrap.wrap(msg, 70)]
+ msg.append("")
+ chars = lambda l: ''.join(chr(x) for x in l)
+ if uc != ruc:
+ msg.extend([
+ " %s -> %s" % (chars(lc), chars(ruc)),
+ " %28s: %s" % ('expected', chars(uc))])
+ if lc != rlc:
+ msg.extend([
+ " %s -> %s" % (chars(uc), chars(rlc)),
+ " %28s: %s" % ('expected', chars(lc))])
+ writemsg_level("".join(["!!! %s\n" % l for l in msg]),
+ level=logging.ERROR, noiselevel=-1)
+ return False
+
+ return True
+
+
+def check_locale():
+ """
+ Check whether the locale is sane. Returns True if it is, prints
+ warning and returns False if it is not. Returns None if the check
+ can not be executed due to platform limitations.
+ """
+
+ pid = os.fork()
+ if pid == 0:
+ try:
+ ret = _check_locale()
+ if ret is None:
+ os._exit(2)
+ else:
+ os._exit(0 if ret else 1)
+ except Exception:
+ traceback.print_exc()
+ os._exit(2)
+
+ pid2, ret = os.waitpid(pid, 0)
+ assert pid == pid2
+ if os.WIFEXITED(ret):
+ ret = os.WEXITSTATUS(ret)
+ if ret != 2:
+ return ret == 0
+ return None