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