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