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