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 */
63const 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 */
76static int
77conv_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
173err4:
174	for (; i > 0; i--)
175		free(esdb->db_charsets[i - 1].ec_csname);
176	free(esdb->db_charsets);
177err3:
178	free(esdb->db_variable);
179err2:
180	free(esdb->db_encname);
181err1:
182	_db_close(db);
183	if (ret == ENOENT)
184		ret = EFTYPE;
185err0:
186	return (ret);
187}
188
189/*
190 * _citrus_esdb_open:
191 *	open an ESDB file.
192 */
193int
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 */
228void
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 */
245void
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 */
258int
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;
360quit3:
361	if (ret)
362		_citrus_esdb_free_list(list, num);
363	_lookup_seq_close(cld);
364quit1:
365	_lookup_seq_close(cla);
366quit0:
367	return (ret);
368}
369