1 /* $FreeBSD$ */ 2 /* $NetBSD: citrus_lookup.c,v 1.6 2009/02/03 04:58:38 lukem 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 54 struct _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 86 static int 87 seq_get_num_entries_db(struct _citrus_lookup *cl) 88 { 89 90 return (cl->cl_dbnum); 91 } 92 93 static int 94 seq_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 115 static int 116 seq_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 130 static void 131 seq_close_db(struct _citrus_lookup *cl) 132 { 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 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 '#' 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, __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 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, __DECONST(void *, p), len); 241 242 return (0); 243 } 244 245 static void 246 seq_close_plain(struct _citrus_lookup *cl) 247 { 248 249 _unmap_file(&cl->cl_plainr); 250 } 251 252 static int 253 seq_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 271 int 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 296 void 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 306 int 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 314 int 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 322 int 323 _citrus_lookup_get_number_of_entries(struct _citrus_lookup *cl) 324 { 325 326 return ((*cl->cl_num_entries)(cl)); 327 } 328 329 void 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 338 char * 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