1219019Sgabor/* $FreeBSD: stable/11/lib/libc/iconv/citrus_esdb.c 316613 2017-04-07 16:08:04Z pfg $ */
2219019Sgabor/* $NetBSD: citrus_esdb.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c)2003 Citrus Project,
6219019Sgabor * All rights reserved.
7219019Sgabor *
8219019Sgabor * Redistribution and use in source and binary forms, with or without
9219019Sgabor * modification, are permitted provided that the following conditions
10219019Sgabor * are met:
11219019Sgabor * 1. Redistributions of source code must retain the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer.
13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14219019Sgabor *    notice, this list of conditions and the following disclaimer in the
15219019Sgabor *    documentation and/or other materials provided with the distribution.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor */
29219019Sgabor
30219019Sgabor#include <sys/cdefs.h>
31219019Sgabor#include <sys/types.h>
32219019Sgabor
33219019Sgabor#include <assert.h>
34219019Sgabor#include <errno.h>
35219019Sgabor#include <limits.h>
36219019Sgabor#include <paths.h>
37219019Sgabor#include <stdbool.h>
38219019Sgabor#include <stdio.h>
39219019Sgabor#include <stdlib.h>
40219019Sgabor#include <string.h>
41219019Sgabor
42219019Sgabor#include "citrus_namespace.h"
43219019Sgabor#include "citrus_types.h"
44219019Sgabor#include "citrus_bcs.h"
45219019Sgabor#include "citrus_region.h"
46219019Sgabor#include "citrus_memstream.h"
47219019Sgabor#include "citrus_mmap.h"
48219019Sgabor#include "citrus_lookup.h"
49219019Sgabor#include "citrus_db.h"
50219019Sgabor#include "citrus_db_hash.h"
51219019Sgabor#include "citrus_esdb.h"
52219019Sgabor#include "citrus_esdb_file.h"
53219019Sgabor
54219019Sgabor#define ESDB_DIR	"esdb.dir"
55219019Sgabor#define ESDB_ALIAS	"esdb.alias"
56219019Sgabor
57219019Sgabor/*
58219019Sgabor * _citrus_esdb_alias:
59219019Sgabor *	resolve encoding scheme name aliases.
60219019Sgabor */
61219019Sgaborconst char *
62219019Sgabor_citrus_esdb_alias(const char *esname, char *buf, size_t bufsize)
63219019Sgabor{
64219019Sgabor
65219019Sgabor	return (_lookup_alias(_PATH_ESDB "/" ESDB_ALIAS, esname, buf, bufsize,
66219019Sgabor	    _LOOKUP_CASE_IGNORE));
67219019Sgabor}
68219019Sgabor
69219019Sgabor
70219019Sgabor/*
71219019Sgabor * conv_esdb:
72219019Sgabor *	external representation -> local structure.
73219019Sgabor */
74219019Sgaborstatic int
75219019Sgaborconv_esdb(struct _citrus_esdb *esdb, struct _region *fr)
76219019Sgabor{
77219019Sgabor	struct _citrus_db *db;
78219019Sgabor	const char *str;
79219019Sgabor	char buf[100];
80219019Sgabor	uint32_t csid, i, num_charsets, tmp, version;
81219019Sgabor	int ret;
82219019Sgabor
83219019Sgabor	/* open db */
84219019Sgabor	ret = _db_open(&db, fr, _CITRUS_ESDB_MAGIC, &_db_hash_std, NULL);
85219019Sgabor	if (ret)
86219019Sgabor		goto err0;
87219019Sgabor
88219019Sgabor	/* check version */
89219019Sgabor	ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_VERSION, &version, NULL);
90219019Sgabor	if (ret)
91219019Sgabor		goto err1;
92219019Sgabor	switch (version) {
93219019Sgabor	case 0x00000001:
94219019Sgabor		/* current version */
95219019Sgabor		/* initial version */
96219019Sgabor		break;
97219019Sgabor	default:
98219019Sgabor		ret = EFTYPE;
99219019Sgabor		goto err1;
100219019Sgabor	}
101219019Sgabor
102219019Sgabor	/* get encoding/variable */
103219019Sgabor	ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_ENCODING, &str, NULL);
104219019Sgabor	if (ret)
105219019Sgabor		goto err1;
106219019Sgabor	esdb->db_encname = strdup(str);
107219019Sgabor	if (esdb->db_encname == NULL) {
108219019Sgabor		ret = errno;
109219019Sgabor		goto err1;
110219019Sgabor	}
111219019Sgabor
112219019Sgabor	esdb->db_len_variable = 0;
113219019Sgabor	esdb->db_variable = NULL;
114219019Sgabor	ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_VARIABLE, &str, NULL);
115219019Sgabor	if (ret == 0) {
116219019Sgabor		esdb->db_len_variable = strlen(str) + 1;
117219019Sgabor		esdb->db_variable = strdup(str);
118219019Sgabor		if (esdb->db_variable == NULL) {
119219019Sgabor			ret = errno;
120219019Sgabor			goto err2;
121219019Sgabor		}
122219019Sgabor	} else if (ret != ENOENT)
123219019Sgabor		goto err2;
124219019Sgabor
125219019Sgabor	/* get number of charsets */
126219019Sgabor	ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_NUM_CHARSETS,
127219019Sgabor	    &num_charsets, NULL);
128219019Sgabor	if (ret)
129219019Sgabor		goto err3;
130219019Sgabor	esdb->db_num_charsets = num_charsets;
131219019Sgabor
132219019Sgabor	/* get invalid character */
133219019Sgabor	ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_INVALID, &tmp, NULL);
134219019Sgabor	if (ret == 0) {
135219019Sgabor		esdb->db_use_invalid = 1;
136219019Sgabor		esdb->db_invalid = tmp;
137219019Sgabor	} else if (ret == ENOENT)
138219019Sgabor		esdb->db_use_invalid = 0;
139219019Sgabor	else
140219019Sgabor		goto err3;
141219019Sgabor
142219019Sgabor	/* get charsets */
143219019Sgabor	esdb->db_charsets = malloc(num_charsets * sizeof(*esdb->db_charsets));
144219019Sgabor	if (esdb->db_charsets == NULL) {
145219019Sgabor		ret = errno;
146219019Sgabor		goto err3;
147219019Sgabor	}
148219019Sgabor	for (i = 0; i < num_charsets; i++) {
149219019Sgabor		snprintf(buf, sizeof(buf),
150219019Sgabor		    _CITRUS_ESDB_SYM_CSID_PREFIX "%d", i);
151219019Sgabor		ret = _db_lookup32_by_s(db, buf, &csid, NULL);
152219019Sgabor		if (ret)
153219019Sgabor			goto err4;
154219019Sgabor		esdb->db_charsets[i].ec_csid = csid;
155219019Sgabor
156219019Sgabor		snprintf(buf, sizeof(buf),
157219019Sgabor		    _CITRUS_ESDB_SYM_CSNAME_PREFIX "%d", i);
158219019Sgabor		ret = _db_lookupstr_by_s(db, buf, &str, NULL);
159219019Sgabor		if (ret)
160219019Sgabor			goto err4;
161219019Sgabor		esdb->db_charsets[i].ec_csname = strdup(str);
162219019Sgabor		if (esdb->db_charsets[i].ec_csname == NULL) {
163219019Sgabor			ret = errno;
164219019Sgabor			goto err4;
165219019Sgabor		}
166219019Sgabor	}
167219019Sgabor
168219019Sgabor	_db_close(db);
169219019Sgabor	return (0);
170219019Sgabor
171219019Sgaborerr4:
172219019Sgabor	for (; i > 0; i--)
173219019Sgabor		free(esdb->db_charsets[i - 1].ec_csname);
174219019Sgabor	free(esdb->db_charsets);
175219019Sgaborerr3:
176219019Sgabor	free(esdb->db_variable);
177219019Sgaborerr2:
178219019Sgabor	free(esdb->db_encname);
179219019Sgaborerr1:
180219019Sgabor	_db_close(db);
181219019Sgabor	if (ret == ENOENT)
182219019Sgabor		ret = EFTYPE;
183219019Sgaborerr0:
184219019Sgabor	return (ret);
185219019Sgabor}
186219019Sgabor
187219019Sgabor/*
188219019Sgabor * _citrus_esdb_open:
189219019Sgabor *	open an ESDB file.
190219019Sgabor */
191219019Sgaborint
192219019Sgabor_citrus_esdb_open(struct _citrus_esdb *db, const char *esname)
193219019Sgabor{
194219019Sgabor	struct _region fr;
195219019Sgabor	const char *realname, *encfile;
196219019Sgabor	char buf1[PATH_MAX], buf2[PATH_MAX], path[PATH_MAX];
197219019Sgabor	int ret;
198219019Sgabor
199219019Sgabor	snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_ALIAS);
200219019Sgabor	realname = _lookup_alias(path, esname, buf1, sizeof(buf1),
201219019Sgabor	    _LOOKUP_CASE_IGNORE);
202219019Sgabor
203219019Sgabor	snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_DIR);
204219019Sgabor	encfile = _lookup_simple(path, realname, buf2, sizeof(buf2),
205219019Sgabor	    _LOOKUP_CASE_IGNORE);
206219019Sgabor	if (encfile == NULL)
207219019Sgabor		return (ENOENT);
208219019Sgabor
209219019Sgabor	/* open file */
210219019Sgabor	snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, encfile);
211219019Sgabor	ret = _map_file(&fr, path);
212219019Sgabor	if (ret)
213219019Sgabor		return (ret);
214219019Sgabor
215219019Sgabor	ret = conv_esdb(db, &fr);
216219019Sgabor
217219019Sgabor	_unmap_file(&fr);
218219019Sgabor
219219019Sgabor	return (ret);
220219019Sgabor}
221219019Sgabor
222219019Sgabor/*
223219019Sgabor * _citrus_esdb_close:
224219019Sgabor *	free an ESDB.
225219019Sgabor */
226219019Sgaborvoid
227219019Sgabor_citrus_esdb_close(struct _citrus_esdb *db)
228219019Sgabor{
229219019Sgabor
230219019Sgabor	for (int i = 0; i < db->db_num_charsets; i++)
231219019Sgabor		free(db->db_charsets[i].ec_csname);
232219019Sgabor	db->db_num_charsets = 0;
233219019Sgabor	free(db->db_charsets); db->db_charsets = NULL;
234219019Sgabor	free(db->db_encname); db->db_encname = NULL;
235219019Sgabor	db->db_len_variable = 0;
236219019Sgabor	free(db->db_variable); db->db_variable = NULL;
237219019Sgabor}
238219019Sgabor
239219019Sgabor/*
240219019Sgabor * _citrus_esdb_free_list:
241219019Sgabor *	free the list.
242219019Sgabor */
243219019Sgaborvoid
244219019Sgabor_citrus_esdb_free_list(char **list, size_t num)
245219019Sgabor{
246219019Sgabor
247219019Sgabor	for (size_t i = 0; i < num; i++)
248219019Sgabor		free(list[i]);
249219019Sgabor	free(list);
250219019Sgabor}
251219019Sgabor
252219019Sgabor/*
253219019Sgabor * _citrus_esdb_get_list:
254219019Sgabor *	get esdb entries.
255219019Sgabor */
256219019Sgaborint
257219019Sgabor_citrus_esdb_get_list(char ***rlist, size_t *rnum, bool sorted)
258219019Sgabor{
259219019Sgabor	struct _citrus_lookup *cla, *cld;
260219019Sgabor	struct _region key, data;
261219019Sgabor	char **list, **q;
262219019Sgabor	char buf[PATH_MAX];
263219019Sgabor	size_t num;
264219019Sgabor	int ret;
265219019Sgabor
266219019Sgabor	ret = _lookup_seq_open(&cla, _PATH_ESDB "/" ESDB_ALIAS,
267219019Sgabor	    _LOOKUP_CASE_IGNORE);
268219019Sgabor	if (ret)
269219019Sgabor		goto quit0;
270219019Sgabor
271219019Sgabor	ret = _lookup_seq_open(&cld, _PATH_ESDB "/" ESDB_DIR,
272219019Sgabor	    _LOOKUP_CASE_IGNORE);
273219019Sgabor	if (ret)
274219019Sgabor		goto quit1;
275219019Sgabor
276219019Sgabor	/* count number of entries */
277219019Sgabor	num = _lookup_get_num_entries(cla) + _lookup_get_num_entries(cld);
278219019Sgabor
279219019Sgabor	_lookup_seq_rewind(cla);
280219019Sgabor	_lookup_seq_rewind(cld);
281219019Sgabor
282219019Sgabor	/* allocate list pointer space */
283219019Sgabor	list = malloc(num * sizeof(char *));
284219019Sgabor	num = 0;
285219019Sgabor	if (list == NULL) {
286219019Sgabor		ret = errno;
287219019Sgabor		goto quit3;
288219019Sgabor	}
289219019Sgabor
290219019Sgabor	/* get alias entries */
291219019Sgabor	while ((ret = _lookup_seq_next(cla, &key, &data)) == 0) {
292301215Spfg		/* XXX: sorted? */
293301215Spfg		snprintf(buf, sizeof(buf), "%.*s/%.*s",
294301215Spfg		    (int)_region_size(&data),
295301215Spfg		    (const char *)_region_head(&data),
296301215Spfg		    (int)_region_size(&key),
297301215Spfg		    (const char *)_region_head(&key));
298219019Sgabor		_bcs_convert_to_upper(buf);
299219019Sgabor		list[num] = strdup(buf);
300219019Sgabor		if (list[num] == NULL) {
301219019Sgabor			ret = errno;
302219019Sgabor			goto quit3;
303219019Sgabor		}
304219019Sgabor		num++;
305219019Sgabor	}
306219019Sgabor	if (ret != ENOENT)
307219019Sgabor		goto quit3;
308219019Sgabor	/* get dir entries */
309219019Sgabor	while ((ret = _lookup_seq_next(cld, &key, &data)) == 0) {
310219019Sgabor		if (!sorted)
311219019Sgabor			snprintf(buf, sizeof(buf), "%.*s",
312219019Sgabor			    (int)_region_size(&key),
313219019Sgabor			    (const char *)_region_head(&key));
314219019Sgabor		else {
315219019Sgabor			/* check duplicated entry */
316219019Sgabor			char *p;
317219019Sgabor			char buf1[PATH_MAX];
318219019Sgabor
319219019Sgabor			snprintf(buf1, sizeof(buf1), "%.*s",
320219019Sgabor			    (int)_region_size(&data),
321219019Sgabor			    (const char *)_region_head(&data));
322219019Sgabor			if ((p = strchr(buf1, '/')) != NULL)
323290169Sbdrewery				memmove(buf1, p + 1, strlen(p) - 1);
324219019Sgabor			if ((p = strstr(buf1, ".esdb")) != NULL)
325219019Sgabor				*p = '\0';
326219019Sgabor			snprintf(buf, sizeof(buf), "%s/%.*s", buf1,
327219019Sgabor			    (int)_region_size(&key),
328219019Sgabor			    (const char *)_region_head(&key));
329219019Sgabor		}
330219019Sgabor		_bcs_convert_to_upper(buf);
331219019Sgabor		ret = _lookup_seq_lookup(cla, buf, NULL);
332219019Sgabor		if (ret) {
333219019Sgabor			if (ret != ENOENT)
334219019Sgabor				goto quit3;
335219019Sgabor			/* not duplicated */
336219019Sgabor			list[num] = strdup(buf);
337219019Sgabor			if (list[num] == NULL) {
338219019Sgabor				ret = errno;
339219019Sgabor				goto quit3;
340219019Sgabor			}
341219019Sgabor			num++;
342219019Sgabor		}
343219019Sgabor	}
344219019Sgabor	if (ret != ENOENT)
345219019Sgabor		goto quit3;
346219019Sgabor
347219019Sgabor	ret = 0;
348219019Sgabor	/* XXX: why reallocing the list space posteriorly?
349219019Sgabor	    shouldn't be done earlier? */
350316613Spfg	q = reallocarray(list, num, sizeof(char *));
351219019Sgabor	if (!q) {
352219019Sgabor		ret = ENOMEM;
353219019Sgabor		goto quit3;
354219019Sgabor	}
355219019Sgabor	list = q;
356219019Sgabor	*rlist = list;
357219019Sgabor	*rnum = num;
358219019Sgaborquit3:
359219019Sgabor	if (ret)
360219019Sgabor		_citrus_esdb_free_list(list, num);
361219019Sgabor	_lookup_seq_close(cld);
362219019Sgaborquit1:
363219019Sgabor	_lookup_seq_close(cla);
364219019Sgaborquit0:
365219019Sgabor	return (ret);
366219019Sgabor}
367