1/* $FreeBSD$ */ 2/* $NetBSD: citrus_lookup.c,v 1.7 2012/05/04 16:45:05 joerg Exp $ */ 3 4/*- 5 * Copyright (c)2003 Citrus Project, 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31#include <sys/types.h> 32 33#include <assert.h> 34#include <dirent.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <limits.h> 38#include <paths.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <unistd.h> 43 44#include "citrus_namespace.h" 45#include "citrus_bcs.h" 46#include "citrus_region.h" 47#include "citrus_memstream.h" 48#include "citrus_mmap.h" 49#include "citrus_db.h" 50#include "citrus_db_hash.h" 51#include "citrus_lookup.h" 52#include "citrus_lookup_file.h" 53 54struct _citrus_lookup { 55 union { 56 struct { 57 struct _citrus_db *db; 58 struct _citrus_region file; 59 int num, idx; 60 struct _db_locator locator; 61 } db; 62 struct { 63 struct _region r; 64 struct _memstream ms; 65 } plain; 66 } u; 67#define cl_db u.db.db 68#define cl_dbidx u.db.idx 69#define cl_dbfile u.db.file 70#define cl_dbnum u.db.num 71#define cl_dblocator u.db.locator 72#define cl_plainr u.plain.r 73#define cl_plainms u.plain.ms 74 int cl_ignore_case; 75 int cl_rewind; 76 char *cl_key; 77 size_t cl_keylen; 78 int (*cl_next)(struct _citrus_lookup *, struct _region *, 79 struct _region *); 80 int (*cl_lookup)(struct _citrus_lookup *, const char *, 81 struct _region *); 82 int (*cl_num_entries)(struct _citrus_lookup *); 83 void (*cl_close)(struct _citrus_lookup *); 84}; 85 86static int 87seq_get_num_entries_db(struct _citrus_lookup *cl) 88{ 89 90 return (cl->cl_dbnum); 91} 92 93static int 94seq_next_db(struct _citrus_lookup *cl, struct _region *key, 95 struct _region *data) 96{ 97 98 if (cl->cl_key) { 99 if (key) 100 _region_init(key, cl->cl_key, cl->cl_keylen); 101 return (_db_lookup_by_s(cl->cl_db, cl->cl_key, data, 102 &cl->cl_dblocator)); 103 } 104 105 if (cl->cl_rewind) { 106 cl->cl_dbidx = 0; 107 } 108 cl->cl_rewind = 0; 109 if (cl->cl_dbidx >= cl->cl_dbnum) 110 return (ENOENT); 111 112 return (_db_get_entry(cl->cl_db, cl->cl_dbidx++, key, data)); 113} 114 115static int 116seq_lookup_db(struct _citrus_lookup *cl, const char *key, struct _region *data) 117{ 118 119 cl->cl_rewind = 0; 120 free(cl->cl_key); 121 cl->cl_key = strdup(key); 122 if (cl->cl_ignore_case) 123 _bcs_convert_to_lower(cl->cl_key); 124 cl->cl_keylen = strlen(cl->cl_key); 125 _db_locator_init(&cl->cl_dblocator); 126 return (_db_lookup_by_s(cl->cl_db, cl->cl_key, data, 127 &cl->cl_dblocator)); 128} 129 130static void 131seq_close_db(struct _citrus_lookup *cl) 132{ 133 134 _db_close(cl->cl_db); 135 _unmap_file(&cl->cl_dbfile); 136} 137 138static int 139seq_open_db(struct _citrus_lookup *cl, const char *name) 140{ 141 struct _region r; 142 char path[PATH_MAX]; 143 int ret; 144 145 snprintf(path, sizeof(path), "%s.db", name); 146 ret = _map_file(&r, path); 147 if (ret) 148 return (ret); 149 150 ret = _db_open(&cl->cl_db, &r, _CITRUS_LOOKUP_MAGIC, 151 _db_hash_std, NULL); 152 if (ret) { 153 _unmap_file(&r); 154 return (ret); 155 } 156 157 cl->cl_dbfile = r; 158 cl->cl_dbnum = _db_get_num_entries(cl->cl_db); 159 cl->cl_dbidx = 0; 160 cl->cl_rewind = 1; 161 cl->cl_lookup = &seq_lookup_db; 162 cl->cl_next = &seq_next_db; 163 cl->cl_num_entries = &seq_get_num_entries_db; 164 cl->cl_close = &seq_close_db; 165 166 return (0); 167} 168 169#define T_COMM '#' 170static int 171seq_next_plain(struct _citrus_lookup *cl, struct _region *key, 172 struct _region *data) 173{ 174 const char *p, *q; 175 size_t len; 176 177 if (cl->cl_rewind) 178 _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 179 cl->cl_rewind = 0; 180 181retry: 182 p = _memstream_getln(&cl->cl_plainms, &len); 183 if (p == NULL) 184 return (ENOENT); 185 /* ignore comment */ 186 q = memchr(p, T_COMM, len); 187 if (q) { 188 len = q - p; 189 } 190 /* ignore trailing spaces */ 191 _bcs_trunc_rws_len(p, &len); 192 p = _bcs_skip_ws_len(p, &len); 193 q = _bcs_skip_nonws_len(p, &len); 194 if (p == q) 195 goto retry; 196 if (cl->cl_key && ((size_t)(q - p) != cl->cl_keylen || 197 memcmp(p, cl->cl_key, (size_t)(q - p)) != 0)) 198 goto retry; 199 200 /* found a entry */ 201 if (key) 202 _region_init(key, __DECONST(void *, p), (size_t)(q - p)); 203 p = _bcs_skip_ws_len(q, &len); 204 if (data) 205 _region_init(data, len ? __DECONST(void *, p) : NULL, len); 206 207 return (0); 208} 209 210static int 211seq_get_num_entries_plain(struct _citrus_lookup *cl) 212{ 213 int num; 214 215 num = 0; 216 while (seq_next_plain(cl, NULL, NULL) == 0) 217 num++; 218 219 return (num); 220} 221 222static int 223seq_lookup_plain(struct _citrus_lookup *cl, const char *key, 224 struct _region *data) 225{ 226 size_t len; 227 const char *p; 228 229 cl->cl_rewind = 0; 230 free(cl->cl_key); 231 cl->cl_key = strdup(key); 232 if (cl->cl_ignore_case) 233 _bcs_convert_to_lower(cl->cl_key); 234 cl->cl_keylen = strlen(cl->cl_key); 235 _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 236 p = _memstream_matchline(&cl->cl_plainms, cl->cl_key, &len, 0); 237 if (p == NULL) 238 return (ENOENT); 239 if (data) 240 _region_init(data, __DECONST(void *, p), len); 241 242 return (0); 243} 244 245static void 246seq_close_plain(struct _citrus_lookup *cl) 247{ 248 249 _unmap_file(&cl->cl_plainr); 250} 251 252static int 253seq_open_plain(struct _citrus_lookup *cl, const char *name) 254{ 255 int ret; 256 257 /* open read stream */ 258 ret = _map_file(&cl->cl_plainr, name); 259 if (ret) 260 return (ret); 261 262 cl->cl_rewind = 1; 263 cl->cl_next = &seq_next_plain; 264 cl->cl_lookup = &seq_lookup_plain; 265 cl->cl_num_entries = &seq_get_num_entries_plain; 266 cl->cl_close = &seq_close_plain; 267 268 return (0); 269} 270 271int 272_citrus_lookup_seq_open(struct _citrus_lookup **rcl, const char *name, 273 int ignore_case) 274{ 275 int ret; 276 struct _citrus_lookup *cl; 277 278 cl = malloc(sizeof(*cl)); 279 if (cl == NULL) 280 return (errno); 281 282 cl->cl_key = NULL; 283 cl->cl_keylen = 0; 284 cl->cl_ignore_case = ignore_case; 285 ret = seq_open_db(cl, name); 286 if (ret == ENOENT) 287 ret = seq_open_plain(cl, name); 288 if (!ret) 289 *rcl = cl; 290 else 291 free(cl); 292 293 return (ret); 294} 295 296void 297_citrus_lookup_seq_rewind(struct _citrus_lookup *cl) 298{ 299 300 cl->cl_rewind = 1; 301 free(cl->cl_key); 302 cl->cl_key = NULL; 303 cl->cl_keylen = 0; 304} 305 306int 307_citrus_lookup_seq_next(struct _citrus_lookup *cl, 308 struct _region *key, struct _region *data) 309{ 310 311 return ((*cl->cl_next)(cl, key, data)); 312} 313 314int 315_citrus_lookup_seq_lookup(struct _citrus_lookup *cl, const char *key, 316 struct _region *data) 317{ 318 319 return ((*cl->cl_lookup)(cl, key, data)); 320} 321 322int 323_citrus_lookup_get_number_of_entries(struct _citrus_lookup *cl) 324{ 325 326 return ((*cl->cl_num_entries)(cl)); 327} 328 329void 330_citrus_lookup_seq_close(struct _citrus_lookup *cl) 331{ 332 333 free(cl->cl_key); 334 (*cl->cl_close)(cl); 335 free(cl); 336} 337 338char * 339_citrus_lookup_simple(const char *name, const char *key, 340 char *linebuf, size_t linebufsize, int ignore_case) 341{ 342 struct _citrus_lookup *cl; 343 struct _region data; 344 int ret; 345 346 ret = _citrus_lookup_seq_open(&cl, name, ignore_case); 347 if (ret) 348 return (NULL); 349 350 ret = _citrus_lookup_seq_lookup(cl, key, &data); 351 if (ret) { 352 _citrus_lookup_seq_close(cl); 353 return (NULL); 354 } 355 356 snprintf(linebuf, linebufsize, "%.*s", (int)_region_size(&data), 357 (const char *)_region_head(&data)); 358 359 _citrus_lookup_seq_close(cl); 360 361 return (linebuf); 362} 363