xref: /freebsd/lib/libc/iconv/citrus_esdb.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
1 /* $FreeBSD$ */
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 *
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
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
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
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
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
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