Commit | Line | Data |
---|---|---|
85e57214 LC |
1 | The format of locale data can be incompatible between libc versions, and |
2 | loading incompatible data can lead to 'setlocale' returning EINVAL at best | |
3 | or triggering an assertion failure at worst. See | |
4 | https://lists.gnu.org/archive/html/guix-devel/2015-09/msg00717.html | |
5 | for background information. | |
6 | ||
7 | To address that, this patch changes libc to honor a new 'GUIX_LOCPATH' | |
8 | variable, and to look for locale data in version-specific sub-directories of | |
9 | that variable. So, if GUIX_LOCPATH=/foo:/bar, locale data is searched for in | |
10 | /foo/X.Y and /bar/X.Y, where X.Y is the libc version number. | |
11 | ||
12 | That way, a single 'GUIX_LOCPATH' setting can work even if different libc | |
13 | versions coexist on the system. | |
14 | ||
15 | --- a/locale/newlocale.c | |
16 | +++ b/locale/newlocale.c | |
17 | @@ -30,6 +30,7 @@ | |
18 | /* Lock for protecting global data. */ | |
19 | __libc_rwlock_define (extern , __libc_setlocale_lock attribute_hidden) | |
20 | ||
21 | +extern error_t compute_locale_search_path (char **, size_t *); | |
22 | ||
23 | /* Use this when we come along an error. */ | |
24 | #define ERROR_RETURN \ | |
25 | @@ -48,7 +49,6 @@ __newlocale (int category_mask, const char *locale, __locale_t base) | |
26 | __locale_t result_ptr; | |
27 | char *locale_path; | |
28 | size_t locale_path_len; | |
29 | - const char *locpath_var; | |
30 | int cnt; | |
31 | size_t names_len; | |
32 | ||
33 | @@ -102,17 +102,8 @@ __newlocale (int category_mask, const char *locale, __locale_t base) | |
34 | locale_path = NULL; | |
35 | locale_path_len = 0; | |
36 | ||
37 | - locpath_var = getenv ("LOCPATH"); | |
38 | - if (locpath_var != NULL && locpath_var[0] != '\0') | |
39 | - { | |
40 | - if (__argz_create_sep (locpath_var, ':', | |
41 | - &locale_path, &locale_path_len) != 0) | |
42 | - return NULL; | |
43 | - | |
44 | - if (__argz_add_sep (&locale_path, &locale_path_len, | |
45 | - _nl_default_locale_path, ':') != 0) | |
46 | - return NULL; | |
47 | - } | |
48 | + if (compute_locale_search_path (&locale_path, &locale_path_len) != 0) | |
49 | + return NULL; | |
50 | ||
51 | /* Get the names for the locales we are interested in. We either | |
52 | allow a composite name or a single name. */ | |
53 | diff --git a/locale/setlocale.c b/locale/setlocale.c | |
54 | index ead030d..0c0e314 100644 | |
55 | --- a/locale/setlocale.c | |
56 | +++ b/locale/setlocale.c | |
57 | @@ -215,12 +215,65 @@ setdata (int category, struct __locale_data *data) | |
58 | } | |
59 | } | |
60 | ||
61 | +/* Return in *LOCALE_PATH and *LOCALE_PATH_LEN the locale data search path as | |
62 | + a colon-separated list. Return ENOMEN on error, zero otherwise. */ | |
63 | +error_t | |
64 | +compute_locale_search_path (char **locale_path, size_t *locale_path_len) | |
65 | +{ | |
66 | + char* guix_locpath_var = getenv ("GUIX_LOCPATH"); | |
67 | + char *locpath_var = getenv ("LOCPATH"); | |
68 | + | |
69 | + if (guix_locpath_var != NULL && guix_locpath_var[0] != '\0') | |
70 | + { | |
71 | + /* Entries in 'GUIX_LOCPATH' take precedence over 'LOCPATH'. These | |
72 | + entries are systematically prefixed with "/X.Y" where "X.Y" is the | |
73 | + libc version. */ | |
74 | + if (__argz_create_sep (guix_locpath_var, ':', | |
75 | + locale_path, locale_path_len) != 0 | |
76 | + || __argz_suffix_entries (locale_path, locale_path_len, | |
77 | + "/" VERSION) != 0) | |
78 | + goto bail_out; | |
79 | + } | |
80 | + | |
81 | + if (locpath_var != NULL && locpath_var[0] != '\0') | |
82 | + { | |
83 | + char *reg_locale_path = NULL; | |
84 | + size_t reg_locale_path_len = 0; | |
85 | + | |
86 | + if (__argz_create_sep (locpath_var, ':', | |
87 | + ®_locale_path, ®_locale_path_len) != 0) | |
88 | + goto bail_out; | |
89 | + | |
90 | + if (__argz_append (locale_path, locale_path_len, | |
91 | + reg_locale_path, reg_locale_path_len) != 0) | |
92 | + goto bail_out; | |
93 | + | |
94 | + free (reg_locale_path); | |
95 | + } | |
96 | + | |
97 | + if (*locale_path != NULL) | |
98 | + { | |
99 | + /* Append the system default locale directory. */ | |
100 | + if (__argz_add_sep (locale_path, locale_path_len, | |
101 | + _nl_default_locale_path, ':') != 0) | |
102 | + goto bail_out; | |
103 | + } | |
104 | + | |
105 | + return 0; | |
106 | + | |
107 | + bail_out: | |
108 | + free (*locale_path); | |
109 | + *locale_path = NULL; | |
110 | + *locale_path_len = 0; | |
111 | + | |
112 | + return ENOMEM; | |
113 | +} | |
114 | + | |
115 | char * | |
116 | setlocale (int category, const char *locale) | |
117 | { | |
118 | char *locale_path; | |
119 | size_t locale_path_len; | |
120 | - const char *locpath_var; | |
121 | char *composite; | |
122 | ||
123 | /* Sanity check for CATEGORY argument. */ | |
124 | @@ -251,17 +304,10 @@ setlocale (int category, const char *locale) | |
125 | locale_path = NULL; | |
126 | locale_path_len = 0; | |
127 | ||
128 | - locpath_var = getenv ("LOCPATH"); | |
129 | - if (locpath_var != NULL && locpath_var[0] != '\0') | |
130 | + if (compute_locale_search_path (&locale_path, &locale_path_len) != 0) | |
131 | { | |
132 | - if (__argz_create_sep (locpath_var, ':', | |
133 | - &locale_path, &locale_path_len) != 0 | |
134 | - || __argz_add_sep (&locale_path, &locale_path_len, | |
135 | - _nl_default_locale_path, ':') != 0) | |
136 | - { | |
137 | - __libc_rwlock_unlock (__libc_setlocale_lock); | |
138 | - return NULL; | |
139 | - } | |
140 | + __libc_rwlock_unlock (__libc_setlocale_lock); | |
141 | + return NULL; | |
142 | } | |
143 | ||
144 | if (category == LC_ALL) | |
145 | diff --git a/string/Makefile b/string/Makefile | |
146 | index 8424a61..f925503 100644 | |
147 | --- a/string/Makefile | |
148 | +++ b/string/Makefile | |
149 | @@ -38,7 +38,7 @@ routines := strcat strchr strcmp strcoll strcpy strcspn \ | |
150 | swab strfry memfrob memmem rawmemchr strchrnul \ | |
151 | $(addprefix argz-,append count create ctsep next \ | |
152 | delete extract insert stringify \ | |
153 | - addsep replace) \ | |
154 | + addsep replace suffix) \ | |
155 | envz basename \ | |
156 | strcoll_l strxfrm_l string-inlines memrchr \ | |
157 | xpg-strerror strerror_l | |
158 | diff --git a/string/argz-suffix.c b/string/argz-suffix.c | |
159 | new file mode 100644 | |
160 | index 0000000..505b0f2 | |
161 | --- /dev/null | |
162 | +++ b/string/argz-suffix.c | |
163 | @@ -0,0 +1,56 @@ | |
164 | +/* Copyright (C) 2015 Free Software Foundation, Inc. | |
165 | + This file is part of the GNU C Library. | |
166 | + Contributed by Ludovic Courtès <ludo@gnu.org>. | |
167 | + | |
168 | + The GNU C Library is free software; you can redistribute it and/or | |
169 | + modify it under the terms of the GNU Lesser General Public | |
170 | + License as published by the Free Software Foundation; either | |
171 | + version 2.1 of the License, or (at your option) any later version. | |
172 | + | |
173 | + The GNU C Library is distributed in the hope that it will be useful, | |
174 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
175 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
176 | + Lesser General Public License for more details. | |
177 | + | |
178 | + You should have received a copy of the GNU Lesser General Public | |
179 | + License along with the GNU C Library; if not, see | |
180 | + <http://www.gnu.org/licenses/>. */ | |
181 | + | |
182 | +#include <argz.h> | |
183 | +#include <errno.h> | |
184 | +#include <stdlib.h> | |
185 | +#include <string.h> | |
186 | + | |
187 | + | |
188 | +error_t | |
189 | +__argz_suffix_entries (char **argz, size_t *argz_len, const char *suffix) | |
190 | + | |
191 | +{ | |
192 | + size_t suffix_len = strlen (suffix); | |
193 | + size_t count = __argz_count (*argz, *argz_len); | |
194 | + size_t new_argz_len = *argz_len + count * suffix_len; | |
195 | + char *new_argz = malloc (new_argz_len); | |
196 | + | |
197 | + if (new_argz) | |
198 | + { | |
199 | + char *p = new_argz, *entry; | |
200 | + | |
201 | + for (entry = *argz; | |
202 | + entry != NULL; | |
203 | + entry = argz_next (*argz, *argz_len, entry)) | |
204 | + { | |
205 | + p = stpcpy (p, entry); | |
206 | + p = stpcpy (p, suffix); | |
207 | + p++; | |
208 | + } | |
209 | + | |
210 | + free (*argz); | |
211 | + *argz = new_argz; | |
212 | + *argz_len = new_argz_len; | |
213 | + | |
214 | + return 0; | |
215 | + } | |
216 | + else | |
217 | + return ENOMEM; | |
218 | +} | |
219 | +weak_alias (__argz_suffix_entries, argz_suffix_entries) | |
220 | diff --git a/string/argz.h b/string/argz.h | |
221 | index bb62a31..d276a35 100644 | |
222 | --- a/string/argz.h | |
223 | +++ b/string/argz.h | |
224 | @@ -134,6 +134,16 @@ extern error_t argz_replace (char **__restrict __argz, | |
225 | const char *__restrict __str, | |
226 | const char *__restrict __with, | |
227 | unsigned int *__restrict __replace_count); | |
228 | + | |
229 | +/* Suffix each entry of ARGZ & ARGZ_LEN with SUFFIX. Return 0 on success, | |
230 | + and ENOMEN if memory cannot be allocated. */ | |
231 | +extern error_t __argz_suffix_entries (char **__restrict __argz, | |
232 | + size_t *__restrict __argz_len, | |
233 | + const char *__restrict __suffix); | |
234 | +extern error_t argz_suffix_entries (char **__restrict __argz, | |
235 | + size_t *__restrict __argz_len, | |
236 | + const char *__restrict __suffix); | |
237 | + | |
238 | \f | |
239 | /* Returns the next entry in ARGZ & ARGZ_LEN after ENTRY, or NULL if there | |
240 | are no more. If entry is NULL, then the first entry is returned. This |