Gentoo Archives: gentoo-portage-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-portage-dev@l.g.o
Cc: "Michał Górny" <mgorny@g.o>
Subject: [gentoo-portage-dev] [PATCH 1/4] Warn if LC_CTYPE does not transform ASCII chars like POSIX
Date: Sun, 15 Nov 2015 21:17:37
Message-Id: 1447622247-9347-1-git-send-email-mgorny@gentoo.org
1 Output a warning if LC_CTYPE is set to a value that causes libc
2 toupper() and/or tolower() conversions not apply correctly to printable
3 ASCII characters.
4 ---
5 pym/_emerge/actions.py | 3 ++
6 pym/portage/util/locale.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++
7 2 files changed, 94 insertions(+)
8 create mode 100644 pym/portage/util/locale.py
9
10 diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py
11 index 7f1cb59..e03e8d4 100644
12 --- a/pym/_emerge/actions.py
13 +++ b/pym/_emerge/actions.py
14 @@ -27,6 +27,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
15 'portage.debug',
16 'portage.news:count_unread_news,display_news_notifications',
17 'portage.util._get_vm_info:get_vm_info',
18 + 'portage.util.locale:check_locale',
19 'portage.emaint.modules.sync.sync:SyncRepos',
20 '_emerge.chk_updated_cfg_files:chk_updated_cfg_files',
21 '_emerge.help:help@emerge_help',
22 @@ -2467,6 +2468,8 @@ def validate_ebuild_environment(trees):
23 for line in textwrap.wrap(msg, 65):
24 out.ewarn(line)
25
26 + check_locale()
27 +
28 def check_procfs():
29 procfs_path = '/proc'
30 if platform.system() not in ("Linux",) or \
31 diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py
32 new file mode 100644
33 index 0000000..529db35
34 --- /dev/null
35 +++ b/pym/portage/util/locale.py
36 @@ -0,0 +1,91 @@
37 +#-*- coding:utf-8 -*-
38 +# Copyright 2015 Gentoo Foundation
39 +# Distributed under the terms of the GNU General Public License v2
40 +"""
41 +Function to check whether the current used LC_CTYPE handles case
42 +transformations of ASCII characters in a way compatible with the POSIX
43 +locale.
44 +get_ro_checker().
45 +"""
46 +from __future__ import unicode_literals
47 +
48 +import logging
49 +import os
50 +import textwrap
51 +import traceback
52 +
53 +from portage.util import writemsg_level
54 +from portage.util._ctypes import find_library, LoadLibrary
55 +
56 +
57 +def _check_locale():
58 + """
59 + The inner locale check function.
60 + """
61 +
62 + libc_fn = find_library("c")
63 + if libc_fn is None:
64 + return None
65 + libc = LoadLibrary(libc_fn)
66 + if libc is None:
67 + return None
68 +
69 + lc = list(range(ord('a'), ord('z')+1))
70 + uc = list(range(ord('A'), ord('Z')+1))
71 + rlc = [libc.tolower(c) for c in uc]
72 + ruc = [libc.toupper(c) for c in lc]
73 +
74 + if lc != rlc or uc != ruc:
75 + msg = ("WARNING: The LC_CTYPE variable is set to a locale " +
76 + "that specifies transformation between lowercase " +
77 + "and uppercase ASCII characters that is different than " +
78 + "the one specified by POSIX locale. This can break " +
79 + "ebuilds and cause issues in programs that rely on " +
80 + "the common character conversion scheme. " +
81 + "Please consider enabling another locale (such as " +
82 + "en_US.UTF-8) in /etc/locale.gen and setting it " +
83 + "as LC_CTYPE in make.conf.")
84 + msg = [l for l in textwrap.wrap(msg, 70)]
85 + msg.append("")
86 + chars = lambda l: ''.join(chr(x) for x in l)
87 + if uc != ruc:
88 + msg.extend([
89 + " %s -> %s" % (chars(lc), chars(ruc)),
90 + " %28s: %s" % ('expected', chars(uc))])
91 + if lc != rlc:
92 + msg.extend([
93 + " %s -> %s" % (chars(uc), chars(rlc)),
94 + " %28s: %s" % ('expected', chars(lc))])
95 + writemsg_level("".join(["!!! %s\n" % l for l in msg]),
96 + level=logging.ERROR, noiselevel=-1)
97 + return False
98 +
99 + return True
100 +
101 +
102 +def check_locale():
103 + """
104 + Check whether the locale is sane. Returns True if it is, prints
105 + warning and returns False if it is not. Returns None if the check
106 + can not be executed due to platform limitations.
107 + """
108 +
109 + pid = os.fork()
110 + if pid == 0:
111 + try:
112 + ret = _check_locale()
113 + if ret is None:
114 + os._exit(2)
115 + else:
116 + os._exit(0 if ret else 1)
117 + except Exception:
118 + traceback.print_exc()
119 + os._exit(2)
120 +
121 + pid2, ret = os.waitpid(pid, 0)
122 + assert pid == pid2
123 + if os.WIFEXITED(ret):
124 + ret = os.WEXITSTATUS(ret)
125 + if ret != 2:
126 + return ret == 0
127 + return None
128 --
129 2.6.3

Replies