[BACK]Return to dbm.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/dbm.c, Revision 1.4

1.4     ! schwarze    1: /*     $Id: dbm.c,v 1.3 2016/08/05 23:15:08 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:  */
1.2       schwarze   20: #include "config.h"
                     21:
1.1       schwarze   22: #include <assert.h>
1.3       schwarze   23: #if HAVE_ENDIAN
1.1       schwarze   24: #include <endian.h>
1.3       schwarze   25: #elif HAVE_SYS_ENDIAN
                     26: #include <sys/endian.h>
                     27: #elif HAVE_NTOHL
                     28: #include <arpa/inet.h>
                     29: #endif
1.2       schwarze   30: #if HAVE_ERR
1.1       schwarze   31: #include <err.h>
1.2       schwarze   32: #endif
1.1       schwarze   33: #include <errno.h>
                     34: #include <regex.h>
                     35: #include <stdint.h>
                     36: #include <stdio.h>
                     37: #include <stdlib.h>
                     38: #include <string.h>
                     39:
                     40: #include "mansearch.h"
                     41: #include "dbm_map.h"
                     42: #include "dbm.h"
                     43:
                     44: struct macro {
                     45:        int32_t value;
                     46:        int32_t pages;
                     47: };
                     48:
                     49: struct page {
                     50:        int32_t name;
                     51:        int32_t sect;
                     52:        int32_t arch;
                     53:        int32_t desc;
                     54:        int32_t file;
                     55: };
                     56:
                     57: enum iter {
                     58:        ITER_NONE = 0,
                     59:        ITER_NAME,
                     60:        ITER_SECT,
                     61:        ITER_ARCH,
                     62:        ITER_DESC,
                     63:        ITER_MACRO
                     64: };
                     65:
                     66: static struct macro    *macros[MACRO_MAX];
                     67: static int32_t          nvals[MACRO_MAX];
                     68: static struct page     *pages;
                     69: static int32_t          npages;
                     70: static enum iter        iteration;
                     71:
                     72: static struct dbm_res   page_bytitle(enum iter, const struct dbm_match *);
                     73: static struct dbm_res   page_byarch(const struct dbm_match *);
                     74: static struct dbm_res   page_bymacro(int32_t, const struct dbm_match *);
                     75: static char            *macro_bypage(int32_t, int32_t);
                     76:
                     77:
                     78: /*** top level functions **********************************************/
                     79:
                     80: /*
                     81:  * Open a disk-based mandoc database for read-only access.
                     82:  * Map the pages and macros[] arrays.
                     83:  * Return 0 on success.  Return -1 and set errno on failure.
                     84:  */
                     85: int
                     86: dbm_open(const char *fname)
                     87: {
                     88:        const int32_t   *mp, *ep;
                     89:        int32_t          im;
                     90:
                     91:        if (dbm_map(fname) == -1)
                     92:                return -1;
                     93:
                     94:        if ((npages = be32toh(*dbm_getint(4))) < 0) {
                     95:                warnx("dbm_open(%s): Invalid number of pages: %d",
                     96:                    fname, npages);
                     97:                goto fail;
                     98:        }
                     99:        pages = (struct page *)dbm_getint(5);
                    100:
                    101:        if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
                    102:                warnx("dbm_open(%s): Invalid offset of macros array", fname);
                    103:                goto fail;
                    104:        }
                    105:        if (be32toh(*mp) != MACRO_MAX) {
                    106:                warnx("dbm_open(%s): Invalid number of macros: %d",
                    107:                    fname, be32toh(*mp));
                    108:                goto fail;
                    109:        }
                    110:        for (im = 0; im < MACRO_MAX; im++) {
                    111:                if ((ep = dbm_get(*++mp)) == NULL) {
                    112:                        warnx("dbm_open(%s): Invalid offset of macro %d",
                    113:                            fname, im);
                    114:                        goto fail;
                    115:                }
                    116:                nvals[im] = be32toh(*ep);
                    117:                macros[im] = (struct macro *)++ep;
                    118:        }
                    119:        return 0;
                    120:
                    121: fail:
                    122:        dbm_unmap();
                    123:        errno = EFTYPE;
                    124:        return -1;
                    125: }
                    126:
                    127: void
                    128: dbm_close(void)
                    129: {
                    130:        dbm_unmap();
                    131: }
                    132:
                    133:
                    134: /*** functions for handling pages *************************************/
                    135:
                    136: int32_t
                    137: dbm_page_count(void)
                    138: {
                    139:        return npages;
                    140: }
                    141:
                    142: /*
                    143:  * Give the caller pointers to the data for one manual page.
                    144:  */
                    145: struct dbm_page *
                    146: dbm_page_get(int32_t ip)
                    147: {
                    148:        static struct dbm_page   res;
                    149:
                    150:        assert(ip >= 0);
                    151:        assert(ip < npages);
                    152:        res.name = dbm_get(pages[ip].name);
1.4     ! schwarze  153:        if (res.name == NULL)
        !           154:                res.name = "(NULL)";
1.1       schwarze  155:        res.sect = dbm_get(pages[ip].sect);
1.4     ! schwarze  156:        if (res.sect == NULL)
        !           157:                res.sect = "(NULL)";
1.1       schwarze  158:        res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
                    159:        res.desc = dbm_get(pages[ip].desc);
1.4     ! schwarze  160:        if (res.desc == NULL)
        !           161:                res.desc = "(NULL)";
1.1       schwarze  162:        res.file = dbm_get(pages[ip].file);
1.4     ! schwarze  163:        if (res.file == NULL)
        !           164:                res.file = " (NULL)";
1.1       schwarze  165:        res.addr = dbm_addr(pages + ip);
                    166:        return &res;
                    167: }
                    168:
                    169: /*
                    170:  * Functions to start filtered iterations over manual pages.
                    171:  */
                    172: void
                    173: dbm_page_byname(const struct dbm_match *match)
                    174: {
                    175:        assert(match != NULL);
                    176:        page_bytitle(ITER_NAME, match);
                    177: }
                    178:
                    179: void
                    180: dbm_page_bysect(const struct dbm_match *match)
                    181: {
                    182:        assert(match != NULL);
                    183:        page_bytitle(ITER_SECT, match);
                    184: }
                    185:
                    186: void
                    187: dbm_page_byarch(const struct dbm_match *match)
                    188: {
                    189:        assert(match != NULL);
                    190:        page_byarch(match);
                    191: }
                    192:
                    193: void
                    194: dbm_page_bydesc(const struct dbm_match *match)
                    195: {
                    196:        assert(match != NULL);
                    197:        page_bytitle(ITER_DESC, match);
                    198: }
                    199:
                    200: void
                    201: dbm_page_bymacro(int32_t im, const struct dbm_match *match)
                    202: {
                    203:        assert(im >= 0);
                    204:        assert(im < MACRO_MAX);
                    205:        assert(match != NULL);
                    206:        page_bymacro(im, match);
                    207: }
                    208:
                    209: /*
                    210:  * Return the number of the next manual page in the current iteration.
                    211:  */
                    212: struct dbm_res
                    213: dbm_page_next(void)
                    214: {
                    215:        struct dbm_res                   res = {-1, 0};
                    216:
                    217:        switch(iteration) {
                    218:        case ITER_NONE:
                    219:                return res;
                    220:        case ITER_ARCH:
                    221:                return page_byarch(NULL);
                    222:        case ITER_MACRO:
                    223:                return page_bymacro(0, NULL);
                    224:        default:
                    225:                return page_bytitle(iteration, NULL);
                    226:        }
                    227: }
                    228:
                    229: /*
                    230:  * Functions implementing the iteration over manual pages.
                    231:  */
                    232: static struct dbm_res
                    233: page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
                    234: {
                    235:        static const struct dbm_match   *match;
                    236:        static const char               *cp;
                    237:        static int32_t                   ip;
                    238:        struct dbm_res                   res = {-1, 0};
                    239:
                    240:        assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
                    241:            arg_iter == ITER_SECT);
                    242:
                    243:        /* Initialize for a new iteration. */
                    244:
                    245:        if (arg_match != NULL) {
                    246:                iteration = arg_iter;
                    247:                match = arg_match;
                    248:                switch (iteration) {
                    249:                case ITER_NAME:
                    250:                        cp = dbm_get(pages[0].name);
                    251:                        break;
                    252:                case ITER_SECT:
                    253:                        cp = dbm_get(pages[0].sect);
                    254:                        break;
                    255:                case ITER_DESC:
                    256:                        cp = dbm_get(pages[0].desc);
                    257:                        break;
                    258:                default:
                    259:                        abort();
                    260:                }
1.4     ! schwarze  261:                if (cp == NULL) {
        !           262:                        iteration = ITER_NONE;
        !           263:                        match = NULL;
        !           264:                        cp = NULL;
        !           265:                        ip = npages;
        !           266:                } else
        !           267:                        ip = 0;
1.1       schwarze  268:                return res;
                    269:        }
                    270:
                    271:        /* Search for a name. */
                    272:
                    273:        while (ip < npages) {
                    274:                if (iteration == ITER_NAME)
                    275:                        cp++;
                    276:                if (dbm_match(match, cp))
                    277:                        break;
                    278:                cp = strchr(cp, '\0') + 1;
                    279:                if (iteration == ITER_DESC)
                    280:                        ip++;
                    281:                else if (*cp == '\0') {
                    282:                        cp++;
                    283:                        ip++;
                    284:                }
                    285:        }
                    286:
                    287:        /* Reached the end without a match. */
                    288:
                    289:        if (ip == npages) {
                    290:                iteration = ITER_NONE;
                    291:                match = NULL;
                    292:                cp = NULL;
                    293:                return res;
                    294:        }
                    295:
                    296:        /* Found a match; save the quality for later retrieval. */
                    297:
                    298:        res.page = ip;
                    299:        res.bits = iteration == ITER_NAME ? cp[-1] : 0;
                    300:
                    301:        /* Skip the remaining names of this page. */
                    302:
                    303:        if (++ip < npages) {
                    304:                do {
                    305:                        cp++;
                    306:                } while (cp[-1] != '\0' ||
                    307:                    (iteration != ITER_DESC && cp[-2] != '\0'));
                    308:        }
                    309:        return res;
                    310: }
                    311:
                    312: static struct dbm_res
                    313: page_byarch(const struct dbm_match *arg_match)
                    314: {
                    315:        static const struct dbm_match   *match;
                    316:        struct dbm_res                   res = {-1, 0};
                    317:        static int32_t                   ip;
                    318:        const char                      *cp;
                    319:
                    320:        /* Initialize for a new iteration. */
                    321:
                    322:        if (arg_match != NULL) {
                    323:                iteration = ITER_ARCH;
                    324:                match = arg_match;
                    325:                ip = 0;
                    326:                return res;
                    327:        }
                    328:
                    329:        /* Search for an architecture. */
                    330:
                    331:        for ( ; ip < npages; ip++)
                    332:                if (pages[ip].arch)
                    333:                        for (cp = dbm_get(pages[ip].arch);
                    334:                            *cp != '\0';
                    335:                            cp = strchr(cp, '\0') + 1)
                    336:                                if (dbm_match(match, cp)) {
                    337:                                        res.page = ip++;
                    338:                                        return res;
                    339:                                }
                    340:
                    341:        /* Reached the end without a match. */
                    342:
                    343:        iteration = ITER_NONE;
                    344:        match = NULL;
                    345:        return res;
                    346: }
                    347:
                    348: static struct dbm_res
                    349: page_bymacro(int32_t im, const struct dbm_match *match)
                    350: {
                    351:        static const int32_t    *pp;
                    352:        struct dbm_res           res = {-1, 0};
                    353:        const char              *cp;
                    354:        int32_t                  iv;
                    355:
                    356:        assert(im >= 0);
                    357:        assert(im < MACRO_MAX);
                    358:
                    359:        /* Initialize for a new iteration. */
                    360:
                    361:        if (match != NULL) {
                    362:                iteration = ITER_MACRO;
                    363:                cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
                    364:                for (iv = 0; iv < nvals[im]; iv++) {
                    365:                        if (dbm_match(match, cp))
                    366:                                break;
                    367:                        cp = strchr(cp, '\0') + 1;
                    368:                }
                    369:                pp = iv == nvals[im] ? NULL : dbm_get(macros[im][iv].pages);
                    370:                return res;
                    371:        }
                    372:        if (iteration != ITER_MACRO)
                    373:                return res;
                    374:
                    375:        /* No more matches. */
                    376:
                    377:        if (pp == NULL || *pp == 0) {
                    378:                iteration = ITER_NONE;
                    379:                pp = NULL;
                    380:                return res;
                    381:        }
                    382:
                    383:        /* Found a match. */
                    384:
                    385:        res.page = (struct page *)dbm_get(*pp++) - pages;
                    386:        return res;
                    387: }
                    388:
                    389:
                    390: /*** functions for handling macros ************************************/
                    391:
                    392: int32_t
                    393: dbm_macro_count(int32_t im)
                    394: {
                    395:        assert(im >= 0);
                    396:        assert(im < MACRO_MAX);
                    397:        return nvals[im];
                    398: }
                    399:
                    400: struct dbm_macro *
                    401: dbm_macro_get(int32_t im, int32_t iv)
                    402: {
                    403:        static struct dbm_macro macro;
                    404:
                    405:        assert(im >= 0);
                    406:        assert(im < MACRO_MAX);
                    407:        assert(iv >= 0);
                    408:        assert(iv < nvals[im]);
                    409:        macro.value = dbm_get(macros[im][iv].value);
                    410:        macro.pp = dbm_get(macros[im][iv].pages);
                    411:        return &macro;
                    412: }
                    413:
                    414: /*
                    415:  * Filtered iteration over macro entries.
                    416:  */
                    417: void
                    418: dbm_macro_bypage(int32_t im, int32_t ip)
                    419: {
                    420:        assert(im >= 0);
                    421:        assert(im < MACRO_MAX);
                    422:        assert(ip != 0);
                    423:        macro_bypage(im, ip);
                    424: }
                    425:
                    426: char *
                    427: dbm_macro_next(void)
                    428: {
                    429:        return macro_bypage(MACRO_MAX, 0);
                    430: }
                    431:
                    432: static char *
                    433: macro_bypage(int32_t arg_im, int32_t arg_ip)
                    434: {
                    435:        static const int32_t    *pp;
                    436:        static int32_t           im, ip, iv;
                    437:
                    438:        /* Initialize for a new iteration. */
                    439:
                    440:        if (arg_im < MACRO_MAX && arg_ip != 0) {
                    441:                im = arg_im;
                    442:                ip = arg_ip;
                    443:                pp = dbm_get(macros[im]->pages);
                    444:                iv = 0;
                    445:                return NULL;
                    446:        }
                    447:        if (im >= MACRO_MAX)
                    448:                return NULL;
                    449:
                    450:        /* Search for the next value. */
                    451:
                    452:        while (iv < nvals[im]) {
                    453:                if (*pp == ip)
                    454:                        break;
                    455:                if (*pp == 0)
                    456:                        iv++;
                    457:                pp++;
                    458:        }
                    459:
                    460:        /* Reached the end without a match. */
                    461:
                    462:        if (iv == nvals[im]) {
                    463:                im = MACRO_MAX;
                    464:                ip = 0;
                    465:                pp = NULL;
                    466:                return NULL;
                    467:        }
                    468:
                    469:        /* Found a match; skip the remaining pages of this entry. */
                    470:
                    471:        if (++iv < nvals[im])
                    472:                while (*pp++ != 0)
                    473:                        continue;
                    474:
                    475:        return dbm_get(macros[im][iv - 1].value);
                    476: }

CVSweb