Annotation of src/usr.bin/mandoc/dba.c, Revision 1.1
1.1 ! schwarze 1: /* $OpenBSD$ */
! 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: * Allocation-based version of the mandoc database, for read-write access.
! 18: * The interface is defined in "dba.h".
! 19: */
! 20: #include <sys/types.h>
! 21: #include <errno.h>
! 22: #include <stdint.h>
! 23: #include <stdlib.h>
! 24: #include <string.h>
! 25: #include <unistd.h>
! 26:
! 27: #include "mandoc_aux.h"
! 28: #include "mansearch.h"
! 29: #include "dba_write.h"
! 30: #include "dba_array.h"
! 31: #include "dba.h"
! 32:
! 33: static void *prepend(const char *, char);
! 34: static void dba_pages_write(struct dba_array *);
! 35: static int compare_names(const void *, const void *);
! 36: static int compare_strings(const void *, const void *);
! 37: static void dba_macros_write(struct dba_array *);
! 38: static void dba_macro_write(struct dba_array *);
! 39:
! 40:
! 41: /*** top-level functions **********************************************/
! 42:
! 43: struct dba *
! 44: dba_new(int32_t npages)
! 45: {
! 46: struct dba *dba;
! 47: int32_t im;
! 48:
! 49: dba = mandoc_malloc(sizeof(*dba));
! 50: dba->pages = dba_array_new(npages, DBA_GROW);
! 51: dba->macros = dba_array_new(MACRO_MAX, 0);
! 52: for (im = 0; im < MACRO_MAX; im++)
! 53: dba_array_set(dba->macros, im, dba_array_new(128, DBA_GROW));
! 54: return dba;
! 55: }
! 56:
! 57: void
! 58: dba_free(struct dba *dba)
! 59: {
! 60: struct dba_array *page, *macro, *entry;
! 61:
! 62: dba_array_FOREACH(dba->macros, macro) {
! 63: dba_array_undel(macro);
! 64: dba_array_FOREACH(macro, entry) {
! 65: free(dba_array_get(entry, 0));
! 66: dba_array_free(dba_array_get(entry, 1));
! 67: dba_array_free(entry);
! 68: }
! 69: dba_array_free(macro);
! 70: }
! 71: dba_array_free(dba->macros);
! 72:
! 73: dba_array_undel(dba->pages);
! 74: dba_array_FOREACH(dba->pages, page) {
! 75: dba_array_free(dba_array_get(page, DBP_NAME));
! 76: dba_array_free(dba_array_get(page, DBP_SECT));
! 77: dba_array_free(dba_array_get(page, DBP_ARCH));
! 78: free(dba_array_get(page, DBP_DESC));
! 79: dba_array_free(dba_array_get(page, DBP_FILE));
! 80: dba_array_free(page);
! 81: }
! 82: dba_array_free(dba->pages);
! 83:
! 84: free(dba);
! 85: }
! 86:
! 87: /*
! 88: * Write the complete mandoc database to disk; the format is:
! 89: * - One integer each for magic and version.
! 90: * - One pointer each to the macros table and to the final magic.
! 91: * - The pages table.
! 92: * - The macros table.
! 93: * - And at the very end, the magic integer again.
! 94: */
! 95: int
! 96: dba_write(const char *fname, struct dba *dba)
! 97: {
! 98: int save_errno;
! 99: int32_t pos_end, pos_macros, pos_macros_ptr;
! 100:
! 101: if (dba_open(fname) == -1)
! 102: return -1;
! 103: dba_int_write(MANDOCDB_MAGIC);
! 104: dba_int_write(MANDOCDB_VERSION);
! 105: pos_macros_ptr = dba_skip(1, 2);
! 106: dba_pages_write(dba->pages);
! 107: pos_macros = dba_tell();
! 108: dba_macros_write(dba->macros);
! 109: pos_end = dba_tell();
! 110: dba_int_write(MANDOCDB_MAGIC);
! 111: dba_seek(pos_macros_ptr);
! 112: dba_int_write(pos_macros);
! 113: dba_int_write(pos_end);
! 114: if (dba_close() == -1) {
! 115: save_errno = errno;
! 116: unlink(fname);
! 117: errno = save_errno;
! 118: return -1;
! 119: }
! 120: return 0;
! 121: }
! 122:
! 123:
! 124: /*** functions for handling pages *************************************/
! 125:
! 126: /*
! 127: * Create a new page and append it to the pages table.
! 128: */
! 129: struct dba_array *
! 130: dba_page_new(struct dba_array *pages, const char *name, const char *sect,
! 131: const char *arch, const char *desc, const char *file, enum form form)
! 132: {
! 133: struct dba_array *page, *entry;
! 134:
! 135: page = dba_array_new(DBP_MAX, 0);
! 136: entry = dba_array_new(1, DBA_STR | DBA_GROW);
! 137: dba_array_add(entry, prepend(name, NAME_FILE & NAME_MASK));
! 138: dba_array_add(page, entry);
! 139: entry = dba_array_new(1, DBA_STR | DBA_GROW);
! 140: dba_array_add(entry, (void *)sect);
! 141: dba_array_add(page, entry);
! 142: if (arch != NULL && *arch != '\0') {
! 143: entry = dba_array_new(1, DBA_STR | DBA_GROW);
! 144: dba_array_add(entry, (void *)arch);
! 145: } else
! 146: entry = NULL;
! 147: dba_array_add(page, entry);
! 148: dba_array_add(page, mandoc_strdup(desc));
! 149: entry = dba_array_new(1, DBA_STR | DBA_GROW);
! 150: dba_array_add(entry, prepend(file, form));
! 151: dba_array_add(page, entry);
! 152: dba_array_add(pages, page);
! 153: return page;
! 154: }
! 155:
! 156: /*
! 157: * Add a section, architecture, or file name to an existing page.
! 158: * Passing the NULL pointer for the architecture makes the page MI.
! 159: * In that case, any earlier or later architectures are ignored.
! 160: */
! 161: void
! 162: dba_page_add(struct dba_array *page, int32_t ie, const char *str)
! 163: {
! 164: struct dba_array *entries;
! 165: char *entry;
! 166:
! 167: entries = dba_array_get(page, ie);
! 168: if (ie == DBP_ARCH) {
! 169: if (entries == NULL)
! 170: return;
! 171: if (str == NULL) {
! 172: dba_array_free(entries);
! 173: dba_array_set(page, DBP_ARCH, NULL);
! 174: return;
! 175: }
! 176: }
! 177: if (*str == '\0')
! 178: return;
! 179: dba_array_FOREACH(entries, entry) {
! 180: if (ie == DBP_FILE && *entry < ' ')
! 181: entry++;
! 182: if (strcmp(entry, str) == 0)
! 183: return;
! 184: }
! 185: dba_array_add(entries, (void *)str);
! 186: }
! 187:
! 188: /*
! 189: * Add an additional name to an existing page.
! 190: */
! 191: void
! 192: dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
! 193: {
! 194: struct dba_array *entries;
! 195: char *entry;
! 196: char maskbyte;
! 197:
! 198: if (*name == '\0')
! 199: return;
! 200: maskbyte = mask & NAME_MASK;
! 201: entries = dba_array_get(page, DBP_NAME);
! 202: dba_array_FOREACH(entries, entry) {
! 203: if (strcmp(entry + 1, name) == 0) {
! 204: *entry |= maskbyte;
! 205: return;
! 206: }
! 207: }
! 208: dba_array_add(entries, prepend(name, maskbyte));
! 209: }
! 210:
! 211: /*
! 212: * Return a pointer to a temporary copy of instr with inbyte prepended.
! 213: */
! 214: static void *
! 215: prepend(const char *instr, char inbyte)
! 216: {
! 217: static char *outstr = NULL;
! 218: static size_t outlen = 0;
! 219: size_t newlen;
! 220:
! 221: newlen = strlen(instr) + 1;
! 222: if (newlen > outlen) {
! 223: outstr = mandoc_realloc(outstr, newlen + 1);
! 224: outlen = newlen;
! 225: }
! 226: *outstr = inbyte;
! 227: memcpy(outstr + 1, instr, newlen);
! 228: return outstr;
! 229: }
! 230:
! 231: /*
! 232: * Write the pages table to disk; the format is:
! 233: * - One integer containing the number of pages.
! 234: * - For each page, five pointers to the names, sections,
! 235: * architectures, description, and file names of the page.
! 236: * MI pages write 0 instead of the architecture pointer.
! 237: * - One list each for names, sections, architectures, descriptions and
! 238: * file names. The description for each page ends with a NUL byte.
! 239: * For all the other lists, each string ends with a NUL byte,
! 240: * and the last string for a page ends with two NUL bytes.
! 241: * - To assure alignment of following integers,
! 242: * the end is padded with NUL bytes up to a multiple of four bytes.
! 243: */
! 244: static void
! 245: dba_pages_write(struct dba_array *pages)
! 246: {
! 247: struct dba_array *page, *entry;
! 248: int32_t pos_pages, pos_end;
! 249:
! 250: pos_pages = dba_array_writelen(pages, 5);
! 251: dba_array_FOREACH(pages, page) {
! 252: dba_array_setpos(page, DBP_NAME, dba_tell());
! 253: entry = dba_array_get(page, DBP_NAME);
! 254: dba_array_sort(entry, compare_names);
! 255: dba_array_writelst(entry);
! 256: }
! 257: dba_array_FOREACH(pages, page) {
! 258: dba_array_setpos(page, DBP_SECT, dba_tell());
! 259: entry = dba_array_get(page, DBP_SECT);
! 260: dba_array_sort(entry, compare_strings);
! 261: dba_array_writelst(entry);
! 262: }
! 263: dba_array_FOREACH(pages, page) {
! 264: if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
! 265: dba_array_setpos(page, DBP_ARCH, dba_tell());
! 266: dba_array_sort(entry, compare_strings);
! 267: dba_array_writelst(entry);
! 268: } else
! 269: dba_array_setpos(page, DBP_ARCH, 0);
! 270: }
! 271: dba_array_FOREACH(pages, page) {
! 272: dba_array_setpos(page, DBP_DESC, dba_tell());
! 273: dba_str_write(dba_array_get(page, DBP_DESC));
! 274: }
! 275: dba_array_FOREACH(pages, page) {
! 276: dba_array_setpos(page, DBP_FILE, dba_tell());
! 277: dba_array_writelst(dba_array_get(page, DBP_FILE));
! 278: }
! 279: pos_end = dba_align();
! 280: dba_seek(pos_pages);
! 281: dba_array_FOREACH(pages, page)
! 282: dba_array_writepos(page);
! 283: dba_seek(pos_end);
! 284: }
! 285:
! 286: static int
! 287: compare_names(const void *vp1, const void *vp2)
! 288: {
! 289: const char *cp1, *cp2;
! 290: int diff;
! 291:
! 292: cp1 = *(char **)vp1;
! 293: cp2 = *(char **)vp2;
! 294: return (diff = *cp2 - *cp1) ? diff :
! 295: strcasecmp(cp1 + 1, cp2 + 1);
! 296: }
! 297:
! 298: static int
! 299: compare_strings(const void *vp1, const void *vp2)
! 300: {
! 301: const char *cp1, *cp2;
! 302:
! 303: cp1 = *(char **)vp1;
! 304: cp2 = *(char **)vp2;
! 305: return strcmp(cp1, cp2);
! 306: }
! 307:
! 308: /*** functions for handling macros ************************************/
! 309:
! 310: /*
! 311: * Create a new macro entry and append it to one of the macro tables.
! 312: */
! 313: void
! 314: dba_macro_new(struct dba *dba, int32_t im, const char *value,
! 315: const int32_t *pp)
! 316: {
! 317: struct dba_array *entry, *pages;
! 318: const int32_t *ip;
! 319: int32_t np;
! 320:
! 321: np = 0;
! 322: for (ip = pp; *ip; ip++)
! 323: np++;
! 324: pages = dba_array_new(np, DBA_GROW);
! 325: for (ip = pp; *ip; ip++)
! 326: dba_array_add(pages, dba_array_get(dba->pages,
! 327: be32toh(*ip) / 5 / sizeof(*ip) - 1));
! 328:
! 329: entry = dba_array_new(2, 0);
! 330: dba_array_add(entry, mandoc_strdup(value));
! 331: dba_array_add(entry, pages);
! 332:
! 333: dba_array_add(dba_array_get(dba->macros, im), entry);
! 334: }
! 335:
! 336: /*
! 337: * Look up a macro entry by value and add a reference to a new page to it.
! 338: * If the value does not yet exist, create a new macro entry
! 339: * and add it to the macro table in question.
! 340: */
! 341: void
! 342: dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
! 343: struct dba_array *page)
! 344: {
! 345: struct dba_array *macro, *entry, *pages;
! 346:
! 347: if (*value == '\0')
! 348: return;
! 349: macro = dba_array_get(macros, im);
! 350: dba_array_FOREACH(macro, entry)
! 351: if (strcmp(value, dba_array_get(entry, 0)) == 0)
! 352: break;
! 353: if (entry == NULL) {
! 354: entry = dba_array_new(2, 0);
! 355: dba_array_add(entry, mandoc_strdup(value));
! 356: pages = dba_array_new(1, DBA_GROW);
! 357: dba_array_add(entry, pages);
! 358: dba_array_add(macro, entry);
! 359: } else
! 360: pages = dba_array_get(entry, 1);
! 361: dba_array_add(pages, page);
! 362: }
! 363:
! 364: /*
! 365: * Write the macros table to disk; the format is:
! 366: * - The number of macro tables (actually, MACRO_MAX).
! 367: * - That number of pointers to the individual macro tables.
! 368: * - The individual macro tables.
! 369: */
! 370: static void
! 371: dba_macros_write(struct dba_array *macros)
! 372: {
! 373: struct dba_array *macro;
! 374: int32_t im, pos_macros, pos_end;
! 375:
! 376: pos_macros = dba_array_writelen(macros, 1);
! 377: im = 0;
! 378: dba_array_FOREACH(macros, macro) {
! 379: dba_array_setpos(macros, im++, dba_tell());
! 380: dba_macro_write(macro);
! 381: }
! 382: pos_end = dba_tell();
! 383: dba_seek(pos_macros);
! 384: dba_array_writepos(macros);
! 385: dba_seek(pos_end);
! 386: }
! 387:
! 388: /*
! 389: * Write one individual macro table to disk; the format is:
! 390: * - The number of entries in the table.
! 391: * - For each entry, two pointers, the first one to the value
! 392: * and the second one to the list of pages.
! 393: * - A list of values, each ending in a NUL byte.
! 394: * - To assure alignment of following integers,
! 395: * padding with NUL bytes up to a multiple of four bytes.
! 396: * - A list of pointers to pages, each list ending in a 0 integer.
! 397: */
! 398: static void
! 399: dba_macro_write(struct dba_array *macro)
! 400: {
! 401: struct dba_array *entry, *pages, *page;
! 402: int empty;
! 403: int32_t addr, pos_macro, pos_end;
! 404:
! 405: dba_array_FOREACH(macro, entry) {
! 406: pages = dba_array_get(entry, 1);
! 407: empty = 1;
! 408: dba_array_FOREACH(pages, page)
! 409: if (dba_array_getpos(page))
! 410: empty = 0;
! 411: if (empty)
! 412: dba_array_del(macro);
! 413: }
! 414: pos_macro = dba_array_writelen(macro, 2);
! 415: dba_array_FOREACH(macro, entry) {
! 416: dba_array_setpos(entry, 0, dba_tell());
! 417: dba_str_write(dba_array_get(entry, 0));
! 418: }
! 419: dba_align();
! 420: dba_array_FOREACH(macro, entry) {
! 421: dba_array_setpos(entry, 1, dba_tell());
! 422: pages = dba_array_get(entry, 1);
! 423: dba_array_FOREACH(pages, page)
! 424: if ((addr = dba_array_getpos(page)))
! 425: dba_int_write(addr);
! 426: dba_int_write(0);
! 427: }
! 428: pos_end = dba_tell();
! 429: dba_seek(pos_macro);
! 430: dba_array_FOREACH(macro, entry)
! 431: dba_array_writepos(entry);
! 432: dba_seek(pos_end);
! 433: }