1 |
From: "Anthony G. Basile" <blueness@g.o> |
2 |
|
3 |
The current method to check for the system locale is to use python's |
4 |
ctypes.util.find_library() to construct a full library path to the |
5 |
system libc.so which is then passed to ctypes.CDLL(). However, |
6 |
this gets bogged down in implementation dependant details and |
7 |
fails with musl. |
8 |
|
9 |
We work around this design flaw in ctypes with a small python module |
10 |
written in C called 'portage_c_check_locale', and only fall back on |
11 |
the current ctypes-based check when this module is not available. |
12 |
|
13 |
This has been tested on glibc, uClibc and musl systems. |
14 |
|
15 |
X-Gentoo-bug: 571444 |
16 |
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=571444 |
17 |
Signed-off-by: Anthony G. Basile <blueness@g.o> |
18 |
--- |
19 |
pym/portage/util/locale.py | 40 +++++++++---- |
20 |
setup.py | 6 +- |
21 |
src/check_locale.c | 144 +++++++++++++++++++++++++++++++++++++++++++++ |
22 |
3 files changed, 178 insertions(+), 12 deletions(-) |
23 |
create mode 100644 src/check_locale.c |
24 |
|
25 |
diff --git a/pym/portage/util/locale.py b/pym/portage/util/locale.py |
26 |
index 2a15ea1..fc5d052 100644 |
27 |
--- a/pym/portage/util/locale.py |
28 |
+++ b/pym/portage/util/locale.py |
29 |
@@ -30,17 +30,17 @@ locale_categories = ( |
30 |
_check_locale_cache = {} |
31 |
|
32 |
|
33 |
-def _check_locale(silent): |
34 |
+def _ctypes_check_locale(): |
35 |
""" |
36 |
- The inner locale check function. |
37 |
+ Check for locale using ctypes. |
38 |
""" |
39 |
|
40 |
libc_fn = find_library("c") |
41 |
if libc_fn is None: |
42 |
- return None |
43 |
+ return (None, "") |
44 |
libc = LoadLibrary(libc_fn) |
45 |
if libc is None: |
46 |
- return None |
47 |
+ return (None, "") |
48 |
|
49 |
lc = list(range(ord('a'), ord('z')+1)) |
50 |
uc = list(range(ord('A'), ord('Z')+1)) |
51 |
@@ -48,9 +48,6 @@ def _check_locale(silent): |
52 |
ruc = [libc.toupper(c) for c in lc] |
53 |
|
54 |
if lc != rlc or uc != ruc: |
55 |
- if silent: |
56 |
- return False |
57 |
- |
58 |
msg = ("WARNING: The LC_CTYPE variable is set to a locale " + |
59 |
"that specifies transformation between lowercase " + |
60 |
"and uppercase ASCII characters that is different than " + |
61 |
@@ -71,11 +68,32 @@ def _check_locale(silent): |
62 |
msg.extend([ |
63 |
" %s -> %s" % (chars(uc), chars(rlc)), |
64 |
" %28s: %s" % ('expected', chars(lc))]) |
65 |
- writemsg_level("".join(["!!! %s\n" % l for l in msg]), |
66 |
- level=logging.ERROR, noiselevel=-1) |
67 |
- return False |
68 |
+ msg = "".join(["!!! %s\n" % l for l in msg]), |
69 |
+ return (False, msg) |
70 |
+ |
71 |
+ return (True, "") |
72 |
+ |
73 |
+ |
74 |
+def _check_locale(silent): |
75 |
+ """ |
76 |
+ The inner locale check function. |
77 |
+ """ |
78 |
+ |
79 |
+ try: |
80 |
+ from portage_c_check_locale import _c_check_locale |
81 |
+ (ret, msg) = _c_check_locale() |
82 |
+ except ImportError: |
83 |
+ writemsg_level("!!! Unable to import portage_c_check_locale\n", |
84 |
+ level=logging.WARNING, noiselevel=-1) |
85 |
+ (ret, msg) = _ctypes_check_locale() |
86 |
+ |
87 |
+ if ret: |
88 |
+ return True |
89 |
+ |
90 |
+ if not silent: |
91 |
+ writemsg_level(msg, level=logging.ERROR, noiselevel=-1) |
92 |
|
93 |
- return True |
94 |
+ return False |
95 |
|
96 |
|
97 |
def check_locale(silent=False, env=None): |
98 |
diff --git a/setup.py b/setup.py |
99 |
index 25429bc..e44ac41 100755 |
100 |
--- a/setup.py |
101 |
+++ b/setup.py |
102 |
@@ -47,7 +47,11 @@ x_scripts = { |
103 |
# Dictionary custom modules written in C/C++ here. The structure is |
104 |
# key = module name |
105 |
# value = list of C/C++ source code, path relative to top source directory |
106 |
-x_c_helpers = {} |
107 |
+x_c_helpers = { |
108 |
+ 'portage_c_check_locale' : [ |
109 |
+ 'src/check_locale.c', |
110 |
+ ], |
111 |
+} |
112 |
|
113 |
class x_build(build): |
114 |
""" Build command with extra build_man call. """ |
115 |
diff --git a/src/check_locale.c b/src/check_locale.c |
116 |
new file mode 100644 |
117 |
index 0000000..9762ef2 |
118 |
--- /dev/null |
119 |
+++ b/src/check_locale.c |
120 |
@@ -0,0 +1,144 @@ |
121 |
+/* Copyright 2005-2015 Gentoo Foundation |
122 |
+ * Distributed under the terms of the GNU General Public License v2 |
123 |
+ */ |
124 |
+ |
125 |
+#include <Python.h> |
126 |
+#include <ctype.h> |
127 |
+#include <string.h> |
128 |
+ |
129 |
+static PyObject * portage_c_check_locale(PyObject *, PyObject *); |
130 |
+ |
131 |
+static PyMethodDef CheckLocaleMethods[] = { |
132 |
+ {"_c_check_locale", portage_c_check_locale, METH_NOARGS, "Check the system locale."}, |
133 |
+ {NULL, NULL, 0, NULL} |
134 |
+}; |
135 |
+ |
136 |
+#if PY_MAJOR_VERSION >= 3 |
137 |
+static struct PyModuleDef moduledef = { |
138 |
+ PyModuleDef_HEAD_INIT, |
139 |
+ "portage_c_check_locale", /* m_name */ |
140 |
+ "Module for checking the system locale for portage", /* m_doc */ |
141 |
+ -1, /* m_size */ |
142 |
+ CheckLocaleMethods, /* m_methods */ |
143 |
+ NULL, /* m_reload */ |
144 |
+ NULL, /* m_traverse */ |
145 |
+ NULL, /* m_clear */ |
146 |
+ NULL, /* m_free */ |
147 |
+}; |
148 |
+#endif |
149 |
+ |
150 |
+static PyObject *LocaleError; |
151 |
+ |
152 |
+PyMODINIT_FUNC |
153 |
+#if PY_MAJOR_VERSION >= 3 |
154 |
+PyInit_portage_c_check_locale(void) |
155 |
+#else |
156 |
+initportage_c_check_locale(void) |
157 |
+#endif |
158 |
+{ |
159 |
+ PyObject *m; |
160 |
+ |
161 |
+#if PY_MAJOR_VERSION >= 3 |
162 |
+ m = PyModule_Create(&moduledef); |
163 |
+#else |
164 |
+ m = Py_InitModule("portage_c_check_locale", CheckLocaleMethods); |
165 |
+#endif |
166 |
+ |
167 |
+ if (m == NULL) |
168 |
+#if PY_MAJOR_VERSION >= 3 |
169 |
+ return NULL; |
170 |
+#else |
171 |
+ return; |
172 |
+#endif |
173 |
+ |
174 |
+ LocaleError = PyErr_NewException("locale.LocaleError", NULL, NULL); |
175 |
+ Py_INCREF(LocaleError); |
176 |
+ PyModule_AddObject(m, "LocaleError", LocaleError); |
177 |
+ |
178 |
+#if PY_MAJOR_VERSION >= 3 |
179 |
+ return m; |
180 |
+#else |
181 |
+ return; |
182 |
+#endif |
183 |
+} |
184 |
+ |
185 |
+ |
186 |
+static void |
187 |
+error_msg(char msg[], int c[], int rc[], int ac[]) |
188 |
+{ |
189 |
+ int i, p; |
190 |
+ |
191 |
+ strcat(msg, " "); |
192 |
+ p = strlen(msg); |
193 |
+ for (i = 'a'; i <= 'z'; i++) |
194 |
+ msg[p++] = c[i-'a']; |
195 |
+ |
196 |
+ strcat(msg, " -> "); |
197 |
+ p = strlen(msg); |
198 |
+ for (i = 'a'; i <= 'z'; i++) |
199 |
+ msg[p++] = rc[i-'a']; |
200 |
+ strcat(msg, "\n"); |
201 |
+ |
202 |
+ strcat(msg, " expected: "); |
203 |
+ p = strlen(msg); |
204 |
+ for (i = 'a'; i <= 'z'; i++) |
205 |
+ msg[p++] = ac[i-'a']; |
206 |
+ strcat(msg, "\n\n"); |
207 |
+} |
208 |
+ |
209 |
+ |
210 |
+static PyObject * |
211 |
+portage_c_check_locale(PyObject *self, PyObject *args) |
212 |
+{ |
213 |
+ int i, upper = 1, lower = 1; |
214 |
+ int lc[26], uc[26], rlc[26], ruc[26]; |
215 |
+ char msg[1024]; |
216 |
+ |
217 |
+ memset(msg, 0, 1024); |
218 |
+ |
219 |
+ for (i = 'a'; i <= 'z'; i++) { |
220 |
+ lc[i-'a'] = i; |
221 |
+ ruc[i-'a'] = toupper(i); |
222 |
+ } |
223 |
+ |
224 |
+ for (i = 'A'; i <= 'Z'; i++) { |
225 |
+ uc[i-'A'] = i; |
226 |
+ rlc[i-'A'] = tolower(i); |
227 |
+ } |
228 |
+ |
229 |
+ for (i = 'a'; i <= 'z'; i++) |
230 |
+ if(lc[i-'a'] != rlc[i-'a']) { |
231 |
+ lower = 0; |
232 |
+ break; |
233 |
+ } |
234 |
+ |
235 |
+ for (i = 'A'; i <= 'Z'; i++) |
236 |
+ if(uc[i-'A'] != ruc[i-'A']) { |
237 |
+ upper = 0; |
238 |
+ break; |
239 |
+ } |
240 |
+ |
241 |
+ if (lower == 0 || upper == 0) { |
242 |
+ strcpy(msg, |
243 |
+ "!!! WARNING: The LC_CTYPE variable is set to a locale that specifies\n" |
244 |
+ "!!! transformation between lowercase and uppercase ASCII characters that\n" |
245 |
+ "!!! is different than the one specified by POSIX locale. This can break\n" |
246 |
+ "!!! ebuilds and cause issues in programs that rely on the common character\n" |
247 |
+ "!!! conversion scheme. Please consider enabling another locale (such as\n" |
248 |
+ "!!! en_US.UTF-8) in /etc/locale.gen and setting it as LC_CTYPE in\n" |
249 |
+ "!!! make.conf.\n\n" |
250 |
+ ); |
251 |
+ |
252 |
+ if (lower == 0) |
253 |
+ error_msg(msg, lc, ruc, uc); |
254 |
+ |
255 |
+ if (upper == 0) |
256 |
+ error_msg(msg, uc, rlc, lc); |
257 |
+ } |
258 |
+ |
259 |
+#if PY_MAJOR_VERSION >= 3 |
260 |
+ return Py_BuildValue("iy", lower && upper, msg); |
261 |
+#else |
262 |
+ return Py_BuildValue("is", lower && upper, msg); |
263 |
+#endif |
264 |
+} |
265 |
-- |
266 |
2.7.3 |