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