File: | src/usr.bin/mandoc/dbm.c |
Warning: | line 289, column 38 Array access (from variable 'cp') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: dbm.c,v 1.5 2019/07/01 22:43:03 schwarze Exp $ */ | |||
2 | /* | |||
3 | * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> | |||
4 | * | |||
5 | * Permission to use, copy, modify, and distribute this software for any | |||
6 | * purpose with or without fee is hereby granted, provided that the above | |||
7 | * copyright notice and this permission notice appear in all copies. | |||
8 | * | |||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
16 | * | |||
17 | * Map-based version of the mandoc database, for read-only access. | |||
18 | * The interface is defined in "dbm.h". | |||
19 | */ | |||
20 | #include <assert.h> | |||
21 | #include <endian.h> | |||
22 | #include <err.h> | |||
23 | #include <errno(*__errno()).h> | |||
24 | #include <regex.h> | |||
25 | #include <stdint.h> | |||
26 | #include <stdio.h> | |||
27 | #include <stdlib.h> | |||
28 | #include <string.h> | |||
29 | ||||
30 | #include "mansearch.h" | |||
31 | #include "dbm_map.h" | |||
32 | #include "dbm.h" | |||
33 | ||||
34 | struct macro { | |||
35 | int32_t value; | |||
36 | int32_t pages; | |||
37 | }; | |||
38 | ||||
39 | struct page { | |||
40 | int32_t name; | |||
41 | int32_t sect; | |||
42 | int32_t arch; | |||
43 | int32_t desc; | |||
44 | int32_t file; | |||
45 | }; | |||
46 | ||||
47 | enum iter { | |||
48 | ITER_NONE = 0, | |||
49 | ITER_NAME, | |||
50 | ITER_SECT, | |||
51 | ITER_ARCH, | |||
52 | ITER_DESC, | |||
53 | ITER_MACRO | |||
54 | }; | |||
55 | ||||
56 | static struct macro *macros[MACRO_MAX36]; | |||
57 | static int32_t nvals[MACRO_MAX36]; | |||
58 | static struct page *pages; | |||
59 | static int32_t npages; | |||
60 | static enum iter iteration; | |||
61 | ||||
62 | static struct dbm_res page_bytitle(enum iter, const struct dbm_match *); | |||
63 | static struct dbm_res page_byarch(const struct dbm_match *); | |||
64 | static struct dbm_res page_bymacro(int32_t, const struct dbm_match *); | |||
65 | static char *macro_bypage(int32_t, int32_t); | |||
66 | ||||
67 | ||||
68 | /*** top level functions **********************************************/ | |||
69 | ||||
70 | /* | |||
71 | * Open a disk-based mandoc database for read-only access. | |||
72 | * Map the pages and macros[] arrays. | |||
73 | * Return 0 on success. Return -1 and set errno on failure. | |||
74 | */ | |||
75 | int | |||
76 | dbm_open(const char *fname) | |||
77 | { | |||
78 | const int32_t *mp, *ep; | |||
79 | int32_t im; | |||
80 | ||||
81 | if (dbm_map(fname) == -1) | |||
82 | return -1; | |||
83 | ||||
84 | if ((npages = be32toh(*dbm_getint(4))(__uint32_t)(__builtin_constant_p(*dbm_getint(4)) ? (__uint32_t )(((__uint32_t)(*dbm_getint(4)) & 0xff) << 24 | ((__uint32_t )(*dbm_getint(4)) & 0xff00) << 8 | ((__uint32_t)(*dbm_getint (4)) & 0xff0000) >> 8 | ((__uint32_t)(*dbm_getint(4 )) & 0xff000000) >> 24) : __swap32md(*dbm_getint(4) ))) < 0) { | |||
85 | warnx("dbm_open(%s): Invalid number of pages: %d", | |||
86 | fname, npages); | |||
87 | goto fail; | |||
88 | } | |||
89 | pages = (struct page *)dbm_getint(5); | |||
90 | ||||
91 | if ((mp = dbm_get(*dbm_getint(2))) == NULL((void *)0)) { | |||
92 | warnx("dbm_open(%s): Invalid offset of macros array", fname); | |||
93 | goto fail; | |||
94 | } | |||
95 | if (be32toh(*mp)(__uint32_t)(__builtin_constant_p(*mp) ? (__uint32_t)(((__uint32_t )(*mp) & 0xff) << 24 | ((__uint32_t)(*mp) & 0xff00 ) << 8 | ((__uint32_t)(*mp) & 0xff0000) >> 8 | ((__uint32_t)(*mp) & 0xff000000) >> 24) : __swap32md (*mp)) != MACRO_MAX36) { | |||
96 | warnx("dbm_open(%s): Invalid number of macros: %d", | |||
97 | fname, be32toh(*mp)(__uint32_t)(__builtin_constant_p(*mp) ? (__uint32_t)(((__uint32_t )(*mp) & 0xff) << 24 | ((__uint32_t)(*mp) & 0xff00 ) << 8 | ((__uint32_t)(*mp) & 0xff0000) >> 8 | ((__uint32_t)(*mp) & 0xff000000) >> 24) : __swap32md (*mp))); | |||
98 | goto fail; | |||
99 | } | |||
100 | for (im = 0; im < MACRO_MAX36; im++) { | |||
101 | if ((ep = dbm_get(*++mp)) == NULL((void *)0)) { | |||
102 | warnx("dbm_open(%s): Invalid offset of macro %d", | |||
103 | fname, im); | |||
104 | goto fail; | |||
105 | } | |||
106 | nvals[im] = be32toh(*ep)(__uint32_t)(__builtin_constant_p(*ep) ? (__uint32_t)(((__uint32_t )(*ep) & 0xff) << 24 | ((__uint32_t)(*ep) & 0xff00 ) << 8 | ((__uint32_t)(*ep) & 0xff0000) >> 8 | ((__uint32_t)(*ep) & 0xff000000) >> 24) : __swap32md (*ep)); | |||
107 | macros[im] = (struct macro *)++ep; | |||
108 | } | |||
109 | return 0; | |||
110 | ||||
111 | fail: | |||
112 | dbm_unmap(); | |||
113 | errno(*__errno()) = EFTYPE79; | |||
114 | return -1; | |||
115 | } | |||
116 | ||||
117 | void | |||
118 | dbm_close(void) | |||
119 | { | |||
120 | dbm_unmap(); | |||
121 | } | |||
122 | ||||
123 | ||||
124 | /*** functions for handling pages *************************************/ | |||
125 | ||||
126 | int32_t | |||
127 | dbm_page_count(void) | |||
128 | { | |||
129 | return npages; | |||
130 | } | |||
131 | ||||
132 | /* | |||
133 | * Give the caller pointers to the data for one manual page. | |||
134 | */ | |||
135 | struct dbm_page * | |||
136 | dbm_page_get(int32_t ip) | |||
137 | { | |||
138 | static struct dbm_page res; | |||
139 | ||||
140 | assert(ip >= 0)((ip >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 140, __func__, "ip >= 0")); | |||
141 | assert(ip < npages)((ip < npages) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 141, __func__, "ip < npages")); | |||
142 | res.name = dbm_get(pages[ip].name); | |||
143 | if (res.name == NULL((void *)0)) | |||
144 | res.name = "(NULL)\0"; | |||
145 | res.sect = dbm_get(pages[ip].sect); | |||
146 | if (res.sect == NULL((void *)0)) | |||
147 | res.sect = "(NULL)\0"; | |||
148 | res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL((void *)0); | |||
149 | res.desc = dbm_get(pages[ip].desc); | |||
150 | if (res.desc == NULL((void *)0)) | |||
151 | res.desc = "(NULL)"; | |||
152 | res.file = dbm_get(pages[ip].file); | |||
153 | if (res.file == NULL((void *)0)) | |||
154 | res.file = " (NULL)\0"; | |||
155 | res.addr = dbm_addr(pages + ip); | |||
156 | return &res; | |||
157 | } | |||
158 | ||||
159 | /* | |||
160 | * Functions to start filtered iterations over manual pages. | |||
161 | */ | |||
162 | void | |||
163 | dbm_page_byname(const struct dbm_match *match) | |||
164 | { | |||
165 | assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 165, __func__, "match != NULL")); | |||
166 | page_bytitle(ITER_NAME, match); | |||
167 | } | |||
168 | ||||
169 | void | |||
170 | dbm_page_bysect(const struct dbm_match *match) | |||
171 | { | |||
172 | assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 172, __func__, "match != NULL")); | |||
173 | page_bytitle(ITER_SECT, match); | |||
174 | } | |||
175 | ||||
176 | void | |||
177 | dbm_page_byarch(const struct dbm_match *match) | |||
178 | { | |||
179 | assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 179, __func__, "match != NULL")); | |||
180 | page_byarch(match); | |||
181 | } | |||
182 | ||||
183 | void | |||
184 | dbm_page_bydesc(const struct dbm_match *match) | |||
185 | { | |||
186 | assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 186, __func__, "match != NULL")); | |||
187 | page_bytitle(ITER_DESC, match); | |||
188 | } | |||
189 | ||||
190 | void | |||
191 | dbm_page_bymacro(int32_t im, const struct dbm_match *match) | |||
192 | { | |||
193 | assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 193, __func__, "im >= 0")); | |||
194 | assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 194, __func__, "im < MACRO_MAX")); | |||
195 | assert(match != NULL)((match != ((void *)0)) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 195, __func__, "match != NULL")); | |||
196 | page_bymacro(im, match); | |||
197 | } | |||
198 | ||||
199 | /* | |||
200 | * Return the number of the next manual page in the current iteration. | |||
201 | */ | |||
202 | struct dbm_res | |||
203 | dbm_page_next(void) | |||
204 | { | |||
205 | struct dbm_res res = {-1, 0}; | |||
206 | ||||
207 | switch(iteration) { | |||
| ||||
208 | case ITER_NONE: | |||
209 | return res; | |||
210 | case ITER_ARCH: | |||
211 | return page_byarch(NULL((void *)0)); | |||
212 | case ITER_MACRO: | |||
213 | return page_bymacro(0, NULL((void *)0)); | |||
214 | default: | |||
215 | return page_bytitle(iteration, NULL((void *)0)); | |||
216 | } | |||
217 | } | |||
218 | ||||
219 | /* | |||
220 | * Functions implementing the iteration over manual pages. | |||
221 | */ | |||
222 | static struct dbm_res | |||
223 | page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match) | |||
224 | { | |||
225 | static const struct dbm_match *match; | |||
226 | static const char *cp; | |||
227 | static int32_t ip; | |||
228 | struct dbm_res res = {-1, 0}; | |||
229 | ||||
230 | assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||((arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 231, __func__, "arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT" )) | |||
231 | arg_iter == ITER_SECT)((arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 231, __func__, "arg_iter == ITER_NAME || arg_iter == ITER_DESC || arg_iter == ITER_SECT" )); | |||
232 | ||||
233 | /* Initialize for a new iteration. */ | |||
234 | ||||
235 | if (arg_match
| |||
236 | iteration = arg_iter; | |||
237 | match = arg_match; | |||
238 | switch (iteration) { | |||
239 | case ITER_NAME: | |||
240 | cp = dbm_get(pages[0].name); | |||
241 | break; | |||
242 | case ITER_SECT: | |||
243 | cp = dbm_get(pages[0].sect); | |||
244 | break; | |||
245 | case ITER_DESC: | |||
246 | cp = dbm_get(pages[0].desc); | |||
247 | break; | |||
248 | default: | |||
249 | abort(); | |||
250 | } | |||
251 | if (cp == NULL((void *)0)) { | |||
252 | iteration = ITER_NONE; | |||
253 | match = NULL((void *)0); | |||
254 | cp = NULL((void *)0); | |||
255 | ip = npages; | |||
256 | } else | |||
257 | ip = 0; | |||
258 | return res; | |||
259 | } | |||
260 | ||||
261 | /* Search for a name. */ | |||
262 | ||||
263 | while (ip < npages) { | |||
264 | if (iteration == ITER_NAME) | |||
265 | cp++; | |||
266 | if (dbm_match(match, cp)) | |||
267 | break; | |||
268 | cp = strchr(cp, '\0') + 1; | |||
269 | if (iteration == ITER_DESC) | |||
270 | ip++; | |||
271 | else if (*cp == '\0') { | |||
272 | cp++; | |||
273 | ip++; | |||
274 | } | |||
275 | } | |||
276 | ||||
277 | /* Reached the end without a match. */ | |||
278 | ||||
279 | if (ip == npages) { | |||
280 | iteration = ITER_NONE; | |||
281 | match = NULL((void *)0); | |||
282 | cp = NULL((void *)0); | |||
283 | return res; | |||
284 | } | |||
285 | ||||
286 | /* Found a match; save the quality for later retrieval. */ | |||
287 | ||||
288 | res.page = ip; | |||
289 | res.bits = iteration
| |||
| ||||
290 | ||||
291 | /* Skip the remaining names of this page. */ | |||
292 | ||||
293 | if (++ip < npages) { | |||
294 | do { | |||
295 | cp++; | |||
296 | } while (cp[-1] != '\0' || | |||
297 | (iteration != ITER_DESC && cp[-2] != '\0')); | |||
298 | } | |||
299 | return res; | |||
300 | } | |||
301 | ||||
302 | static struct dbm_res | |||
303 | page_byarch(const struct dbm_match *arg_match) | |||
304 | { | |||
305 | static const struct dbm_match *match; | |||
306 | struct dbm_res res = {-1, 0}; | |||
307 | static int32_t ip; | |||
308 | const char *cp; | |||
309 | ||||
310 | /* Initialize for a new iteration. */ | |||
311 | ||||
312 | if (arg_match != NULL((void *)0)) { | |||
313 | iteration = ITER_ARCH; | |||
314 | match = arg_match; | |||
315 | ip = 0; | |||
316 | return res; | |||
317 | } | |||
318 | ||||
319 | /* Search for an architecture. */ | |||
320 | ||||
321 | for ( ; ip < npages; ip++) | |||
322 | if (pages[ip].arch) | |||
323 | for (cp = dbm_get(pages[ip].arch); | |||
324 | *cp != '\0'; | |||
325 | cp = strchr(cp, '\0') + 1) | |||
326 | if (dbm_match(match, cp)) { | |||
327 | res.page = ip++; | |||
328 | return res; | |||
329 | } | |||
330 | ||||
331 | /* Reached the end without a match. */ | |||
332 | ||||
333 | iteration = ITER_NONE; | |||
334 | match = NULL((void *)0); | |||
335 | return res; | |||
336 | } | |||
337 | ||||
338 | static struct dbm_res | |||
339 | page_bymacro(int32_t arg_im, const struct dbm_match *arg_match) | |||
340 | { | |||
341 | static const struct dbm_match *match; | |||
342 | static const int32_t *pp; | |||
343 | static const char *cp; | |||
344 | static int32_t im, iv; | |||
345 | struct dbm_res res = {-1, 0}; | |||
346 | ||||
347 | assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 347, __func__, "im >= 0")); | |||
348 | assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 348, __func__, "im < MACRO_MAX")); | |||
349 | ||||
350 | /* Initialize for a new iteration. */ | |||
351 | ||||
352 | if (arg_match != NULL((void *)0)) { | |||
353 | iteration = ITER_MACRO; | |||
354 | match = arg_match; | |||
355 | im = arg_im; | |||
356 | cp = nvals[im] ? dbm_get(macros[im]->value) : NULL((void *)0); | |||
357 | pp = NULL((void *)0); | |||
358 | iv = -1; | |||
359 | return res; | |||
360 | } | |||
361 | if (iteration != ITER_MACRO) | |||
362 | return res; | |||
363 | ||||
364 | /* Find the next matching macro value. */ | |||
365 | ||||
366 | while (pp == NULL((void *)0) || *pp == 0) { | |||
367 | if (++iv == nvals[im]) { | |||
368 | iteration = ITER_NONE; | |||
369 | return res; | |||
370 | } | |||
371 | if (iv) | |||
372 | cp = strchr(cp, '\0') + 1; | |||
373 | if (dbm_match(match, cp)) | |||
374 | pp = dbm_get(macros[im][iv].pages); | |||
375 | } | |||
376 | ||||
377 | /* Found a matching page. */ | |||
378 | ||||
379 | res.page = (struct page *)dbm_get(*pp++) - pages; | |||
380 | return res; | |||
381 | } | |||
382 | ||||
383 | ||||
384 | /*** functions for handling macros ************************************/ | |||
385 | ||||
386 | int32_t | |||
387 | dbm_macro_count(int32_t im) | |||
388 | { | |||
389 | assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 389, __func__, "im >= 0")); | |||
390 | assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 390, __func__, "im < MACRO_MAX")); | |||
391 | return nvals[im]; | |||
392 | } | |||
393 | ||||
394 | struct dbm_macro * | |||
395 | dbm_macro_get(int32_t im, int32_t iv) | |||
396 | { | |||
397 | static struct dbm_macro macro; | |||
398 | ||||
399 | assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 399, __func__, "im >= 0")); | |||
400 | assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 400, __func__, "im < MACRO_MAX")); | |||
401 | assert(iv >= 0)((iv >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 401, __func__, "iv >= 0")); | |||
402 | assert(iv < nvals[im])((iv < nvals[im]) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 402, __func__, "iv < nvals[im]")); | |||
403 | macro.value = dbm_get(macros[im][iv].value); | |||
404 | macro.pp = dbm_get(macros[im][iv].pages); | |||
405 | return ¯o; | |||
406 | } | |||
407 | ||||
408 | /* | |||
409 | * Filtered iteration over macro entries. | |||
410 | */ | |||
411 | void | |||
412 | dbm_macro_bypage(int32_t im, int32_t ip) | |||
413 | { | |||
414 | assert(im >= 0)((im >= 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 414, __func__, "im >= 0")); | |||
415 | assert(im < MACRO_MAX)((im < 36) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 415, __func__, "im < MACRO_MAX")); | |||
416 | assert(ip != 0)((ip != 0) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/dbm.c" , 416, __func__, "ip != 0")); | |||
417 | macro_bypage(im, ip); | |||
418 | } | |||
419 | ||||
420 | char * | |||
421 | dbm_macro_next(void) | |||
422 | { | |||
423 | return macro_bypage(MACRO_MAX36, 0); | |||
424 | } | |||
425 | ||||
426 | static char * | |||
427 | macro_bypage(int32_t arg_im, int32_t arg_ip) | |||
428 | { | |||
429 | static const int32_t *pp; | |||
430 | static int32_t im, ip, iv; | |||
431 | ||||
432 | /* Initialize for a new iteration. */ | |||
433 | ||||
434 | if (arg_im < MACRO_MAX36 && arg_ip != 0) { | |||
435 | im = arg_im; | |||
436 | ip = arg_ip; | |||
437 | pp = dbm_get(macros[im]->pages); | |||
438 | iv = 0; | |||
439 | return NULL((void *)0); | |||
440 | } | |||
441 | if (im >= MACRO_MAX36) | |||
442 | return NULL((void *)0); | |||
443 | ||||
444 | /* Search for the next value. */ | |||
445 | ||||
446 | while (iv < nvals[im]) { | |||
447 | if (*pp == ip) | |||
448 | break; | |||
449 | if (*pp == 0) | |||
450 | iv++; | |||
451 | pp++; | |||
452 | } | |||
453 | ||||
454 | /* Reached the end without a match. */ | |||
455 | ||||
456 | if (iv == nvals[im]) { | |||
457 | im = MACRO_MAX36; | |||
458 | ip = 0; | |||
459 | pp = NULL((void *)0); | |||
460 | return NULL((void *)0); | |||
461 | } | |||
462 | ||||
463 | /* Found a match; skip the remaining pages of this entry. */ | |||
464 | ||||
465 | if (++iv < nvals[im]) | |||
466 | while (*pp++ != 0) | |||
467 | continue; | |||
468 | ||||
469 | return dbm_get(macros[im][iv - 1].value); | |||
470 | } |