1 /* $NetBSD: citrus_esdb.c,v 1.5 2008/02/09 14:56:20 junyoung Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause 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 <sys/cdefs.h> 32 #include <sys/types.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <paths.h> 38 #include <stdbool.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include "citrus_namespace.h" 44 #include "citrus_types.h" 45 #include "citrus_bcs.h" 46 #include "citrus_region.h" 47 #include "citrus_memstream.h" 48 #include "citrus_mmap.h" 49 #include "citrus_lookup.h" 50 #include "citrus_db.h" 51 #include "citrus_db_hash.h" 52 #include "citrus_esdb.h" 53 #include "citrus_esdb_file.h" 54 55 #define ESDB_DIR "esdb.dir" 56 #define ESDB_ALIAS "esdb.alias" 57 58 /* 59 * _citrus_esdb_alias: 60 * resolve encoding scheme name aliases. 61 */ 62 const char * 63 _citrus_esdb_alias(const char *esname, char *buf, size_t bufsize) 64 { 65 66 return (_lookup_alias(_PATH_ESDB "/" ESDB_ALIAS, esname, buf, bufsize, 67 _LOOKUP_CASE_IGNORE)); 68 } 69 70 71 /* 72 * conv_esdb: 73 * external representation -> local structure. 74 */ 75 static int 76 conv_esdb(struct _citrus_esdb *esdb, struct _region *fr) 77 { 78 struct _citrus_db *db; 79 const char *str; 80 char buf[100]; 81 uint32_t csid, i, num_charsets, tmp, version; 82 int ret; 83 84 /* open db */ 85 ret = _db_open(&db, fr, _CITRUS_ESDB_MAGIC, &_db_hash_std, NULL); 86 if (ret) 87 goto err0; 88 89 /* check version */ 90 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_VERSION, &version, NULL); 91 if (ret) 92 goto err1; 93 switch (version) { 94 case 0x00000001: 95 /* current version */ 96 /* initial version */ 97 break; 98 default: 99 ret = EFTYPE; 100 goto err1; 101 } 102 103 /* get encoding/variable */ 104 ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_ENCODING, &str, NULL); 105 if (ret) 106 goto err1; 107 esdb->db_encname = strdup(str); 108 if (esdb->db_encname == NULL) { 109 ret = errno; 110 goto err1; 111 } 112 113 esdb->db_len_variable = 0; 114 esdb->db_variable = NULL; 115 ret = _db_lookupstr_by_s(db, _CITRUS_ESDB_SYM_VARIABLE, &str, NULL); 116 if (ret == 0) { 117 esdb->db_len_variable = strlen(str) + 1; 118 esdb->db_variable = strdup(str); 119 if (esdb->db_variable == NULL) { 120 ret = errno; 121 goto err2; 122 } 123 } else if (ret != ENOENT) 124 goto err2; 125 126 /* get number of charsets */ 127 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_NUM_CHARSETS, 128 &num_charsets, NULL); 129 if (ret) 130 goto err3; 131 esdb->db_num_charsets = num_charsets; 132 133 /* get invalid character */ 134 ret = _db_lookup32_by_s(db, _CITRUS_ESDB_SYM_INVALID, &tmp, NULL); 135 if (ret == 0) { 136 esdb->db_use_invalid = 1; 137 esdb->db_invalid = tmp; 138 } else if (ret == ENOENT) 139 esdb->db_use_invalid = 0; 140 else 141 goto err3; 142 143 /* get charsets */ 144 esdb->db_charsets = malloc(num_charsets * sizeof(*esdb->db_charsets)); 145 if (esdb->db_charsets == NULL) { 146 ret = errno; 147 goto err3; 148 } 149 for (i = 0; i < num_charsets; i++) { 150 snprintf(buf, sizeof(buf), 151 _CITRUS_ESDB_SYM_CSID_PREFIX "%d", i); 152 ret = _db_lookup32_by_s(db, buf, &csid, NULL); 153 if (ret) 154 goto err4; 155 esdb->db_charsets[i].ec_csid = csid; 156 157 snprintf(buf, sizeof(buf), 158 _CITRUS_ESDB_SYM_CSNAME_PREFIX "%d", i); 159 ret = _db_lookupstr_by_s(db, buf, &str, NULL); 160 if (ret) 161 goto err4; 162 esdb->db_charsets[i].ec_csname = strdup(str); 163 if (esdb->db_charsets[i].ec_csname == NULL) { 164 ret = errno; 165 goto err4; 166 } 167 } 168 169 _db_close(db); 170 return (0); 171 172 err4: 173 for (; i > 0; i--) 174 free(esdb->db_charsets[i - 1].ec_csname); 175 free(esdb->db_charsets); 176 err3: 177 free(esdb->db_variable); 178 err2: 179 free(esdb->db_encname); 180 err1: 181 _db_close(db); 182 if (ret == ENOENT) 183 ret = EFTYPE; 184 err0: 185 return (ret); 186 } 187 188 /* 189 * _citrus_esdb_open: 190 * open an ESDB file. 191 */ 192 int 193 _citrus_esdb_open(struct _citrus_esdb *db, const char *esname) 194 { 195 struct _region fr; 196 const char *realname, *encfile; 197 char buf1[PATH_MAX], buf2[PATH_MAX], path[PATH_MAX]; 198 int ret; 199 200 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_ALIAS); 201 realname = _lookup_alias(path, esname, buf1, sizeof(buf1), 202 _LOOKUP_CASE_IGNORE); 203 204 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, ESDB_DIR); 205 encfile = _lookup_simple(path, realname, buf2, sizeof(buf2), 206 _LOOKUP_CASE_IGNORE); 207 if (encfile == NULL) 208 return (ENOENT); 209 210 /* open file */ 211 snprintf(path, sizeof(path), "%s/%s", _PATH_ESDB, encfile); 212 ret = _map_file(&fr, path); 213 if (ret) 214 return (ret); 215 216 ret = conv_esdb(db, &fr); 217 218 _unmap_file(&fr); 219 220 return (ret); 221 } 222 223 /* 224 * _citrus_esdb_close: 225 * free an ESDB. 226 */ 227 void 228 _citrus_esdb_close(struct _citrus_esdb *db) 229 { 230 231 for (int i = 0; i < db->db_num_charsets; i++) 232 free(db->db_charsets[i].ec_csname); 233 db->db_num_charsets = 0; 234 free(db->db_charsets); db->db_charsets = NULL; 235 free(db->db_encname); db->db_encname = NULL; 236 db->db_len_variable = 0; 237 free(db->db_variable); db->db_variable = NULL; 238 } 239 240 /* 241 * _citrus_esdb_free_list: 242 * free the list. 243 */ 244 void 245 _citrus_esdb_free_list(char **list, size_t num) 246 { 247 248 for (size_t i = 0; i < num; i++) 249 free(list[i]); 250 free(list); 251 } 252 253 /* 254 * _citrus_esdb_get_list: 255 * get esdb entries. 256 */ 257 int 258 _citrus_esdb_get_list(char ***rlist, size_t *rnum, bool sorted) 259 { 260 struct _citrus_lookup *cla, *cld; 261 struct _region key, data; 262 char **list, **q; 263 char buf[PATH_MAX]; 264 size_t num; 265 int ret; 266 267 ret = _lookup_seq_open(&cla, _PATH_ESDB "/" ESDB_ALIAS, 268 _LOOKUP_CASE_IGNORE); 269 if (ret) 270 goto quit0; 271 272 ret = _lookup_seq_open(&cld, _PATH_ESDB "/" ESDB_DIR, 273 _LOOKUP_CASE_IGNORE); 274 if (ret) 275 goto quit1; 276 277 /* count number of entries */ 278 num = _lookup_get_num_entries(cla) + _lookup_get_num_entries(cld); 279 280 _lookup_seq_rewind(cla); 281 _lookup_seq_rewind(cld); 282 283 /* allocate list pointer space */ 284 list = malloc(num * sizeof(char *)); 285 num = 0; 286 if (list == NULL) { 287 ret = errno; 288 goto quit3; 289 } 290 291 /* get alias entries */ 292 while ((ret = _lookup_seq_next(cla, &key, &data)) == 0) { 293 /* XXX: sorted? */ 294 snprintf(buf, sizeof(buf), "%.*s/%.*s", 295 (int)_region_size(&data), 296 (const char *)_region_head(&data), 297 (int)_region_size(&key), 298 (const char *)_region_head(&key)); 299 _bcs_convert_to_upper(buf); 300 list[num] = strdup(buf); 301 if (list[num] == NULL) { 302 ret = errno; 303 goto quit3; 304 } 305 num++; 306 } 307 if (ret != ENOENT) 308 goto quit3; 309 /* get dir entries */ 310 while ((ret = _lookup_seq_next(cld, &key, &data)) == 0) { 311 if (!sorted) 312 snprintf(buf, sizeof(buf), "%.*s", 313 (int)_region_size(&key), 314 (const char *)_region_head(&key)); 315 else { 316 /* check duplicated entry */ 317 char *p; 318 char buf1[PATH_MAX]; 319 320 snprintf(buf1, sizeof(buf1), "%.*s", 321 (int)_region_size(&data), 322 (const char *)_region_head(&data)); 323 if ((p = strchr(buf1, '/')) != NULL) 324 memmove(buf1, p + 1, strlen(p) - 1); 325 if ((p = strstr(buf1, ".esdb")) != NULL) 326 *p = '\0'; 327 snprintf(buf, sizeof(buf), "%s/%.*s", buf1, 328 (int)_region_size(&key), 329 (const char *)_region_head(&key)); 330 } 331 _bcs_convert_to_upper(buf); 332 ret = _lookup_seq_lookup(cla, buf, NULL); 333 if (ret) { 334 if (ret != ENOENT) 335 goto quit3; 336 /* not duplicated */ 337 list[num] = strdup(buf); 338 if (list[num] == NULL) { 339 ret = errno; 340 goto quit3; 341 } 342 num++; 343 } 344 } 345 if (ret != ENOENT) 346 goto quit3; 347 348 ret = 0; 349 /* XXX: why reallocing the list space posteriorly? 350 shouldn't be done earlier? */ 351 q = reallocarray(list, num, sizeof(char *)); 352 if (!q) { 353 ret = ENOMEM; 354 goto quit3; 355 } 356 list = q; 357 *rlist = list; 358 *rnum = num; 359 quit3: 360 if (ret) 361 _citrus_esdb_free_list(list, num); 362 _lookup_seq_close(cld); 363 quit1: 364 _lookup_seq_close(cla); 365 quit0: 366 return (ret); 367 } 368