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