Annotation of src/usr.bin/mandoc/man.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: man.c,v 1.16 2009/04/05 16:34:22 kristaps Exp $ */
! 2: /*
! 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@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
! 7: * above copyright notice and this permission notice appear in all
! 8: * copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
! 11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
! 12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
! 13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
! 14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
! 15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
! 16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
! 17: * PERFORMANCE OF THIS SOFTWARE.
! 18: */
! 19: #include <assert.h>
! 20: #include <ctype.h>
! 21: #include <stdarg.h>
! 22: #include <stdlib.h>
! 23: #include <stdio.h>
! 24: #include <string.h>
! 25:
! 26: #include "libman.h"
! 27:
! 28: const char *const __man_macronames[MAN_MAX] = {
! 29: "\\\"", "TH", "SH", "SS",
! 30: "TP", "LP", "PP", "P",
! 31: "IP", "HP", "SM", "SB",
! 32: "BI", "IB", "BR", "RB",
! 33: "R", "B", "I", "IR",
! 34: "RI", "br", "na", "i"
! 35: };
! 36:
! 37: const char * const *man_macronames = __man_macronames;
! 38:
! 39: static struct man_node *man_node_alloc(int, int,
! 40: enum man_type, int);
! 41: static int man_node_append(struct man *,
! 42: struct man_node *);
! 43: static int man_ptext(struct man *, int, char *);
! 44: static int man_pmacro(struct man *, int, char *);
! 45: static void man_free1(struct man *);
! 46: static int man_alloc1(struct man *);
! 47:
! 48:
! 49: const struct man_node *
! 50: man_node(const struct man *m)
! 51: {
! 52:
! 53: return(MAN_HALT & m->flags ? NULL : m->first);
! 54: }
! 55:
! 56:
! 57: const struct man_meta *
! 58: man_meta(const struct man *m)
! 59: {
! 60:
! 61: return(MAN_HALT & m->flags ? NULL : &m->meta);
! 62: }
! 63:
! 64:
! 65: int
! 66: man_reset(struct man *man)
! 67: {
! 68:
! 69: man_free1(man);
! 70: return(man_alloc1(man));
! 71: }
! 72:
! 73:
! 74: void
! 75: man_free(struct man *man)
! 76: {
! 77:
! 78: man_free1(man);
! 79:
! 80: if (man->htab)
! 81: man_hash_free(man->htab);
! 82: free(man);
! 83: }
! 84:
! 85:
! 86: struct man *
! 87: man_alloc(void *data, int pflags, const struct man_cb *cb)
! 88: {
! 89: struct man *p;
! 90:
! 91: if (NULL == (p = calloc(1, sizeof(struct man))))
! 92: return(NULL);
! 93:
! 94: if ( ! man_alloc1(p)) {
! 95: free(p);
! 96: return(NULL);
! 97: }
! 98:
! 99: p->data = data;
! 100: p->pflags = pflags;
! 101: (void)memcpy(&p->cb, cb, sizeof(struct man_cb));
! 102:
! 103: if (NULL == (p->htab = man_hash_alloc())) {
! 104: free(p);
! 105: return(NULL);
! 106: }
! 107: return(p);
! 108: }
! 109:
! 110:
! 111: int
! 112: man_endparse(struct man *m)
! 113: {
! 114:
! 115: if (MAN_HALT & m->flags)
! 116: return(0);
! 117: else if (man_macroend(m))
! 118: return(1);
! 119: m->flags |= MAN_HALT;
! 120: return(0);
! 121: }
! 122:
! 123:
! 124: int
! 125: man_parseln(struct man *m, int ln, char *buf)
! 126: {
! 127:
! 128: return('.' == *buf ?
! 129: man_pmacro(m, ln, buf) :
! 130: man_ptext(m, ln, buf));
! 131: }
! 132:
! 133:
! 134: static void
! 135: man_free1(struct man *man)
! 136: {
! 137:
! 138: if (man->first)
! 139: man_node_freelist(man->first);
! 140: if (man->meta.title)
! 141: free(man->meta.title);
! 142: if (man->meta.source)
! 143: free(man->meta.source);
! 144: if (man->meta.vol)
! 145: free(man->meta.vol);
! 146: }
! 147:
! 148:
! 149: static int
! 150: man_alloc1(struct man *m)
! 151: {
! 152:
! 153: bzero(&m->meta, sizeof(struct man_meta));
! 154: m->flags = 0;
! 155: m->last = calloc(1, sizeof(struct man_node));
! 156: if (NULL == m->last)
! 157: return(0);
! 158: m->first = m->last;
! 159: m->last->type = MAN_ROOT;
! 160: m->next = MAN_NEXT_CHILD;
! 161: return(1);
! 162: }
! 163:
! 164:
! 165: static int
! 166: man_node_append(struct man *man, struct man_node *p)
! 167: {
! 168:
! 169: assert(man->last);
! 170: assert(man->first);
! 171: assert(MAN_ROOT != p->type);
! 172:
! 173: switch (man->next) {
! 174: case (MAN_NEXT_SIBLING):
! 175: man->last->next = p;
! 176: p->prev = man->last;
! 177: p->parent = man->last->parent;
! 178: break;
! 179: case (MAN_NEXT_CHILD):
! 180: man->last->child = p;
! 181: p->parent = man->last;
! 182: break;
! 183: default:
! 184: abort();
! 185: /* NOTREACHED */
! 186: }
! 187:
! 188: man->last = p;
! 189:
! 190: switch (p->type) {
! 191: case (MAN_TEXT):
! 192: if ( ! man_valid_post(man))
! 193: return(0);
! 194: if ( ! man_action_post(man))
! 195: return(0);
! 196: break;
! 197: default:
! 198: break;
! 199: }
! 200:
! 201: return(1);
! 202: }
! 203:
! 204:
! 205: static struct man_node *
! 206: man_node_alloc(int line, int pos, enum man_type type, int tok)
! 207: {
! 208: struct man_node *p;
! 209:
! 210: p = calloc(1, sizeof(struct man_node));
! 211: if (NULL == p)
! 212: return(NULL);
! 213:
! 214: p->line = line;
! 215: p->pos = pos;
! 216: p->type = type;
! 217: p->tok = tok;
! 218: return(p);
! 219: }
! 220:
! 221:
! 222: int
! 223: man_elem_alloc(struct man *man, int line, int pos, int tok)
! 224: {
! 225: struct man_node *p;
! 226:
! 227: p = man_node_alloc(line, pos, MAN_ELEM, tok);
! 228: if (NULL == p)
! 229: return(0);
! 230: return(man_node_append(man, p));
! 231: }
! 232:
! 233:
! 234: int
! 235: man_word_alloc(struct man *man,
! 236: int line, int pos, const char *word)
! 237: {
! 238: struct man_node *p;
! 239:
! 240: p = man_node_alloc(line, pos, MAN_TEXT, -1);
! 241: if (NULL == p)
! 242: return(0);
! 243: if (NULL == (p->string = strdup(word)))
! 244: return(0);
! 245: return(man_node_append(man, p));
! 246: }
! 247:
! 248:
! 249: void
! 250: man_node_free(struct man_node *p)
! 251: {
! 252:
! 253: if (p->string)
! 254: free(p->string);
! 255: free(p);
! 256: }
! 257:
! 258:
! 259: void
! 260: man_node_freelist(struct man_node *p)
! 261: {
! 262:
! 263: if (p->child)
! 264: man_node_freelist(p->child);
! 265: if (p->next)
! 266: man_node_freelist(p->next);
! 267:
! 268: man_node_free(p);
! 269: }
! 270:
! 271:
! 272: static int
! 273: man_ptext(struct man *m, int line, char *buf)
! 274: {
! 275:
! 276: if ( ! man_word_alloc(m, line, 0, buf))
! 277: return(0);
! 278: m->next = MAN_NEXT_SIBLING;
! 279:
! 280: /*
! 281: * If this is one of the zany NLINE macros that consumes the
! 282: * next line of input as being influenced, then close out the
! 283: * existing macro "scope" and continue processing.
! 284: */
! 285:
! 286: if ( ! (MAN_NLINE & m->flags))
! 287: return(1);
! 288:
! 289: m->flags &= ~MAN_NLINE;
! 290: m->last = m->last->parent;
! 291:
! 292: assert(MAN_ROOT != m->last->type);
! 293: if ( ! man_valid_post(m))
! 294: return(0);
! 295: if ( ! man_action_post(m))
! 296: return(0);
! 297:
! 298: return(1);
! 299: }
! 300:
! 301:
! 302: int
! 303: man_pmacro(struct man *m, int ln, char *buf)
! 304: {
! 305: int i, j, c, ppos, fl;
! 306: char mac[5];
! 307: struct man_node *n;
! 308:
! 309: /* Comments and empties are quickly ignored. */
! 310:
! 311: n = m->last;
! 312: fl = MAN_NLINE & m->flags;
! 313:
! 314: if (0 == buf[1])
! 315: goto out;
! 316:
! 317: i = 1;
! 318:
! 319: if (' ' == buf[i]) {
! 320: i++;
! 321: while (buf[i] && ' ' == buf[i])
! 322: i++;
! 323: if (0 == buf[i])
! 324: goto out;
! 325: }
! 326:
! 327: ppos = i;
! 328:
! 329: if (buf[i] && '\\' == buf[i])
! 330: if (buf[i + 1] && '\"' == buf[i + 1])
! 331: goto out;
! 332:
! 333: /* Copy the first word into a nil-terminated buffer. */
! 334:
! 335: for (j = 0; j < 4; j++, i++) {
! 336: if (0 == (mac[j] = buf[i]))
! 337: break;
! 338: else if (' ' == buf[i])
! 339: break;
! 340: }
! 341:
! 342: mac[j] = 0;
! 343:
! 344: if (j == 4 || j < 1) {
! 345: if ( ! (MAN_IGN_MACRO & m->pflags)) {
! 346: (void)man_verr(m, ln, ppos,
! 347: "ill-formed macro: %s", mac);
! 348: goto err;
! 349: }
! 350: if ( ! man_vwarn(m, ln, ppos,
! 351: "ill-formed macro: %s", mac))
! 352: goto err;
! 353: return(1);
! 354: }
! 355:
! 356: if (MAN_MAX == (c = man_hash_find(m->htab, mac))) {
! 357: if ( ! (MAN_IGN_MACRO & m->pflags)) {
! 358: (void)man_verr(m, ln, ppos,
! 359: "unknown macro: %s", mac);
! 360: goto err;
! 361: }
! 362: if ( ! man_vwarn(m, ln, ppos,
! 363: "unknown macro: %s", mac))
! 364: goto err;
! 365: return(1);
! 366: }
! 367:
! 368: /* The macro is sane. Jump to the next word. */
! 369:
! 370: while (buf[i] && ' ' == buf[i])
! 371: i++;
! 372:
! 373: /* Begin recursive parse sequence. */
! 374:
! 375: if ( ! man_macro(m, c, ln, ppos, &i, buf))
! 376: goto err;
! 377:
! 378: out:
! 379: if (fl) {
! 380: /*
! 381: * A NLINE macro has been immediately followed with
! 382: * another. Close out the preceeding macro's scope, and
! 383: * continue.
! 384: */
! 385: assert(MAN_ROOT != m->last->type);
! 386: assert(m->last->parent);
! 387: assert(MAN_ROOT != m->last->parent->type);
! 388:
! 389: if (n != m->last)
! 390: m->last = m->last->parent;
! 391:
! 392: if ( ! man_valid_post(m))
! 393: return(0);
! 394: if ( ! man_action_post(m))
! 395: return(0);
! 396: m->next = MAN_NEXT_SIBLING;
! 397: m->flags &= ~MAN_NLINE;
! 398: }
! 399:
! 400: return(1);
! 401:
! 402: err: /* Error out. */
! 403:
! 404: m->flags |= MAN_HALT;
! 405: return(0);
! 406: }
! 407:
! 408:
! 409: int
! 410: man_verr(struct man *man, int ln, int pos, const char *fmt, ...)
! 411: {
! 412: char buf[256];
! 413: va_list ap;
! 414:
! 415: if (NULL == man->cb.man_err)
! 416: return(0);
! 417:
! 418: va_start(ap, fmt);
! 419: (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
! 420: va_end(ap);
! 421: return((*man->cb.man_err)(man->data, ln, pos, buf));
! 422: }
! 423:
! 424:
! 425: int
! 426: man_vwarn(struct man *man, int ln, int pos, const char *fmt, ...)
! 427: {
! 428: char buf[256];
! 429: va_list ap;
! 430:
! 431: if (NULL == man->cb.man_warn)
! 432: return(0);
! 433:
! 434: va_start(ap, fmt);
! 435: (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
! 436: va_end(ap);
! 437: return((*man->cb.man_warn)(man->data, ln, pos, buf));
! 438: }
! 439:
! 440: