1 /* $NetBSD: citrus_lookup.c,v 1.5 2006/03/27 01:09:11 christos Exp $ */ 2 /* $DragonFly: src/lib/libc/citrus/citrus_lookup.c,v 1.3 2008/04/10 10:21:01 hasso 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 <assert.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <errno.h> 35 #include <limits.h> 36 #include <unistd.h> 37 #include <fcntl.h> 38 #include <paths.h> 39 #include <dirent.h> 40 #include <sys/types.h> 41 42 #include "citrus_namespace.h" 43 #include "citrus_bcs.h" 44 #include "citrus_region.h" 45 #include "citrus_memstream.h" 46 #include "citrus_mmap.h" 47 #include "citrus_db.h" 48 #include "citrus_db_hash.h" 49 #include "citrus_lookup.h" 50 #include "citrus_lookup_file.h" 51 52 struct _citrus_lookup { 53 union { 54 struct { 55 struct _citrus_db *db; 56 struct _citrus_region file; 57 int num, idx; 58 struct _db_locator locator; 59 } db; 60 struct { 61 struct _region r; 62 struct _memstream ms; 63 } plain; 64 } u; 65 #define cl_db u.db.db 66 #define cl_dbidx u.db.idx 67 #define cl_dbfile u.db.file 68 #define cl_dbnum u.db.num 69 #define cl_dblocator u.db.locator 70 #define cl_plainr u.plain.r 71 #define cl_plainms u.plain.ms 72 int cl_ignore_case; 73 int cl_rewind; 74 char *cl_key; 75 size_t cl_keylen; 76 int (*cl_next)(struct _citrus_lookup *, struct _region *, 77 struct _region *); 78 int (*cl_lookup)(struct _citrus_lookup *, const char *, 79 struct _region *); 80 int (*cl_num_entries)(struct _citrus_lookup *); 81 void (*cl_close)(struct _citrus_lookup *); 82 }; 83 84 static int 85 seq_get_num_entries_db(struct _citrus_lookup *cl) 86 { 87 return cl->cl_dbnum; 88 } 89 90 static int 91 seq_next_db(struct _citrus_lookup *cl, 92 struct _region *key, struct _region *data) 93 { 94 95 if (cl->cl_key) { 96 if (key) 97 _region_init(key, cl->cl_key, cl->cl_keylen); 98 return _db_lookup_by_s(cl->cl_db, cl->cl_key, data, 99 &cl->cl_dblocator); 100 } 101 102 if (cl->cl_rewind) { 103 cl->cl_dbidx = 0; 104 } 105 cl->cl_rewind = 0; 106 if (cl->cl_dbidx >= cl->cl_dbnum) 107 return ENOENT; 108 109 return _db_get_entry(cl->cl_db, cl->cl_dbidx++, key, data); 110 } 111 112 static int 113 seq_lookup_db(struct _citrus_lookup *cl, const char *key, 114 struct _region *data) 115 { 116 cl->cl_rewind = 0; 117 free(cl->cl_key); 118 cl->cl_key = strdup(key); 119 if (cl->cl_ignore_case) 120 _bcs_convert_to_lower(cl->cl_key); 121 cl->cl_keylen = strlen(cl->cl_key); 122 _db_locator_init(&cl->cl_dblocator); 123 return _db_lookup_by_s(cl->cl_db, cl->cl_key, data, &cl->cl_dblocator); 124 } 125 126 static void 127 seq_close_db(struct _citrus_lookup *cl) 128 { 129 _db_close(cl->cl_db); 130 _unmap_file(&cl->cl_dbfile); 131 } 132 133 static int 134 seq_open_db(struct _citrus_lookup *cl, const char *name) 135 { 136 int ret; 137 struct _region r; 138 char path[PATH_MAX]; 139 140 snprintf(path, sizeof(path), "%s.db", name); 141 ret = _map_file(&r, path); 142 if (ret) 143 return ret; 144 145 ret = _db_open(&cl->cl_db, &r, _CITRUS_LOOKUP_MAGIC, 146 _db_hash_std, NULL); 147 if (ret) { 148 _unmap_file(&r); 149 return ret; 150 } 151 152 cl->cl_dbfile = r; 153 cl->cl_dbnum = _db_get_num_entries(cl->cl_db); 154 cl->cl_dbidx = 0; 155 cl->cl_rewind = 1; 156 cl->cl_lookup = &seq_lookup_db; 157 cl->cl_next = &seq_next_db; 158 cl->cl_num_entries = &seq_get_num_entries_db; 159 cl->cl_close = &seq_close_db; 160 161 return 0; 162 } 163 164 #define T_COMM '#' 165 static int 166 seq_next_plain(struct _citrus_lookup *cl, struct _region *key, 167 struct _region *data) 168 { 169 const char *p, *q; 170 size_t len; 171 172 if (cl->cl_rewind) 173 _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 174 cl->cl_rewind = 0; 175 176 retry: 177 p = _memstream_getln(&cl->cl_plainms, &len); 178 if (p == NULL) 179 return ENOENT; 180 /* ignore comment */ 181 q = memchr(p, T_COMM, len); 182 if (q) { 183 len = q-p; 184 } 185 /* ignore trailing spaces */ 186 _bcs_trunc_rws_len(p, &len); 187 p = _bcs_skip_ws_len(p, &len); 188 q = _bcs_skip_nonws_len(p, &len); 189 if (p==q) 190 goto retry; 191 if (cl->cl_key && ((size_t)(q-p) != cl->cl_keylen || 192 memcmp(p, cl->cl_key, (size_t)(q-p)) != 0)) 193 goto retry; 194 195 /* found a entry */ 196 if (key) 197 _region_init(key, __DECONST(char *, p), q-p); 198 p = _bcs_skip_ws_len(q, &len); 199 if (data) 200 _region_init(data, len ? __DECONST(char *, p) : NULL, len); 201 202 return 0; 203 } 204 205 static int 206 seq_get_num_entries_plain(struct _citrus_lookup *cl) 207 { 208 int num; 209 210 num = 0; 211 while (seq_next_plain(cl, NULL, NULL) == 0) 212 num++; 213 214 return num; 215 } 216 217 static int 218 seq_lookup_plain(struct _citrus_lookup *cl, const char *key, 219 struct _region *data) 220 { 221 size_t len; 222 const char *p; 223 224 cl->cl_rewind = 0; 225 free(cl->cl_key); 226 cl->cl_key = strdup(key); 227 if (cl->cl_ignore_case) 228 _bcs_convert_to_lower(cl->cl_key); 229 cl->cl_keylen = strlen(cl->cl_key); 230 _memstream_bind(&cl->cl_plainms, &cl->cl_plainr); 231 p = _memstream_matchline(&cl->cl_plainms, cl->cl_key, &len, 0); 232 if (p == NULL) 233 return ENOENT; 234 if (data) 235 _region_init(data, __DECONST(char *, p), len); 236 237 return 0; 238 } 239 240 static void 241 seq_close_plain(struct _citrus_lookup *cl) 242 { 243 _unmap_file(&cl->cl_plainr); 244 } 245 246 static int 247 seq_open_plain(struct _citrus_lookup *cl, const char *name) 248 { 249 int ret; 250 251 /* open read stream */ 252 ret = _map_file(&cl->cl_plainr, name); 253 if (ret) 254 return ret; 255 256 cl->cl_rewind = 1; 257 cl->cl_next = &seq_next_plain; 258 cl->cl_lookup = &seq_lookup_plain; 259 cl->cl_num_entries = &seq_get_num_entries_plain; 260 cl->cl_close = &seq_close_plain; 261 262 return 0; 263 } 264 265 int 266 _citrus_lookup_seq_open(struct _citrus_lookup **rcl, const char *name, 267 int ignore_case) 268 { 269 int ret; 270 struct _citrus_lookup *cl; 271 272 cl = malloc(sizeof(*cl)); 273 if (cl == NULL) 274 return errno; 275 276 cl->cl_key = NULL; 277 cl->cl_keylen = 0; 278 cl->cl_ignore_case = ignore_case; 279 ret = seq_open_db(cl, name); 280 if (ret == ENOENT) 281 ret = seq_open_plain(cl, name); 282 if (!ret) 283 *rcl = cl; 284 else 285 free(cl); 286 287 return ret; 288 } 289 290 void 291 _citrus_lookup_seq_rewind(struct _citrus_lookup *cl) 292 { 293 cl->cl_rewind = 1; 294 free(cl->cl_key); 295 cl->cl_key = NULL; 296 cl->cl_keylen = 0; 297 } 298 299 int 300 _citrus_lookup_seq_next(struct _citrus_lookup *cl, 301 struct _region *key, struct _region *data) 302 { 303 return (*cl->cl_next)(cl, key, data); 304 } 305 306 int 307 _citrus_lookup_seq_lookup(struct _citrus_lookup *cl, const char *key, 308 struct _region *data) 309 { 310 return (*cl->cl_lookup)(cl, key, data); 311 } 312 313 int 314 _citrus_lookup_get_number_of_entries(struct _citrus_lookup *cl) 315 { 316 return (*cl->cl_num_entries)(cl); 317 } 318 319 void 320 _citrus_lookup_seq_close(struct _citrus_lookup *cl) 321 { 322 free(cl->cl_key); 323 (*cl->cl_close)(cl); 324 free(cl); 325 } 326 327 char * 328 _citrus_lookup_simple(const char *name, const char *key, 329 char *linebuf, size_t linebufsize, int ignore_case) 330 { 331 int ret; 332 struct _citrus_lookup *cl; 333 struct _region data; 334 335 ret = _citrus_lookup_seq_open(&cl, name, ignore_case); 336 if (ret) 337 return NULL; 338 339 ret = _citrus_lookup_seq_lookup(cl, key, &data); 340 if (ret) { 341 _citrus_lookup_seq_close(cl); 342 return NULL; 343 } 344 345 snprintf(linebuf, linebufsize, "%.*s", 346 (int)_region_size(&data), (const char *)_region_head(&data)); 347 348 _citrus_lookup_seq_close(cl); 349 350 return linebuf; 351 } 352