1 /* $Id: man.c,v 1.13 2009/09/21 21:11:37 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 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 #include <assert.h> 18 #include <ctype.h> 19 #include <stdarg.h> 20 #include <stdlib.h> 21 #include <stdio.h> 22 #include <string.h> 23 24 #include "libman.h" 25 26 const char *const __man_merrnames[WERRMAX] = { 27 "invalid character", /* WNPRINT */ 28 "system: malloc error", /* WNMEM */ 29 "invalid manual section", /* WMSEC */ 30 "invalid date format", /* WDATE */ 31 "scope of prior line violated", /* WLNSCOPE */ 32 "trailing whitespace", /* WTSPACE */ 33 "unterminated quoted parameter", /* WTQUOTE */ 34 "document has no body", /* WNODATA */ 35 "document has no title/section", /* WNOTITLE */ 36 "invalid escape sequence", /* WESCAPE */ 37 "invalid number format", /* WNUMFMT */ 38 "expected block head arguments", /* WHEADARGS */ 39 "expected block body arguments", /* WBODYARGS */ 40 "expected empty block head", /* WNHEADARGS */ 41 "unknown macro", /* WMACRO */ 42 "ill-formed macro", /* WMACROFORM */ 43 "scope open on exit", /* WEXITSCOPE */ 44 "no scope context", /* WNOSCOPE */ 45 "literal context already open", /* WOLITERAL */ 46 "no literal context open" /* WNLITERAL */ 47 }; 48 49 const char *const __man_macronames[MAN_MAX] = { 50 "br", "TH", "SH", "SS", 51 "TP", "LP", "PP", "P", 52 "IP", "HP", "SM", "SB", 53 "BI", "IB", "BR", "RB", 54 "R", "B", "I", "IR", 55 "RI", "na", "i", "sp", 56 "nf", "fi", "r", "RE", 57 "RS", "DT", "UC" 58 }; 59 60 const char * const *man_macronames = __man_macronames; 61 62 static struct man_node *man_node_alloc(int, int, 63 enum man_type, int); 64 static int man_node_append(struct man *, 65 struct man_node *); 66 static int man_ptext(struct man *, int, char *); 67 static int man_pmacro(struct man *, int, char *); 68 static void man_free1(struct man *); 69 static int man_alloc1(struct man *); 70 static int pstring(struct man *, int, int, 71 const char *, size_t); 72 73 74 const struct man_node * 75 man_node(const struct man *m) 76 { 77 78 return(MAN_HALT & m->flags ? NULL : m->first); 79 } 80 81 82 const struct man_meta * 83 man_meta(const struct man *m) 84 { 85 86 return(MAN_HALT & m->flags ? NULL : &m->meta); 87 } 88 89 90 int 91 man_reset(struct man *man) 92 { 93 94 man_free1(man); 95 return(man_alloc1(man)); 96 } 97 98 99 void 100 man_free(struct man *man) 101 { 102 103 man_free1(man); 104 free(man); 105 } 106 107 108 struct man * 109 man_alloc(void *data, int pflags, const struct man_cb *cb) 110 { 111 struct man *p; 112 113 if (NULL == (p = calloc(1, sizeof(struct man)))) 114 return(NULL); 115 116 if ( ! man_alloc1(p)) { 117 free(p); 118 return(NULL); 119 } 120 121 man_hash_init(); 122 123 p->data = data; 124 p->pflags = pflags; 125 (void)memcpy(&p->cb, cb, sizeof(struct man_cb)); 126 return(p); 127 } 128 129 130 int 131 man_endparse(struct man *m) 132 { 133 134 if (MAN_HALT & m->flags) 135 return(0); 136 else if (man_macroend(m)) 137 return(1); 138 m->flags |= MAN_HALT; 139 return(0); 140 } 141 142 143 int 144 man_parseln(struct man *m, int ln, char *buf) 145 { 146 147 return('.' == *buf ? 148 man_pmacro(m, ln, buf) : 149 man_ptext(m, ln, buf)); 150 } 151 152 153 static void 154 man_free1(struct man *man) 155 { 156 157 if (man->first) 158 man_node_freelist(man->first); 159 if (man->meta.title) 160 free(man->meta.title); 161 if (man->meta.source) 162 free(man->meta.source); 163 if (man->meta.vol) 164 free(man->meta.vol); 165 } 166 167 168 static int 169 man_alloc1(struct man *m) 170 { 171 172 bzero(&m->meta, sizeof(struct man_meta)); 173 m->flags = 0; 174 m->last = calloc(1, sizeof(struct man_node)); 175 if (NULL == m->last) 176 return(0); 177 m->first = m->last; 178 m->last->type = MAN_ROOT; 179 m->next = MAN_NEXT_CHILD; 180 return(1); 181 } 182 183 184 static int 185 man_node_append(struct man *man, struct man_node *p) 186 { 187 188 assert(man->last); 189 assert(man->first); 190 assert(MAN_ROOT != p->type); 191 192 switch (man->next) { 193 case (MAN_NEXT_SIBLING): 194 man->last->next = p; 195 p->prev = man->last; 196 p->parent = man->last->parent; 197 break; 198 case (MAN_NEXT_CHILD): 199 man->last->child = p; 200 p->parent = man->last; 201 break; 202 default: 203 abort(); 204 /* NOTREACHED */ 205 } 206 207 p->parent->nchild++; 208 209 if ( ! man_valid_pre(man, p)) 210 return(0); 211 212 switch (p->type) { 213 case (MAN_HEAD): 214 assert(MAN_BLOCK == p->parent->type); 215 p->parent->head = p; 216 break; 217 case (MAN_BODY): 218 assert(MAN_BLOCK == p->parent->type); 219 p->parent->body = p; 220 break; 221 default: 222 break; 223 } 224 225 man->last = p; 226 227 switch (p->type) { 228 case (MAN_TEXT): 229 if ( ! man_valid_post(man)) 230 return(0); 231 if ( ! man_action_post(man)) 232 return(0); 233 break; 234 default: 235 break; 236 } 237 238 return(1); 239 } 240 241 242 static struct man_node * 243 man_node_alloc(int line, int pos, enum man_type type, int tok) 244 { 245 struct man_node *p; 246 247 p = calloc(1, sizeof(struct man_node)); 248 if (NULL == p) 249 return(NULL); 250 251 p->line = line; 252 p->pos = pos; 253 p->type = type; 254 p->tok = tok; 255 return(p); 256 } 257 258 259 int 260 man_elem_alloc(struct man *m, int line, int pos, int tok) 261 { 262 struct man_node *p; 263 264 p = man_node_alloc(line, pos, MAN_ELEM, tok); 265 if (NULL == p) 266 return(0); 267 if ( ! man_node_append(m, p)) 268 return(0); 269 m->next = MAN_NEXT_CHILD; 270 return(1); 271 } 272 273 274 int 275 man_head_alloc(struct man *m, int line, int pos, int tok) 276 { 277 struct man_node *p; 278 279 p = man_node_alloc(line, pos, MAN_HEAD, tok); 280 if (NULL == p) 281 return(0); 282 if ( ! man_node_append(m, p)) 283 return(0); 284 m->next = MAN_NEXT_CHILD; 285 return(1); 286 } 287 288 289 int 290 man_body_alloc(struct man *m, int line, int pos, int tok) 291 { 292 struct man_node *p; 293 294 p = man_node_alloc(line, pos, MAN_BODY, tok); 295 if (NULL == p) 296 return(0); 297 if ( ! man_node_append(m, p)) 298 return(0); 299 m->next = MAN_NEXT_CHILD; 300 return(1); 301 } 302 303 304 int 305 man_block_alloc(struct man *m, int line, int pos, int tok) 306 { 307 struct man_node *p; 308 309 p = man_node_alloc(line, pos, MAN_BLOCK, tok); 310 if (NULL == p) 311 return(0); 312 if ( ! man_node_append(m, p)) 313 return(0); 314 m->next = MAN_NEXT_CHILD; 315 return(1); 316 } 317 318 319 static int 320 pstring(struct man *m, int line, int pos, 321 const char *p, size_t len) 322 { 323 struct man_node *n; 324 size_t sv; 325 326 n = man_node_alloc(line, pos, MAN_TEXT, -1); 327 if (NULL == n) 328 return(0); 329 330 n->string = malloc(len + 1); 331 if (NULL == n->string) { 332 free(n); 333 return(0); 334 } 335 336 sv = strlcpy(n->string, p, len + 1); 337 338 /* Prohibit truncation. */ 339 assert(sv < len + 1); 340 341 if ( ! man_node_append(m, n)) 342 return(0); 343 m->next = MAN_NEXT_SIBLING; 344 return(1); 345 } 346 347 348 int 349 man_word_alloc(struct man *m, int line, int pos, const char *word) 350 { 351 352 return(pstring(m, line, pos, word, strlen(word))); 353 } 354 355 356 void 357 man_node_free(struct man_node *p) 358 { 359 360 if (p->string) 361 free(p->string); 362 if (p->parent) 363 p->parent->nchild--; 364 free(p); 365 } 366 367 368 void 369 man_node_freelist(struct man_node *p) 370 { 371 struct man_node *n; 372 373 if (p->child) 374 man_node_freelist(p->child); 375 assert(0 == p->nchild); 376 n = p->next; 377 man_node_free(p); 378 if (n) 379 man_node_freelist(n); 380 } 381 382 383 static int 384 man_ptext(struct man *m, int line, char *buf) 385 { 386 int i, j; 387 388 /* Literal free-form text whitespace is preserved. */ 389 390 if (MAN_LITERAL & m->flags) { 391 if ( ! man_word_alloc(m, line, 0, buf)) 392 return(0); 393 goto descope; 394 } 395 396 /* First de-chunk and allocate words. */ 397 398 for (i = 0; ' ' == buf[i]; i++) 399 /* Skip leading whitespace. */ ; 400 if (0 == buf[i]) { 401 if ( ! pstring(m, line, 0, &buf[i], 0)) 402 return(0); 403 goto descope; 404 } 405 406 for (j = i; buf[i]; i++) { 407 if (' ' != buf[i]) 408 continue; 409 410 /* Escaped whitespace. */ 411 if (i && ' ' == buf[i] && '\\' == buf[i - 1]) 412 continue; 413 414 buf[i++] = 0; 415 if ( ! pstring(m, line, j, &buf[j], (size_t)(i - j))) 416 return(0); 417 418 for ( ; ' ' == buf[i]; i++) 419 /* Skip trailing whitespace. */ ; 420 421 j = i; 422 if (0 == buf[i]) 423 break; 424 } 425 426 if (j != i && ! pstring(m, line, j, &buf[j], (size_t)(i - j))) 427 return(0); 428 429 descope: 430 431 /* 432 * Co-ordinate what happens with having a next-line scope open: 433 * first close out the element scope (if applicable), then close 434 * out the block scope (also if applicable). 435 */ 436 437 if (MAN_ELINE & m->flags) { 438 m->flags &= ~MAN_ELINE; 439 if ( ! man_unscope(m, m->last->parent)) 440 return(0); 441 } 442 443 if ( ! (MAN_BLINE & m->flags)) 444 return(1); 445 m->flags &= ~MAN_BLINE; 446 447 if ( ! man_unscope(m, m->last->parent)) 448 return(0); 449 return(man_body_alloc(m, line, 0, m->last->tok)); 450 } 451 452 453 int 454 man_pmacro(struct man *m, int ln, char *buf) 455 { 456 int i, j, c, ppos, fl; 457 char mac[5]; 458 struct man_node *n; 459 460 /* Comments and empties are quickly ignored. */ 461 462 fl = m->flags; 463 464 if (0 == buf[1]) 465 goto out; 466 467 i = 1; 468 469 if (' ' == buf[i]) { 470 i++; 471 while (buf[i] && ' ' == buf[i]) 472 i++; 473 if (0 == buf[i]) 474 goto out; 475 } 476 477 ppos = i; 478 479 /* Copy the first word into a nil-terminated buffer. */ 480 481 for (j = 0; j < 4; j++, i++) { 482 if (0 == (mac[j] = buf[i])) 483 break; 484 else if (' ' == buf[i]) 485 break; 486 487 /* Check for invalid characters. */ 488 489 if (isgraph((u_char)buf[i])) 490 continue; 491 return(man_perr(m, ln, i, WNPRINT)); 492 } 493 494 mac[j] = 0; 495 496 if (j == 4 || j < 1) { 497 if ( ! (MAN_IGN_MACRO & m->pflags)) { 498 (void)man_perr(m, ln, ppos, WMACROFORM); 499 goto err; 500 } 501 if ( ! man_pwarn(m, ln, ppos, WMACROFORM)) 502 goto err; 503 return(1); 504 } 505 506 if (MAN_MAX == (c = man_hash_find(mac))) { 507 if ( ! (MAN_IGN_MACRO & m->pflags)) { 508 (void)man_perr(m, ln, ppos, WMACRO); 509 goto err; 510 } 511 if ( ! man_pwarn(m, ln, ppos, WMACRO)) 512 goto err; 513 return(1); 514 } 515 516 /* The macro is sane. Jump to the next word. */ 517 518 while (buf[i] && ' ' == buf[i]) 519 i++; 520 521 /* Remove prior ELINE macro, if applicable. */ 522 523 if (m->flags & MAN_ELINE) { 524 n = m->last; 525 assert(NULL == n->child); 526 assert(0 == n->nchild); 527 if ( ! man_nwarn(m, n, WLNSCOPE)) 528 return(0); 529 530 if (n->prev) { 531 assert(n != n->parent->child); 532 assert(n == n->prev->next); 533 n->prev->next = NULL; 534 m->last = n->prev; 535 m->next = MAN_NEXT_SIBLING; 536 } else { 537 assert(n == n->parent->child); 538 n->parent->child = NULL; 539 m->last = n->parent; 540 m->next = MAN_NEXT_CHILD; 541 } 542 543 man_node_free(n); 544 m->flags &= ~MAN_ELINE; 545 } 546 547 /* Begin recursive parse sequence. */ 548 549 assert(man_macros[c].fp); 550 551 if ( ! (*man_macros[c].fp)(m, c, ln, ppos, &i, buf)) 552 goto err; 553 554 out: 555 if ( ! (MAN_BLINE & fl)) 556 return(1); 557 558 /* 559 * If we've opened a new next-line element scope, then return 560 * now, as the next line will close out the block scope. 561 */ 562 563 if (MAN_ELINE & m->flags) 564 return(1); 565 566 /* Close out the block scope opened in the prior line. */ 567 568 assert(MAN_BLINE & m->flags); 569 m->flags &= ~MAN_BLINE; 570 571 if ( ! man_unscope(m, m->last->parent)) 572 return(0); 573 return(man_body_alloc(m, ln, 0, m->last->tok)); 574 575 err: /* Error out. */ 576 577 m->flags |= MAN_HALT; 578 return(0); 579 } 580 581 582 int 583 man_verr(struct man *man, int ln, int pos, const char *fmt, ...) 584 { 585 char buf[256]; 586 va_list ap; 587 588 if (NULL == man->cb.man_err) 589 return(0); 590 591 va_start(ap, fmt); 592 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 593 va_end(ap); 594 return((*man->cb.man_err)(man->data, ln, pos, buf)); 595 } 596 597 598 int 599 man_vwarn(struct man *man, int ln, int pos, const char *fmt, ...) 600 { 601 char buf[256]; 602 va_list ap; 603 604 if (NULL == man->cb.man_warn) 605 return(0); 606 607 va_start(ap, fmt); 608 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 609 va_end(ap); 610 return((*man->cb.man_warn)(man->data, ln, pos, buf)); 611 } 612 613 614 int 615 man_err(struct man *m, int line, int pos, int iserr, enum merr type) 616 { 617 const char *p; 618 619 p = __man_merrnames[(int)type]; 620 assert(p); 621 622 if (iserr) 623 return(man_verr(m, line, pos, p)); 624 625 return(man_vwarn(m, line, pos, p)); 626 } 627