[BACK]Return to dbm.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/dbm.c, Revision 1.3

1.3     ! schwarze    1: /*     $OpenBSD: dbm.c,v 1.2 2016/08/30 21:58:59 schwarze Exp $ */
1.1       schwarze    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.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_MAX];
                     57: static int32_t          nvals[MACRO_MAX];
                     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))) < 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) {
                     92:                warnx("dbm_open(%s): Invalid offset of macros array", fname);
                     93:                goto fail;
                     94:        }
                     95:        if (be32toh(*mp) != MACRO_MAX) {
                     96:                warnx("dbm_open(%s): Invalid number of macros: %d",
                     97:                    fname, be32toh(*mp));
                     98:                goto fail;
                     99:        }
                    100:        for (im = 0; im < MACRO_MAX; im++) {
                    101:                if ((ep = dbm_get(*++mp)) == NULL) {
                    102:                        warnx("dbm_open(%s): Invalid offset of macro %d",
                    103:                            fname, im);
                    104:                        goto fail;
                    105:                }
                    106:                nvals[im] = be32toh(*ep);
                    107:                macros[im] = (struct macro *)++ep;
                    108:        }
                    109:        return 0;
                    110:
                    111: fail:
                    112:        dbm_unmap();
                    113:        errno = EFTYPE;
                    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);
                    141:        assert(ip < npages);
                    142:        res.name = dbm_get(pages[ip].name);
1.2       schwarze  143:        if (res.name == NULL)
                    144:                res.name = "(NULL)";
1.1       schwarze  145:        res.sect = dbm_get(pages[ip].sect);
1.2       schwarze  146:        if (res.sect == NULL)
                    147:                res.sect = "(NULL)";
1.1       schwarze  148:        res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
                    149:        res.desc = dbm_get(pages[ip].desc);
1.2       schwarze  150:        if (res.desc == NULL)
                    151:                res.desc = "(NULL)";
1.1       schwarze  152:        res.file = dbm_get(pages[ip].file);
1.2       schwarze  153:        if (res.file == NULL)
                    154:                res.file = " (NULL)";
1.1       schwarze  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);
                    166:        page_bytitle(ITER_NAME, match);
                    167: }
                    168:
                    169: void
                    170: dbm_page_bysect(const struct dbm_match *match)
                    171: {
                    172:        assert(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);
                    180:        page_byarch(match);
                    181: }
                    182:
                    183: void
                    184: dbm_page_bydesc(const struct dbm_match *match)
                    185: {
                    186:        assert(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);
                    194:        assert(im < MACRO_MAX);
                    195:        assert(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);
                    212:        case ITER_MACRO:
                    213:                return page_bymacro(0, NULL);
                    214:        default:
                    215:                return page_bytitle(iteration, NULL);
                    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 ||
                    231:            arg_iter == ITER_SECT);
                    232:
                    233:        /* Initialize for a new iteration. */
                    234:
                    235:        if (arg_match != NULL) {
                    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:                }
1.2       schwarze  251:                if (cp == NULL) {
                    252:                        iteration = ITER_NONE;
                    253:                        match = NULL;
                    254:                        cp = NULL;
                    255:                        ip = npages;
                    256:                } else
                    257:                        ip = 0;
1.1       schwarze  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;
                    282:                cp = NULL;
                    283:                return res;
                    284:        }
                    285:
                    286:        /* Found a match; save the quality for later retrieval. */
                    287:
                    288:        res.page = ip;
                    289:        res.bits = iteration == ITER_NAME ? cp[-1] : 0;
                    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) {
                    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;
                    335:        return res;
                    336: }
                    337:
                    338: static struct dbm_res
1.3     ! schwarze  339: page_bymacro(int32_t arg_im, const struct dbm_match *arg_match)
1.1       schwarze  340: {
1.3     ! schwarze  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};
1.1       schwarze  346:
                    347:        assert(im >= 0);
                    348:        assert(im < MACRO_MAX);
                    349:
                    350:        /* Initialize for a new iteration. */
                    351:
1.3     ! schwarze  352:        if (arg_match != NULL) {
1.1       schwarze  353:                iteration = ITER_MACRO;
1.3     ! schwarze  354:                match = arg_match;
        !           355:                im = arg_im;
1.1       schwarze  356:                cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
1.3     ! schwarze  357:                pp = NULL;
        !           358:                iv = -1;
1.1       schwarze  359:                return res;
                    360:        }
                    361:        if (iteration != ITER_MACRO)
                    362:                return res;
                    363:
1.3     ! schwarze  364:        /* Find the next matching macro value. */
1.1       schwarze  365:
1.3     ! schwarze  366:        while (pp == NULL || *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);
1.1       schwarze  375:        }
                    376:
1.3     ! schwarze  377:        /* Found a matching page. */
1.1       schwarze  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);
                    390:        assert(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);
                    400:        assert(im < MACRO_MAX);
                    401:        assert(iv >= 0);
                    402:        assert(iv < nvals[im]);
                    403:        macro.value = dbm_get(macros[im][iv].value);
                    404:        macro.pp = dbm_get(macros[im][iv].pages);
                    405:        return &macro;
                    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);
                    415:        assert(im < MACRO_MAX);
                    416:        assert(ip != 0);
                    417:        macro_bypage(im, ip);
                    418: }
                    419:
                    420: char *
                    421: dbm_macro_next(void)
                    422: {
                    423:        return macro_bypage(MACRO_MAX, 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_MAX && arg_ip != 0) {
                    435:                im = arg_im;
                    436:                ip = arg_ip;
                    437:                pp = dbm_get(macros[im]->pages);
                    438:                iv = 0;
                    439:                return NULL;
                    440:        }
                    441:        if (im >= MACRO_MAX)
                    442:                return NULL;
                    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_MAX;
                    458:                ip = 0;
                    459:                pp = NULL;
                    460:                return NULL;
                    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: }