1219019Sgabor/* $FreeBSD: releng/10.2/lib/libc/iconv/citrus_lookup.c 264497 2014-04-15 09:49:44Z tijl $ */ 2264497Stijl/* $NetBSD: citrus_lookup.c,v 1.7 2012/05/04 16:45:05 joerg 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 <dirent.h> 35219019Sgabor#include <errno.h> 36219019Sgabor#include <fcntl.h> 37219019Sgabor#include <limits.h> 38219019Sgabor#include <paths.h> 39219019Sgabor#include <stdio.h> 40219019Sgabor#include <stdlib.h> 41219019Sgabor#include <string.h> 42219019Sgabor#include <unistd.h> 43219019Sgabor 44219019Sgabor#include "citrus_namespace.h" 45219019Sgabor#include "citrus_bcs.h" 46219019Sgabor#include "citrus_region.h" 47219019Sgabor#include "citrus_memstream.h" 48219019Sgabor#include "citrus_mmap.h" 49219019Sgabor#include "citrus_db.h" 50219019Sgabor#include "citrus_db_hash.h" 51219019Sgabor#include "citrus_lookup.h" 52219019Sgabor#include "citrus_lookup_file.h" 53219019Sgabor 54219019Sgaborstruct _citrus_lookup { 55219019Sgabor union { 56219019Sgabor struct { 57219019Sgabor struct _citrus_db *db; 58219019Sgabor struct _citrus_region file; 59219019Sgabor int num, idx; 60219019Sgabor struct _db_locator locator; 61219019Sgabor } db; 62219019Sgabor struct { 63219019Sgabor struct _region r; 64219019Sgabor struct _memstream ms; 65219019Sgabor } plain; 66219019Sgabor } u; 67219019Sgabor#define cl_db u.db.db 68219019Sgabor#define cl_dbidx u.db.idx 69219019Sgabor#define cl_dbfile u.db.file 70219019Sgabor#define cl_dbnum u.db.num 71219019Sgabor#define cl_dblocator u.db.locator 72219019Sgabor#define cl_plainr u.plain.r 73219019Sgabor#define cl_plainms u.plain.ms 74219019Sgabor int cl_ignore_case; 75219019Sgabor int cl_rewind; 76219019Sgabor char *cl_key; 77219019Sgabor size_t cl_keylen; 78219019Sgabor int (*cl_next)(struct _citrus_lookup *, struct _region *, 79219019Sgabor struct _region *); 80219019Sgabor int (*cl_lookup)(struct _citrus_lookup *, const char *, 81219019Sgabor struct _region *); 82219019Sgabor int (*cl_num_entries)(struct _citrus_lookup *); 83219019Sgabor void (*cl_close)(struct _citrus_lookup *); 84219019Sgabor}; 85219019Sgabor 86219019Sgaborstatic int 87219019Sgaborseq_get_num_entries_db(struct _citrus_lookup *cl) 88219019Sgabor{ 89219019Sgabor 90219019Sgabor return (cl->cl_dbnum); 91219019Sgabor} 92219019Sgabor 93219019Sgaborstatic int 94219019Sgaborseq_next_db(struct _citrus_lookup *cl, struct _region *key, 95219019Sgabor struct _region *data) 96219019Sgabor{ 97219019Sgabor 98219019Sgabor if (cl->cl_key) { 99219019Sgabor if (key) 100219019Sgabor _region_init(key, cl->cl_key, cl->cl_keylen); 101219019Sgabor return (_db_lookup_by_s(cl->cl_db, cl->cl_key, data, 102219019Sgabor &cl->cl_dblocator)); 103219019Sgabor } 104219019Sgabor 105219019Sgabor if (cl->cl_rewind) { 106219019Sgabor cl->cl_dbidx = 0; 107219019Sgabor } 108219019Sgabor cl->cl_rewind = 0; 109219019Sgabor if (cl->cl_dbidx >= cl->cl_dbnum) 110219019Sgabor return (ENOENT); 111219019Sgabor 112219019Sgabor return (_db_get_entry(cl->cl_db, cl->cl_dbidx++, key, data)); 113219019Sgabor} 114219019Sgabor 115219019Sgaborstatic int 116219019Sgaborseq_lookup_db(struct _citrus_lookup *cl, const char *key, struct _region *data) 117219019Sgabor{ 118219019Sgabor 119219019Sgabor cl->cl_rewind = 0; 120219019Sgabor free(cl->cl_key); 121219019Sgabor cl->cl_key = strdup(key); 122219019Sgabor if (cl->cl_ignore_case) 123219019Sgabor _bcs_convert_to_lower(cl->cl_key); 124219019Sgabor cl->cl_keylen = strlen(cl->cl_key); 125219019Sgabor _db_locator_init(&cl->cl_dblocator); 126219019Sgabor return (_db_lookup_by_s(cl->cl_db, cl->cl_key, data, 127219019Sgabor &cl->cl_dblocator)); 128219019Sgabor} 129219019Sgabor 130219019Sgaborstatic void 131219019Sgaborseq_close_db(struct _citrus_lookup *cl) 132219019Sgabor{ 133219019Sgabor 134219019Sgabor _db_close(cl->cl_db); 135219019Sgabor _unmap_file(&cl->cl_dbfile); 136219019Sgabor} 137219019Sgabor 138219019Sgaborstatic int 139219019Sgaborseq_open_db(struct _citrus_lookup *cl, const char *name) 140219019Sgabor{ 141219019Sgabor struct _region r; 142219019Sgabor char path[PATH_MAX]; 143219019Sgabor int ret; 144219019Sgabor 145219019Sgabor snprintf(path, sizeof(path), "%s.db", name); 146219019Sgabor ret = _map_file(&r, path); 147219019Sgabor if (ret) 148219019Sgabor return (ret); 149219019Sgabor 150219019Sgabor ret = _db_open(&cl->cl_db, &r, _CITRUS_LOOKUP_MAGIC, 151219019Sgabor _db_hash_std, NULL); 152219019Sgabor if (ret) { 153219019Sgabor _unmap_file(&r); 154219019Sgabor return (ret); 155219019Sgabor } 156219019Sgabor 157219019Sgabor cl->cl_dbfile = r; 158219019Sgabor cl->cl_dbnum = _db_get_num_entries(cl->cl_db); 159219019Sgabor cl->cl_dbidx = 0; 160219019Sgabor cl->cl_rewind = 1; 161219019Sgabor cl->cl_lookup = &seq_lookup_db; 162219019Sgabor cl->cl_next = &seq_next_db; 163219019Sgabor cl->cl_num_entries = &seq_get_num_entries_db; 164219019Sgabor cl->cl_close = &seq_close_db; 165219019Sgabor 166219019Sgabor return (0); 167219019Sgabor} 168219019Sgabor 169219019Sgabor#define T_COMM '#' 170219019Sgaborstatic int 171219019Sgaborseq_next_plain(struct _citrus_lookup *cl, struct _region *key, 172219019Sgabor struct _region *data) 173219019Sgabor{ 174219019Sgabor const char *p, *q; 175219019Sgabor size_t len; 176219019Sgabor 177219019Sgabor if (cl->cl_rewind) 178219019Sgabor _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 179219019Sgabor cl->cl_rewind = 0; 180219019Sgabor 181219019Sgaborretry: 182219019Sgabor p = _memstream_getln(&cl->cl_plainms, &len); 183219019Sgabor if (p == NULL) 184219019Sgabor return (ENOENT); 185219019Sgabor /* ignore comment */ 186219019Sgabor q = memchr(p, T_COMM, len); 187219019Sgabor if (q) { 188219019Sgabor len = q - p; 189219019Sgabor } 190219019Sgabor /* ignore trailing spaces */ 191219019Sgabor _bcs_trunc_rws_len(p, &len); 192219019Sgabor p = _bcs_skip_ws_len(p, &len); 193219019Sgabor q = _bcs_skip_nonws_len(p, &len); 194219019Sgabor if (p == q) 195219019Sgabor goto retry; 196219019Sgabor if (cl->cl_key && ((size_t)(q - p) != cl->cl_keylen || 197219019Sgabor memcmp(p, cl->cl_key, (size_t)(q - p)) != 0)) 198219019Sgabor goto retry; 199219019Sgabor 200219019Sgabor /* found a entry */ 201219019Sgabor if (key) 202219019Sgabor _region_init(key, __DECONST(void *, p), (size_t)(q - p)); 203219019Sgabor p = _bcs_skip_ws_len(q, &len); 204219019Sgabor if (data) 205219019Sgabor _region_init(data, len ? __DECONST(void *, p) : NULL, len); 206219019Sgabor 207219019Sgabor return (0); 208219019Sgabor} 209219019Sgabor 210219019Sgaborstatic int 211219019Sgaborseq_get_num_entries_plain(struct _citrus_lookup *cl) 212219019Sgabor{ 213219019Sgabor int num; 214219019Sgabor 215219019Sgabor num = 0; 216219019Sgabor while (seq_next_plain(cl, NULL, NULL) == 0) 217219019Sgabor num++; 218219019Sgabor 219219019Sgabor return (num); 220219019Sgabor} 221219019Sgabor 222219019Sgaborstatic int 223219019Sgaborseq_lookup_plain(struct _citrus_lookup *cl, const char *key, 224219019Sgabor struct _region *data) 225219019Sgabor{ 226219019Sgabor size_t len; 227219019Sgabor const char *p; 228219019Sgabor 229219019Sgabor cl->cl_rewind = 0; 230219019Sgabor free(cl->cl_key); 231219019Sgabor cl->cl_key = strdup(key); 232219019Sgabor if (cl->cl_ignore_case) 233219019Sgabor _bcs_convert_to_lower(cl->cl_key); 234219019Sgabor cl->cl_keylen = strlen(cl->cl_key); 235219019Sgabor _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 236219019Sgabor p = _memstream_matchline(&cl->cl_plainms, cl->cl_key, &len, 0); 237219019Sgabor if (p == NULL) 238219019Sgabor return (ENOENT); 239219019Sgabor if (data) 240219019Sgabor _region_init(data, __DECONST(void *, p), len); 241219019Sgabor 242219019Sgabor return (0); 243219019Sgabor} 244219019Sgabor 245219019Sgaborstatic void 246219019Sgaborseq_close_plain(struct _citrus_lookup *cl) 247219019Sgabor{ 248219019Sgabor 249219019Sgabor _unmap_file(&cl->cl_plainr); 250219019Sgabor} 251219019Sgabor 252219019Sgaborstatic int 253219019Sgaborseq_open_plain(struct _citrus_lookup *cl, const char *name) 254219019Sgabor{ 255219019Sgabor int ret; 256219019Sgabor 257219019Sgabor /* open read stream */ 258219019Sgabor ret = _map_file(&cl->cl_plainr, name); 259219019Sgabor if (ret) 260219019Sgabor return (ret); 261219019Sgabor 262219019Sgabor cl->cl_rewind = 1; 263219019Sgabor cl->cl_next = &seq_next_plain; 264219019Sgabor cl->cl_lookup = &seq_lookup_plain; 265219019Sgabor cl->cl_num_entries = &seq_get_num_entries_plain; 266219019Sgabor cl->cl_close = &seq_close_plain; 267219019Sgabor 268219019Sgabor return (0); 269219019Sgabor} 270219019Sgabor 271219019Sgaborint 272219019Sgabor_citrus_lookup_seq_open(struct _citrus_lookup **rcl, const char *name, 273219019Sgabor int ignore_case) 274219019Sgabor{ 275219019Sgabor int ret; 276219019Sgabor struct _citrus_lookup *cl; 277219019Sgabor 278219019Sgabor cl = malloc(sizeof(*cl)); 279219019Sgabor if (cl == NULL) 280219019Sgabor return (errno); 281219019Sgabor 282219019Sgabor cl->cl_key = NULL; 283219019Sgabor cl->cl_keylen = 0; 284219019Sgabor cl->cl_ignore_case = ignore_case; 285219019Sgabor ret = seq_open_db(cl, name); 286219019Sgabor if (ret == ENOENT) 287219019Sgabor ret = seq_open_plain(cl, name); 288219019Sgabor if (!ret) 289219019Sgabor *rcl = cl; 290219019Sgabor else 291219019Sgabor free(cl); 292219019Sgabor 293219019Sgabor return (ret); 294219019Sgabor} 295219019Sgabor 296219019Sgaborvoid 297219019Sgabor_citrus_lookup_seq_rewind(struct _citrus_lookup *cl) 298219019Sgabor{ 299219019Sgabor 300219019Sgabor cl->cl_rewind = 1; 301219019Sgabor free(cl->cl_key); 302219019Sgabor cl->cl_key = NULL; 303219019Sgabor cl->cl_keylen = 0; 304219019Sgabor} 305219019Sgabor 306219019Sgaborint 307219019Sgabor_citrus_lookup_seq_next(struct _citrus_lookup *cl, 308219019Sgabor struct _region *key, struct _region *data) 309219019Sgabor{ 310219019Sgabor 311219019Sgabor return ((*cl->cl_next)(cl, key, data)); 312219019Sgabor} 313219019Sgabor 314219019Sgaborint 315219019Sgabor_citrus_lookup_seq_lookup(struct _citrus_lookup *cl, const char *key, 316219019Sgabor struct _region *data) 317219019Sgabor{ 318219019Sgabor 319219019Sgabor return ((*cl->cl_lookup)(cl, key, data)); 320219019Sgabor} 321219019Sgabor 322219019Sgaborint 323219019Sgabor_citrus_lookup_get_number_of_entries(struct _citrus_lookup *cl) 324219019Sgabor{ 325219019Sgabor 326219019Sgabor return ((*cl->cl_num_entries)(cl)); 327219019Sgabor} 328219019Sgabor 329219019Sgaborvoid 330219019Sgabor_citrus_lookup_seq_close(struct _citrus_lookup *cl) 331219019Sgabor{ 332219019Sgabor 333219019Sgabor free(cl->cl_key); 334219019Sgabor (*cl->cl_close)(cl); 335219019Sgabor free(cl); 336219019Sgabor} 337219019Sgabor 338219019Sgaborchar * 339219019Sgabor_citrus_lookup_simple(const char *name, const char *key, 340219019Sgabor char *linebuf, size_t linebufsize, int ignore_case) 341219019Sgabor{ 342219019Sgabor struct _citrus_lookup *cl; 343219019Sgabor struct _region data; 344219019Sgabor int ret; 345219019Sgabor 346219019Sgabor ret = _citrus_lookup_seq_open(&cl, name, ignore_case); 347219019Sgabor if (ret) 348219019Sgabor return (NULL); 349219019Sgabor 350219019Sgabor ret = _citrus_lookup_seq_lookup(cl, key, &data); 351219019Sgabor if (ret) { 352219019Sgabor _citrus_lookup_seq_close(cl); 353219019Sgabor return (NULL); 354219019Sgabor } 355219019Sgabor 356219019Sgabor snprintf(linebuf, linebufsize, "%.*s", (int)_region_size(&data), 357219019Sgabor (const char *)_region_head(&data)); 358219019Sgabor 359219019Sgabor _citrus_lookup_seq_close(cl); 360219019Sgabor 361219019Sgabor return (linebuf); 362219019Sgabor} 363