1 /* $FreeBSD: stable/12/lib/libc/iconv/citrus_esdb.c 326193 2017-11-25 17:12:48Z pfg $ */
2 /* $NetBSD: citrus_esdb.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */
3
4 /*-
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * Copyright (c)2003 Citrus Project,
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #include <sys/types.h>
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <paths.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "citrus_namespace.h"
45 #include "citrus_types.h"
46 #include "citrus_bcs.h"
47 #include "citrus_region.h"
48 #include "citrus_memstream.h"
49 #include "citrus_mmap.h"
50 #include "citrus_lookup.h"
51 #include "citrus_db.h"
52 #include "citrus_db_hash.h"
53 #include "citrus_esdb.h"
54 #include "citrus_esdb_file.h"
55
56 #define ESDB_DIR "esdb.dir"
57 #define ESDB_ALIAS "esdb.alias"
58
59 /*
60 * _citrus_esdb_alias:
61 * resolve encoding scheme name aliases.
62 */
63 const char *
_citrus_esdb_alias(const char * esname,char * buf,size_t bufsize)64 _citrus_esdb_alias(const char *esname, char *buf, size_t bufsize)
65 {
66
67 return (_lookup_alias(_PATH_ESDB "/" ESDB_ALIAS, esname, buf, bufsize,
68 _LOOKUP_CASE_IGNORE));
69 }
70
71
72 /*
73 * conv_esdb:
74 * external representation -> local structure.
75 */
76 static int
conv_esdb(struct _citrus_esdb * esdb,struct _region * fr)77 conv_esdb(struct _citrus_esdb *esdb, struct _region *fr)
78 {
79 struct _citrus_db *db;
80 const char *str;
81 char buf[100];
82 uint32_t csid, i, num_charsets, tmp, version;
83 int ret;
84
85 /* open db */
86 ret = _db_open(&db, fr, _CITRUS_ESDB_MAGIC, &_db_hash_std, NULL);
87 if (ret)
88 goto err0;
89
90 /* check version */
91 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_VERSION, &version, NULL);
92 if (ret)
93 goto err1;
94 switch (version) {
95 case 0x00000001:
96 /* current version */
97 /* initial version */
98 break;
99 default:
100 ret = EFTYPE;
101 goto err1;
102 }
103
104 /* get encoding/variable */
105 ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_ENCODING, &str, NULL);
106 if (ret)
107 goto err1;
108 esdb->db_encname = strdup(str);
109 if (esdb->db_encname == NULL) {
110 ret = errno;
111 goto err1;
112 }
113
114 esdb->db_len_variable = 0;
115 esdb->db_variable = NULL;
116 ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_VARIABLE, &str, NULL);
117 if (ret == 0) {
118 esdb->db_len_variable = strlen(str) + 1;
119 esdb->db_variable = strdup(str);
120 if (esdb->db_variable == NULL) {
121 ret = errno;
122 goto err2;
123 }
124 } else if (ret != ENOENT)
125 goto err2;
126
127 /* get number of charsets */
128 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_NUM_CHARSETS,
129 &num_charsets, NULL);
130 if (ret)
131 goto err3;
132 esdb->db_num_charsets = num_charsets;
133
134 /* get invalid character */
135 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_INVALID, &tmp, NULL);
136 if (ret == 0) {
137 esdb->db_use_invalid = 1;
138 esdb->db_invalid = tmp;
139 } else if (ret == ENOENT)
140 esdb->db_use_invalid = 0;
141 else
142 goto err3;
143
144 /* get charsets */
145 esdb->db_charsets = malloc(num_charsets * sizeof(*esdb->db_charsets));
146 if (esdb->db_charsets == NULL) {
147 ret = errno;
148 goto err3;
149 }
150 for (i = 0; i < num_charsets; i++) {
151 snprintf(buf, sizeof(buf),
152 _CITRUS_ESDB_SYM_CSID_PREFIX "%d", i);
153 ret = _db_lookup32_by_s(db, buf, &csid, NULL);
154 if (ret)
155 goto err4;
156 esdb->db_charsets[i].ec_csid = csid;
157
158 snprintf(buf, sizeof(buf),
159 _CITRUS_ESDB_SYM_CSNAME_PREFIX "%d", i);
160 ret = _db_lookupstr_by_s(db, buf, &str, NULL);
161 if (ret)
162 goto err4;
163 esdb->db_charsets[i].ec_csname = strdup(str);
164 if (esdb->db_charsets[i].ec_csname == NULL) {
165 ret = errno;
166 goto err4;
167 }
168 }
169
170 _db_close(db);
171 return (0);
172
173 err4:
174 for (; i > 0; i--)
175 free(esdb->db_charsets[i - 1].ec_csname);
176 free(esdb->db_charsets);
177 err3:
178 free(esdb->db_variable);
179 err2:
180 free(esdb->db_encname);
181 err1:
182 _db_close(db);
183 if (ret == ENOENT)
184 ret = EFTYPE;
185 err0:
186 return (ret);
187 }
188
189 /*
190 * _citrus_esdb_open:
191 * open an ESDB file.
192 */
193 int
_citrus_esdb_open(struct _citrus_esdb * db,const char * esname)194 _citrus_esdb_open(struct _citrus_esdb *db, const char *esname)
195 {
196 struct _region fr;
197 const char *realname, *encfile;
198 char buf1[PATH_MAX], buf2[PATH_MAX], path[PATH_MAX];
199 int ret;
200
201 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_ALIAS);
202 realname = _lookup_alias(path, esname, buf1, sizeof(buf1),
203 _LOOKUP_CASE_IGNORE);
204
205 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_DIR);
206 encfile = _lookup_simple(path, realname, buf2, sizeof(buf2),
207 _LOOKUP_CASE_IGNORE);
208 if (encfile == NULL)
209 return (ENOENT);
210
211 /* open file */
212 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, encfile);
213 ret = _map_file(&fr, path);
214 if (ret)
215 return (ret);
216
217 ret = conv_esdb(db, &fr);
218
219 _unmap_file(&fr);
220
221 return (ret);
222 }
223
224 /*
225 * _citrus_esdb_close:
226 * free an ESDB.
227 */
228 void
_citrus_esdb_close(struct _citrus_esdb * db)229 _citrus_esdb_close(struct _citrus_esdb *db)
230 {
231
232 for (int i = 0; i < db->db_num_charsets; i++)
233 free(db->db_charsets[i].ec_csname);
234 db->db_num_charsets = 0;
235 free(db->db_charsets); db->db_charsets = NULL;
236 free(db->db_encname); db->db_encname = NULL;
237 db->db_len_variable = 0;
238 free(db->db_variable); db->db_variable = NULL;
239 }
240
241 /*
242 * _citrus_esdb_free_list:
243 * free the list.
244 */
245 void
_citrus_esdb_free_list(char ** list,size_t num)246 _citrus_esdb_free_list(char **list, size_t num)
247 {
248
249 for (size_t i = 0; i < num; i++)
250 free(list[i]);
251 free(list);
252 }
253
254 /*
255 * _citrus_esdb_get_list:
256 * get esdb entries.
257 */
258 int
_citrus_esdb_get_list(char *** rlist,size_t * rnum,bool sorted)259 _citrus_esdb_get_list(char ***rlist, size_t *rnum, bool sorted)
260 {
261 struct _citrus_lookup *cla, *cld;
262 struct _region key, data;
263 char **list, **q;
264 char buf[PATH_MAX];
265 size_t num;
266 int ret;
267
268 ret = _lookup_seq_open(&cla, _PATH_ESDB "/" ESDB_ALIAS,
269 _LOOKUP_CASE_IGNORE);
270 if (ret)
271 goto quit0;
272
273 ret = _lookup_seq_open(&cld, _PATH_ESDB "/" ESDB_DIR,
274 _LOOKUP_CASE_IGNORE);
275 if (ret)
276 goto quit1;
277
278 /* count number of entries */
279 num = _lookup_get_num_entries(cla) + _lookup_get_num_entries(cld);
280
281 _lookup_seq_rewind(cla);
282 _lookup_seq_rewind(cld);
283
284 /* allocate list pointer space */
285 list = malloc(num * sizeof(char *));
286 num = 0;
287 if (list == NULL) {
288 ret = errno;
289 goto quit3;
290 }
291
292 /* get alias entries */
293 while ((ret = _lookup_seq_next(cla, &key, &data)) == 0) {
294 /* XXX: sorted? */
295 snprintf(buf, sizeof(buf), "%.*s/%.*s",
296 (int)_region_size(&data),
297 (const char *)_region_head(&data),
298 (int)_region_size(&key),
299 (const char *)_region_head(&key));
300 _bcs_convert_to_upper(buf);
301 list[num] = strdup(buf);
302 if (list[num] == NULL) {
303 ret = errno;
304 goto quit3;
305 }
306 num++;
307 }
308 if (ret != ENOENT)
309 goto quit3;
310 /* get dir entries */
311 while ((ret = _lookup_seq_next(cld, &key, &data)) == 0) {
312 if (!sorted)
313 snprintf(buf, sizeof(buf), "%.*s",
314 (int)_region_size(&key),
315 (const char *)_region_head(&key));
316 else {
317 /* check duplicated entry */
318 char *p;
319 char buf1[PATH_MAX];
320
321 snprintf(buf1, sizeof(buf1), "%.*s",
322 (int)_region_size(&data),
323 (const char *)_region_head(&data));
324 if ((p = strchr(buf1, '/')) != NULL)
325 memmove(buf1, p + 1, strlen(p) - 1);
326 if ((p = strstr(buf1, ".esdb")) != NULL)
327 *p = '\0';
328 snprintf(buf, sizeof(buf), "%s/%.*s", buf1,
329 (int)_region_size(&key),
330 (const char *)_region_head(&key));
331 }
332 _bcs_convert_to_upper(buf);
333 ret = _lookup_seq_lookup(cla, buf, NULL);
334 if (ret) {
335 if (ret != ENOENT)
336 goto quit3;
337 /* not duplicated */
338 list[num] = strdup(buf);
339 if (list[num] == NULL) {
340 ret = errno;
341 goto quit3;
342 }
343 num++;
344 }
345 }
346 if (ret != ENOENT)
347 goto quit3;
348
349 ret = 0;
350 /* XXX: why reallocing the list space posteriorly?
351 shouldn't be done earlier? */
352 q = reallocarray(list, num, sizeof(char *));
353 if (!q) {
354 ret = ENOMEM;
355 goto quit3;
356 }
357 list = q;
358 *rlist = list;
359 *rnum = num;
360 quit3:
361 if (ret)
362 _citrus_esdb_free_list(list, num);
363 _lookup_seq_close(cld);
364 quit1:
365 _lookup_seq_close(cla);
366 quit0:
367 return (ret);
368 }
369