Annotation of mandoc/termact.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: term.c,v 1.4 2009/02/21 15:34:46 kristaps Exp $ */
! 2: /*
! 3: * Copyright (c) 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
! 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 <stdlib.h>
! 21: #include <string.h>
! 22:
! 23: #include "term.h"
! 24:
! 25: #define TTYPE_PROG 0
! 26: #define TTYPE_CMD_FLAG 1
! 27: #define TTYPE_CMD_ARG 2
! 28: #define TTYPE_SECTION 3
! 29: #define TTYPE_NMAX 4
! 30:
! 31: /*
! 32: * These define "styles" for element types, like command arguments or
! 33: * executable names. This is useful when multiple macros must decorate
! 34: * the same thing (like .Ex -std cmd and .Nm cmd).
! 35: */
! 36:
! 37: const int ttypes[TTYPE_NMAX] = {
! 38: TERMP_BOLD, /* TTYPE_PROG */
! 39: TERMP_BOLD, /* TTYPE_CMD_FLAG */
! 40: TERMP_UNDERLINE, /* TTYPE_CMD_ARG */
! 41: TERMP_BOLD /* TTYPE_SECTION */
! 42: };
! 43:
! 44: static int arg_hasattr(int, size_t,
! 45: const struct mdoc_arg *);
! 46: static int arg_getattr(int, size_t,
! 47: const struct mdoc_arg *);
! 48:
! 49: /*
! 50: * What follows describes prefix and postfix operations for the abstract
! 51: * syntax tree descent.
! 52: */
! 53:
! 54: #define DECL_ARGS \
! 55: struct termp *p, \
! 56: const struct mdoc_meta *meta, \
! 57: const struct mdoc_node *node
! 58:
! 59: #define DECL_PREPOST(name, suffix) \
! 60: static int name##_##suffix(DECL_ARGS)
! 61:
! 62: #define DECL_PRE(name) DECL_PREPOST(name, pre)
! 63: #define DECL_POST(name) DECL_PREPOST(name, post)
! 64:
! 65: DECL_PRE(termp_aq);
! 66: DECL_PRE(termp_ar);
! 67: DECL_PRE(termp_d1);
! 68: DECL_PRE(termp_dq);
! 69: DECL_PRE(termp_ex);
! 70: DECL_PRE(termp_fl);
! 71: DECL_PRE(termp_it);
! 72: DECL_PRE(termp_nd);
! 73: DECL_PRE(termp_nm);
! 74: DECL_PRE(termp_ns);
! 75: DECL_PRE(termp_op);
! 76: DECL_PRE(termp_pp);
! 77: DECL_PRE(termp_sh);
! 78: DECL_PRE(termp_ud);
! 79: DECL_PRE(termp_xr);
! 80:
! 81: DECL_POST(termp_aq);
! 82: DECL_POST(termp_ar);
! 83: DECL_POST(termp_bl);
! 84: DECL_POST(termp_d1);
! 85: DECL_POST(termp_dq);
! 86: DECL_POST(termp_fl);
! 87: DECL_POST(termp_it);
! 88: DECL_POST(termp_nm);
! 89: DECL_POST(termp_op);
! 90: DECL_POST(termp_sh);
! 91:
! 92: const struct termact __termacts[MDOC_MAX] = {
! 93: { NULL, NULL }, /* \" */
! 94: { NULL, NULL }, /* Dd */
! 95: { NULL, NULL }, /* Dt */
! 96: { NULL, NULL }, /* Os */
! 97: { termp_sh_pre, termp_sh_post }, /* Sh */
! 98: { NULL, NULL }, /* Ss */
! 99: { termp_pp_pre, NULL }, /* Pp */
! 100: { termp_d1_pre, termp_d1_post }, /* D1 */
! 101: { NULL, NULL }, /* Dl */
! 102: { NULL, NULL }, /* Bd */
! 103: { NULL, NULL }, /* Ed */
! 104: { NULL, termp_bl_post }, /* Bl */
! 105: { NULL, NULL }, /* El */
! 106: { termp_it_pre, termp_it_post }, /* It */
! 107: { NULL, NULL }, /* Ad */
! 108: { NULL, NULL }, /* An */
! 109: { termp_ar_pre, termp_ar_post }, /* Ar */
! 110: { NULL, NULL }, /* Cd */
! 111: { NULL, NULL }, /* Cm */
! 112: { NULL, NULL }, /* Dv */
! 113: { NULL, NULL }, /* Er */
! 114: { NULL, NULL }, /* Ev */
! 115: { termp_ex_pre, NULL }, /* Ex */
! 116: { NULL, NULL }, /* Fa */
! 117: { NULL, NULL }, /* Fd */
! 118: { termp_fl_pre, termp_fl_post }, /* Fl */
! 119: { NULL, NULL }, /* Fn */
! 120: { NULL, NULL }, /* Ft */
! 121: { NULL, NULL }, /* Ic */
! 122: { NULL, NULL }, /* In */
! 123: { NULL, NULL }, /* Li */
! 124: { termp_nd_pre, NULL }, /* Nd */
! 125: { termp_nm_pre, termp_nm_post }, /* Nm */
! 126: { termp_op_pre, termp_op_post }, /* Op */
! 127: { NULL, NULL }, /* Ot */
! 128: { NULL, NULL }, /* Pa */
! 129: { NULL, NULL }, /* Rv */
! 130: { NULL, NULL }, /* St */
! 131: { NULL, NULL }, /* Va */
! 132: { NULL, NULL }, /* Vt */
! 133: { termp_xr_pre, NULL }, /* Xr */
! 134: { NULL, NULL }, /* %A */
! 135: { NULL, NULL }, /* %B */
! 136: { NULL, NULL }, /* %D */
! 137: { NULL, NULL }, /* %I */
! 138: { NULL, NULL }, /* %J */
! 139: { NULL, NULL }, /* %N */
! 140: { NULL, NULL }, /* %O */
! 141: { NULL, NULL }, /* %P */
! 142: { NULL, NULL }, /* %R */
! 143: { NULL, NULL }, /* %T */
! 144: { NULL, NULL }, /* %V */
! 145: { NULL, NULL }, /* Ac */
! 146: { NULL, NULL }, /* Ao */
! 147: { termp_aq_pre, termp_aq_post }, /* Aq */
! 148: { NULL, NULL }, /* At */
! 149: { NULL, NULL }, /* Bc */
! 150: { NULL, NULL }, /* Bf */
! 151: { NULL, NULL }, /* Bo */
! 152: { NULL, NULL }, /* Bq */
! 153: { NULL, NULL }, /* Bsx */
! 154: { NULL, NULL }, /* Bx */
! 155: { NULL, NULL }, /* Db */
! 156: { NULL, NULL }, /* Dc */
! 157: { NULL, NULL }, /* Do */
! 158: { termp_dq_pre, termp_dq_post }, /* Dq */
! 159: { NULL, NULL }, /* Ec */
! 160: { NULL, NULL }, /* Ef */
! 161: { NULL, NULL }, /* Em */
! 162: { NULL, NULL }, /* Eo */
! 163: { NULL, NULL }, /* Fx */
! 164: { NULL, NULL }, /* Ms */
! 165: { NULL, NULL }, /* No */
! 166: { termp_ns_pre, NULL }, /* Ns */
! 167: { NULL, NULL }, /* Nx */
! 168: { NULL, NULL }, /* Ox */
! 169: { NULL, NULL }, /* Pc */
! 170: { NULL, NULL }, /* Pf */
! 171: { NULL, NULL }, /* Po */
! 172: { NULL, NULL }, /* Pq */
! 173: { NULL, NULL }, /* Qc */
! 174: { NULL, NULL }, /* Ql */
! 175: { NULL, NULL }, /* Qo */
! 176: { NULL, NULL }, /* Qq */
! 177: { NULL, NULL }, /* Re */
! 178: { NULL, NULL }, /* Rs */
! 179: { NULL, NULL }, /* Sc */
! 180: { NULL, NULL }, /* So */
! 181: { NULL, NULL }, /* Sq */
! 182: { NULL, NULL }, /* Sm */
! 183: { NULL, NULL }, /* Sx */
! 184: { NULL, NULL }, /* Sy */
! 185: { NULL, NULL }, /* Tn */
! 186: { NULL, NULL }, /* Ux */
! 187: { NULL, NULL }, /* Xc */
! 188: { NULL, NULL }, /* Xo */
! 189: { NULL, NULL }, /* Fo */
! 190: { NULL, NULL }, /* Fc */
! 191: { NULL, NULL }, /* Oo */
! 192: { NULL, NULL }, /* Oc */
! 193: { NULL, NULL }, /* Bk */
! 194: { NULL, NULL }, /* Ek */
! 195: { NULL, NULL }, /* Bt */
! 196: { NULL, NULL }, /* Hf */
! 197: { NULL, NULL }, /* Fr */
! 198: { termp_ud_pre, NULL }, /* Ud */
! 199: };
! 200:
! 201: const struct termact *termacts = __termacts;
! 202:
! 203:
! 204: /* ARGSUSED */
! 205: static int
! 206: termp_dq_pre(DECL_ARGS)
! 207: {
! 208:
! 209: if (MDOC_BODY != node->type)
! 210: return(1);
! 211:
! 212: word(p, "``");
! 213: p->flags |= TERMP_NOSPACE;
! 214: return(1);
! 215: }
! 216:
! 217:
! 218: /* ARGSUSED */
! 219: static int
! 220: termp_dq_post(DECL_ARGS)
! 221: {
! 222:
! 223: if (MDOC_BODY != node->type)
! 224: return(1);
! 225:
! 226: p->flags |= TERMP_NOSPACE;
! 227: word(p, "''");
! 228: return(1);
! 229: }
! 230:
! 231:
! 232: /* ARGSUSED */
! 233: static int
! 234: termp_it_post(DECL_ARGS)
! 235: {
! 236: const struct mdoc_node *n, *it;
! 237: const struct mdoc_block *bl;
! 238: int i;
! 239: size_t width;
! 240:
! 241: /*
! 242: * This (and termp_it_pre()) are the most complicated functions
! 243: * here. They must account for a considerable number of
! 244: * switches that completely change the output behaviour, like
! 245: * -tag versus -column. Yech.
! 246: */
! 247:
! 248: switch (node->type) {
! 249: case (MDOC_BODY):
! 250: /* FALLTHROUGH */
! 251: case (MDOC_HEAD):
! 252: break;
! 253: default:
! 254: return(1);
! 255: }
! 256:
! 257: it = node->parent;
! 258: assert(MDOC_BLOCK == it->type);
! 259: assert(MDOC_It == it->tok);
! 260:
! 261: n = it->parent;
! 262: assert(MDOC_BODY == n->type);
! 263: assert(MDOC_Bl == n->tok);
! 264: n = n->parent;
! 265: bl = &n->data.block;
! 266:
! 267: /* If `-tag', adjust our margins accordingly. */
! 268:
! 269: if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
! 270: i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
! 271: assert(i >= 0);
! 272: assert(1 == bl->argv[i].sz);
! 273: width = strlen(*bl->argv[i].value); /* XXX */
! 274:
! 275: if (MDOC_HEAD == node->type) {
! 276: flushln(p);
! 277: /* FIXME: nested lists. */
! 278: p->rmargin = p->maxrmargin;
! 279: p->flags &= ~TERMP_NOBREAK;
! 280: } else {
! 281: flushln(p);
! 282: p->offset -= width + 1;
! 283: p->flags &= ~TERMP_NOLPAD;
! 284: }
! 285: }
! 286:
! 287: return(1);
! 288: }
! 289:
! 290:
! 291: /* ARGSUSED */
! 292: static int
! 293: termp_it_pre(DECL_ARGS)
! 294: {
! 295: const struct mdoc_node *n, *it;
! 296: const struct mdoc_block *bl;
! 297: int i;
! 298: size_t width;
! 299:
! 300: /*
! 301: * Also see termp_it_post() for general comments.
! 302: */
! 303:
! 304: switch (node->type) {
! 305: case (MDOC_BODY):
! 306: /* FALLTHROUGH */
! 307: case (MDOC_HEAD):
! 308: it = node->parent;
! 309: break;
! 310: case (MDOC_BLOCK):
! 311: it = node;
! 312: break;
! 313: default:
! 314: return(1);
! 315: }
! 316:
! 317: assert(MDOC_BLOCK == it->type);
! 318: assert(MDOC_It == it->tok);
! 319:
! 320: n = it->parent;
! 321: assert(MDOC_BODY == n->type);
! 322: assert(MDOC_Bl == n->tok);
! 323: n = n->parent;
! 324: bl = &n->data.block;
! 325:
! 326: /* If `-compact', don't assert vertical space. */
! 327:
! 328: if (MDOC_BLOCK == node->type) {
! 329: if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
! 330: newln(p);
! 331: else
! 332: vspace(p);
! 333: return(1);
! 334: }
! 335:
! 336: assert(MDOC_HEAD == node->type
! 337: || MDOC_BODY == node->type);
! 338:
! 339: /* If `-tag', adjust our margins accordingly. */
! 340:
! 341: if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
! 342: i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
! 343: assert(i >= 0); /* XXX */
! 344: assert(1 == bl->argv[i].sz);
! 345: width = strlen(*bl->argv[i].value); /* XXX */
! 346:
! 347: /* FIXME: nested lists. */
! 348:
! 349: if (MDOC_HEAD == node->type) {
! 350: p->flags |= TERMP_NOBREAK;
! 351: p->flags |= TERMP_NOSPACE;
! 352: p->rmargin = p->offset + width;
! 353: } else {
! 354: p->flags |= TERMP_NOSPACE;
! 355: p->flags |= TERMP_NOLPAD;
! 356: p->offset += width + 1;
! 357: }
! 358: }
! 359:
! 360: return(1);
! 361: }
! 362:
! 363:
! 364: /* ARGSUSED */
! 365: static int
! 366: termp_nm_post(DECL_ARGS)
! 367: {
! 368:
! 369: p->flags &= ~ttypes[TTYPE_PROG];
! 370: return(1);
! 371: }
! 372:
! 373:
! 374: /* ARGSUSED */
! 375: static int
! 376: termp_fl_post(DECL_ARGS)
! 377: {
! 378:
! 379: p->flags &= ~ttypes[TTYPE_CMD_FLAG];
! 380: return(1);
! 381: }
! 382:
! 383:
! 384: /* ARGSUSED */
! 385: static int
! 386: termp_ar_pre(DECL_ARGS)
! 387: {
! 388:
! 389: p->flags |= ttypes[TTYPE_CMD_ARG];
! 390: if (NULL == node->child)
! 391: word(p, "...");
! 392: return(1);
! 393: }
! 394:
! 395:
! 396: /* ARGSUSED */
! 397: static int
! 398: termp_nm_pre(DECL_ARGS)
! 399: {
! 400:
! 401: p->flags |= ttypes[TTYPE_PROG];
! 402: if (NULL == node->child)
! 403: word(p, meta->name);
! 404: return(1);
! 405: }
! 406:
! 407:
! 408: /* ARGSUSED */
! 409: static int
! 410: termp_ns_pre(DECL_ARGS)
! 411: {
! 412:
! 413: p->flags |= TERMP_NOSPACE;
! 414: return(1);
! 415: }
! 416:
! 417:
! 418: /* ARGSUSED */
! 419: static int
! 420: termp_pp_pre(DECL_ARGS)
! 421: {
! 422:
! 423: vspace(p);
! 424: return(1);
! 425: }
! 426:
! 427:
! 428: /* ARGSUSED */
! 429: static int
! 430: termp_ar_post(DECL_ARGS)
! 431: {
! 432:
! 433: p->flags &= ~ttypes[TTYPE_CMD_ARG];
! 434: return(1);
! 435: }
! 436:
! 437:
! 438: /* ARGSUSED */
! 439: static int
! 440: termp_ex_pre(DECL_ARGS)
! 441: {
! 442: int i;
! 443:
! 444: i = arg_getattr(MDOC_Std, node->data.elem.argc,
! 445: node->data.elem.argv);
! 446: assert(i >= 0);
! 447:
! 448: word(p, "The");
! 449: p->flags |= ttypes[TTYPE_PROG];
! 450: word(p, *node->data.elem.argv[i].value);
! 451: p->flags &= ~ttypes[TTYPE_PROG];
! 452: word(p, "utility exits 0 on success, and >0 if an error occurs.");
! 453:
! 454: return(1);
! 455: }
! 456:
! 457:
! 458: /* ARGSUSED */
! 459: static int
! 460: termp_nd_pre(DECL_ARGS)
! 461: {
! 462:
! 463: word(p, "\\-");
! 464: return(1);
! 465: }
! 466:
! 467:
! 468: /* ARGSUSED */
! 469: static int
! 470: termp_bl_post(DECL_ARGS)
! 471: {
! 472:
! 473: switch (node->type) {
! 474: case (MDOC_BLOCK):
! 475: newln(p);
! 476: break;
! 477: default:
! 478: break;
! 479: }
! 480: return(1);
! 481: }
! 482:
! 483:
! 484: /* ARGSUSED */
! 485: static int
! 486: termp_op_post(DECL_ARGS)
! 487: {
! 488:
! 489: switch (node->type) {
! 490: case (MDOC_BODY):
! 491: p->flags |= TERMP_NOSPACE;
! 492: word(p, "\\]");
! 493: break;
! 494: default:
! 495: break;
! 496: }
! 497: return(1);
! 498: }
! 499:
! 500:
! 501: /* ARGSUSED */
! 502: static int
! 503: termp_sh_post(DECL_ARGS)
! 504: {
! 505:
! 506: switch (node->type) {
! 507: case (MDOC_HEAD):
! 508: p->flags &= ~ttypes[TTYPE_SECTION];
! 509: newln(p);
! 510: break;
! 511: case (MDOC_BODY):
! 512: newln(p);
! 513: p->offset -= 4;
! 514: break;
! 515: default:
! 516: break;
! 517: }
! 518: return(1);
! 519: }
! 520:
! 521:
! 522: /* ARGSUSED */
! 523: static int
! 524: termp_xr_pre(DECL_ARGS)
! 525: {
! 526: const struct mdoc_node *n;
! 527:
! 528: n = node->child;
! 529: assert(n);
! 530:
! 531: assert(MDOC_TEXT == n->type);
! 532: word(p, n->data.text.string);
! 533:
! 534: if (NULL == (n = n->next))
! 535: return(0);
! 536:
! 537: assert(MDOC_TEXT == n->type);
! 538: p->flags |= TERMP_NOSPACE;
! 539: word(p, "\\(");
! 540: p->flags |= TERMP_NOSPACE;
! 541: word(p, n->data.text.string);
! 542: p->flags |= TERMP_NOSPACE;
! 543: word(p, "\\)");
! 544:
! 545: return(0);
! 546: }
! 547:
! 548:
! 549: /* ARGSUSED */
! 550: static int
! 551: termp_sh_pre(DECL_ARGS)
! 552: {
! 553:
! 554: switch (node->type) {
! 555: case (MDOC_HEAD):
! 556: vspace(p);
! 557: p->flags |= ttypes[TTYPE_SECTION];
! 558: break;
! 559: case (MDOC_BODY):
! 560: p->offset += 4;
! 561: break;
! 562: default:
! 563: break;
! 564: }
! 565: return(1);
! 566: }
! 567:
! 568:
! 569: /* ARGSUSED */
! 570: static int
! 571: termp_op_pre(DECL_ARGS)
! 572: {
! 573:
! 574: switch (node->type) {
! 575: case (MDOC_BODY):
! 576: word(p, "\\[");
! 577: p->flags |= TERMP_NOSPACE;
! 578: break;
! 579: default:
! 580: break;
! 581: }
! 582: return(1);
! 583: }
! 584:
! 585:
! 586: /* ARGSUSED */
! 587: static int
! 588: termp_ud_pre(DECL_ARGS)
! 589: {
! 590:
! 591: word(p, "currently under development.");
! 592: return(1);
! 593: }
! 594:
! 595:
! 596: /* ARGSUSED */
! 597: static int
! 598: termp_fl_pre(DECL_ARGS)
! 599: {
! 600:
! 601: p->flags |= ttypes[TTYPE_CMD_FLAG];
! 602: word(p, "\\-");
! 603: p->flags |= TERMP_NOSPACE;
! 604: return(1);
! 605: }
! 606:
! 607:
! 608: /* ARGSUSED */
! 609: static int
! 610: termp_d1_pre(DECL_ARGS)
! 611: {
! 612:
! 613: if (MDOC_BODY != node->type)
! 614: return(1);
! 615: newln(p);
! 616: p->offset += 4;
! 617: return(1);
! 618: }
! 619:
! 620:
! 621: /* ARGSUSED */
! 622: static int
! 623: termp_d1_post(DECL_ARGS)
! 624: {
! 625:
! 626: if (MDOC_BODY != node->type)
! 627: return(1);
! 628: newln(p);
! 629: p->offset -= 4;
! 630: return(1);
! 631: }
! 632:
! 633:
! 634: /* ARGSUSED */
! 635: static int
! 636: termp_aq_pre(DECL_ARGS)
! 637: {
! 638:
! 639: if (MDOC_BODY != node->type)
! 640: return(1);
! 641: word(p, "\\<");
! 642: p->flags |= TERMP_NOSPACE;
! 643: return(1);
! 644: }
! 645:
! 646:
! 647: /* ARGSUSED */
! 648: static int
! 649: termp_aq_post(DECL_ARGS)
! 650: {
! 651:
! 652: if (MDOC_BODY != node->type)
! 653: return(1);
! 654: p->flags |= TERMP_NOSPACE;
! 655: word(p, "\\>");
! 656: return(1);
! 657: }
! 658:
! 659:
! 660: static int
! 661: arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
! 662: {
! 663:
! 664: return(-1 != arg_getattr(arg, argc, argv));
! 665: }
! 666:
! 667:
! 668: static int
! 669: arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
! 670: {
! 671: int i;
! 672:
! 673: for (i = 0; i < (int)argc; i++)
! 674: if (argv[i].arg == arg)
! 675: return(i);
! 676: return(-1);
! 677: }
! 678:
CVSweb