xref: /freebsd/lib/libc/iconv/citrus_lookup.c (revision 1d386b48)
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