]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/locale-util.c
util-lib: split stat()/statfs()/stavfs() related calls into stat-util.[ch]
[thirdparty/systemd.git] / src / basic / locale-util.c
CommitLineData
75683450
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/mman.h>
23
a0956174 24#include "dirent-util.h"
3ffd4af2
LP
25#include "fd-util.h"
26#include "locale-util.h"
bb15fafe 27#include "path-util.h"
75683450 28#include "set.h"
07630cea 29#include "string-util.h"
75683450 30#include "strv.h"
07630cea
LP
31#include "utf8.h"
32#include "util.h"
75683450
LP
33
34static int add_locales_from_archive(Set *locales) {
35 /* Stolen from glibc... */
36
37 struct locarhead {
38 uint32_t magic;
39 /* Serial number. */
40 uint32_t serial;
41 /* Name hash table. */
42 uint32_t namehash_offset;
43 uint32_t namehash_used;
44 uint32_t namehash_size;
45 /* String table. */
46 uint32_t string_offset;
47 uint32_t string_used;
48 uint32_t string_size;
49 /* Table with locale records. */
50 uint32_t locrectab_offset;
51 uint32_t locrectab_used;
52 uint32_t locrectab_size;
53 /* MD5 sum hash table. */
54 uint32_t sumhash_offset;
55 uint32_t sumhash_used;
56 uint32_t sumhash_size;
57 };
58
59 struct namehashent {
60 /* Hash value of the name. */
61 uint32_t hashval;
62 /* Offset of the name in the string table. */
63 uint32_t name_offset;
64 /* Offset of the locale record. */
65 uint32_t locrec_offset;
66 };
67
68 const struct locarhead *h;
69 const struct namehashent *e;
70 const void *p = MAP_FAILED;
71 _cleanup_close_ int fd = -1;
72 size_t sz = 0;
73 struct stat st;
74 unsigned i;
75 int r;
76
77 fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC);
78 if (fd < 0)
79 return errno == ENOENT ? 0 : -errno;
80
81 if (fstat(fd, &st) < 0)
82 return -errno;
83
84 if (!S_ISREG(st.st_mode))
85 return -EBADMSG;
86
87 if (st.st_size < (off_t) sizeof(struct locarhead))
88 return -EBADMSG;
89
90 p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
91 if (p == MAP_FAILED)
92 return -errno;
93
94 h = (const struct locarhead *) p;
95 if (h->magic != 0xde020109 ||
96 h->namehash_offset + h->namehash_size > st.st_size ||
97 h->string_offset + h->string_size > st.st_size ||
98 h->locrectab_offset + h->locrectab_size > st.st_size ||
99 h->sumhash_offset + h->sumhash_size > st.st_size) {
100 r = -EBADMSG;
101 goto finish;
102 }
103
104 e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset);
105 for (i = 0; i < h->namehash_size; i++) {
106 char *z;
107
108 if (e[i].locrec_offset == 0)
109 continue;
110
111 if (!utf8_is_valid((char*) p + e[i].name_offset))
112 continue;
113
114 z = strdup((char*) p + e[i].name_offset);
115 if (!z) {
116 r = -ENOMEM;
117 goto finish;
118 }
119
120 r = set_consume(locales, z);
121 if (r < 0)
122 goto finish;
123 }
124
125 r = 0;
126
127 finish:
128 if (p != MAP_FAILED)
129 munmap((void*) p, sz);
130
131 return r;
132}
133
134static int add_locales_from_libdir (Set *locales) {
135 _cleanup_closedir_ DIR *dir = NULL;
136 struct dirent *entry;
137 int r;
138
139 dir = opendir("/usr/lib/locale");
140 if (!dir)
141 return errno == ENOENT ? 0 : -errno;
142
143 FOREACH_DIRENT(entry, dir, return -errno) {
144 char *z;
145
146 if (entry->d_type != DT_DIR)
147 continue;
148
149 z = strdup(entry->d_name);
150 if (!z)
151 return -ENOMEM;
152
153 r = set_consume(locales, z);
154 if (r < 0 && r != -EEXIST)
155 return r;
156 }
157
158 return 0;
159}
160
161int get_locales(char ***ret) {
162 _cleanup_set_free_ Set *locales = NULL;
163 _cleanup_strv_free_ char **l = NULL;
164 int r;
165
d5099efc 166 locales = set_new(&string_hash_ops);
75683450
LP
167 if (!locales)
168 return -ENOMEM;
169
170 r = add_locales_from_archive(locales);
171 if (r < 0 && r != -ENOENT)
172 return r;
173
174 r = add_locales_from_libdir(locales);
175 if (r < 0)
176 return r;
177
178 l = set_get_strv(locales);
179 if (!l)
180 return -ENOMEM;
181
182 strv_sort(l);
183
184 *ret = l;
185 l = NULL;
186
187 return 0;
188}
189
190bool locale_is_valid(const char *name) {
191
192 if (isempty(name))
193 return false;
194
195 if (strlen(name) >= 128)
196 return false;
197
198 if (!utf8_is_valid(name))
199 return false;
200
ae6c3cc0 201 if (!filename_is_valid(name))
75683450
LP
202 return false;
203
204 if (!string_is_safe(name))
205 return false;
206
207 return true;
208}
a3428668
MS
209
210static const char * const locale_variable_table[_VARIABLE_LC_MAX] = {
211 [VARIABLE_LANG] = "LANG",
212 [VARIABLE_LANGUAGE] = "LANGUAGE",
213 [VARIABLE_LC_CTYPE] = "LC_CTYPE",
214 [VARIABLE_LC_NUMERIC] = "LC_NUMERIC",
215 [VARIABLE_LC_TIME] = "LC_TIME",
216 [VARIABLE_LC_COLLATE] = "LC_COLLATE",
217 [VARIABLE_LC_MONETARY] = "LC_MONETARY",
218 [VARIABLE_LC_MESSAGES] = "LC_MESSAGES",
219 [VARIABLE_LC_PAPER] = "LC_PAPER",
220 [VARIABLE_LC_NAME] = "LC_NAME",
221 [VARIABLE_LC_ADDRESS] = "LC_ADDRESS",
222 [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE",
223 [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT",
224 [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
225};
226
227DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable);