Annotation of src/usr.bin/mandoc/mdoc_macro.c, Revision 1.1
1.1 ! kristaps 1: /* $Id: mdoc_macro.c,v 1.6 2009/04/02 06:51:44 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 <stdlib.h>
! 22: #include <stdio.h>
! 23: #include <string.h>
! 24:
! 25: #include "libmdoc.h"
! 26:
! 27: /* FIXME: .Fl, .Ar, .Cd handling of `|'. */
! 28:
! 29: enum mwarn {
! 30: WIGNE,
! 31: WIMPBRK,
! 32: WMACPARM,
! 33: WOBS
! 34: };
! 35:
! 36: enum merr {
! 37: EOPEN,
! 38: EQUOT,
! 39: ENOCTX,
! 40: ENOPARMS
! 41: };
! 42:
! 43: #define REWIND_REWIND (1 << 0)
! 44: #define REWIND_NOHALT (1 << 1)
! 45: #define REWIND_HALT (1 << 2)
! 46:
! 47: static int obsolete(MACRO_PROT_ARGS);
! 48: static int blk_part_exp(MACRO_PROT_ARGS);
! 49: static int in_line_eoln(MACRO_PROT_ARGS);
! 50: static int in_line_argn(MACRO_PROT_ARGS);
! 51: static int in_line(MACRO_PROT_ARGS);
! 52: static int blk_full(MACRO_PROT_ARGS);
! 53: static int blk_exp_close(MACRO_PROT_ARGS);
! 54: static int blk_part_imp(MACRO_PROT_ARGS);
! 55:
! 56: static int phrase(struct mdoc *, int, int, char *);
! 57: static int rew_dohalt(int, enum mdoc_type,
! 58: const struct mdoc_node *);
! 59: static int rew_alt(int);
! 60: static int rew_dobreak(int, const struct mdoc_node *);
! 61: static int rew_elem(struct mdoc *, int);
! 62: static int rew_impblock(struct mdoc *, int, int, int);
! 63: static int rew_expblock(struct mdoc *, int, int, int);
! 64: static int rew_subblock(enum mdoc_type,
! 65: struct mdoc *, int, int, int);
! 66: static int rew_last(struct mdoc *, struct mdoc_node *);
! 67: static int append_delims(struct mdoc *, int, int *, char *);
! 68: static int lookup(struct mdoc *, int, int, int, const char *);
! 69: static int pwarn(struct mdoc *, int, int, enum mwarn);
! 70: static int perr(struct mdoc *, int, int, enum merr);
! 71: static int swarn(struct mdoc *, enum mdoc_type, int, int,
! 72: const struct mdoc_node *);
! 73:
! 74: #define nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t))
! 75:
! 76: /* Central table of library: who gets parsed how. */
! 77:
! 78: const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
! 79: { NULL, 0 }, /* \" */
! 80: { in_line_eoln, MDOC_PROLOGUE }, /* Dd */
! 81: { in_line_eoln, MDOC_PROLOGUE }, /* Dt */
! 82: { in_line_eoln, MDOC_PROLOGUE }, /* Os */
! 83: { blk_full, 0 }, /* Sh */
! 84: { blk_full, 0 }, /* Ss */
! 85: { in_line, 0 }, /* Pp */
! 86: { blk_part_imp, MDOC_PARSED }, /* D1 */
! 87: { blk_part_imp, MDOC_PARSED }, /* Dl */
! 88: { blk_full, MDOC_EXPLICIT }, /* Bd */
! 89: { blk_exp_close, MDOC_EXPLICIT }, /* Ed */
! 90: { blk_full, MDOC_EXPLICIT }, /* Bl */
! 91: { blk_exp_close, MDOC_EXPLICIT }, /* El */
! 92: { blk_full, MDOC_PARSED }, /* It */
! 93: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
! 94: { in_line, MDOC_PARSED }, /* An */
! 95: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
! 96: { in_line_eoln, MDOC_CALLABLE }, /* Cd */
! 97: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
! 98: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
! 99: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
! 100: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
! 101: { in_line_eoln, 0 }, /* Ex */
! 102: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
! 103: { in_line_eoln, 0 }, /* Fd */
! 104: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
! 105: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
! 106: { in_line, MDOC_PARSED }, /* Ft */
! 107: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
! 108: { in_line_eoln, 0 }, /* In */
! 109: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
! 110: { in_line_eoln, 0 }, /* Nd */
! 111: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
! 112: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
! 113: { obsolete, 0 }, /* Ot */
! 114: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
! 115: { in_line_eoln, 0 }, /* Rv */
! 116: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
! 117: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
! 118: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
! 119: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
! 120: { in_line_eoln, 0 }, /* %A */
! 121: { in_line_eoln, 0 }, /* %B */
! 122: { in_line_eoln, 0 }, /* %D */
! 123: { in_line_eoln, 0 }, /* %I */
! 124: { in_line_eoln, 0 }, /* %J */
! 125: { in_line_eoln, 0 }, /* %N */
! 126: { in_line_eoln, 0 }, /* %O */
! 127: { in_line_eoln, 0 }, /* %P */
! 128: { in_line_eoln, 0 }, /* %R */
! 129: { in_line_eoln, 0 }, /* %T */
! 130: { in_line_eoln, 0 }, /* %V */
! 131: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
! 132: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
! 133: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
! 134: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
! 135: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
! 136: { blk_full, MDOC_EXPLICIT }, /* Bf */
! 137: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
! 138: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
! 139: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
! 140: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
! 141: { in_line_eoln, 0 }, /* Db */
! 142: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
! 143: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
! 144: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
! 145: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
! 146: { blk_exp_close, MDOC_EXPLICIT }, /* Ef */
! 147: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
! 148: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
! 149: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
! 150: { in_line, MDOC_PARSED }, /* Ms */
! 151: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */
! 152: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */
! 153: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
! 154: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
! 155: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
! 156: { in_line_argn, MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
! 157: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
! 158: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
! 159: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
! 160: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
! 161: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
! 162: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
! 163: { blk_exp_close, MDOC_EXPLICIT }, /* Re */
! 164: { blk_full, MDOC_EXPLICIT }, /* Rs */
! 165: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
! 166: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
! 167: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
! 168: { in_line_eoln, 0 }, /* Sm */
! 169: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
! 170: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
! 171: { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
! 172: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */
! 173: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
! 174: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
! 175: { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
! 176: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */
! 177: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
! 178: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
! 179: { blk_full, MDOC_EXPLICIT }, /* Bk */
! 180: { blk_exp_close, MDOC_EXPLICIT }, /* Ek */
! 181: { in_line_eoln, 0 }, /* Bt */
! 182: { in_line_eoln, 0 }, /* Hf */
! 183: { obsolete, 0 }, /* Fr */
! 184: { in_line_eoln, 0 }, /* Ud */
! 185: { in_line_eoln, 0 }, /* Lb */
! 186: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */
! 187: { in_line, 0 }, /* Lp */
! 188: { in_line, MDOC_PARSED }, /* Lk */
! 189: { in_line, MDOC_PARSED }, /* Mt */
! 190: { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */
! 191: { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */
! 192: { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */
! 193: { in_line_eoln, 0 }, /* %C */
! 194: { obsolete, 0 }, /* Es */
! 195: { obsolete, 0 }, /* En */
! 196: { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
! 197: { in_line_eoln, 0 }, /* %Q */
! 198: };
! 199:
! 200: const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
! 201:
! 202:
! 203: static int
! 204: perr(struct mdoc *mdoc, int line, int pos, enum merr type)
! 205: {
! 206: char *p;
! 207:
! 208: p = NULL;
! 209: switch (type) {
! 210: case (EOPEN):
! 211: p = "explicit scope still open on exit";
! 212: break;
! 213: case (EQUOT):
! 214: p = "unterminated quotation";
! 215: break;
! 216: case (ENOCTX):
! 217: p = "closure has no prior context";
! 218: break;
! 219: case (ENOPARMS):
! 220: p = "unexpect line arguments";
! 221: break;
! 222: }
! 223: assert(p);
! 224: return(mdoc_perr(mdoc, line, pos, p));
! 225: }
! 226:
! 227:
! 228: static int
! 229: pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn type)
! 230: {
! 231: char *p;
! 232:
! 233: p = NULL;
! 234: switch (type) {
! 235: case (WIGNE):
! 236: p = "ignoring empty element";
! 237: break;
! 238: case (WIMPBRK):
! 239: p = "crufty end-of-line scope violation";
! 240: break;
! 241: case (WMACPARM):
! 242: p = "macro-like parameter";
! 243: break;
! 244: case (WOBS):
! 245: p = "macro marked obsolete";
! 246: break;
! 247: }
! 248: assert(p);
! 249: return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, p));
! 250: }
! 251:
! 252:
! 253: static int
! 254: swarn(struct mdoc *mdoc, enum mdoc_type type,
! 255: int line, int pos, const struct mdoc_node *p)
! 256: {
! 257: const char *n, *t, *tt;
! 258:
! 259: n = t = "<root>";
! 260: tt = "block";
! 261:
! 262: switch (type) {
! 263: case (MDOC_BODY):
! 264: tt = "multi-line";
! 265: break;
! 266: case (MDOC_HEAD):
! 267: tt = "line";
! 268: break;
! 269: default:
! 270: break;
! 271: }
! 272:
! 273: switch (p->type) {
! 274: case (MDOC_BLOCK):
! 275: n = mdoc_macronames[p->tok];
! 276: t = "block";
! 277: break;
! 278: case (MDOC_BODY):
! 279: n = mdoc_macronames[p->tok];
! 280: t = "multi-line";
! 281: break;
! 282: case (MDOC_HEAD):
! 283: n = mdoc_macronames[p->tok];
! 284: t = "line";
! 285: break;
! 286: default:
! 287: break;
! 288: }
! 289:
! 290: if ( ! (MDOC_IGN_SCOPE & mdoc->pflags))
! 291: return(mdoc_perr(mdoc, line, pos,
! 292: "%s scope breaks %s scope of %s",
! 293: tt, t, n));
! 294: return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX,
! 295: "%s scope breaks %s scope of %s",
! 296: tt, t, n));
! 297: }
! 298:
! 299:
! 300: /*
! 301: * This is called at the end of parsing. It must traverse up the tree,
! 302: * closing out open [implicit] scopes. Obviously, open explicit scopes
! 303: * are errors.
! 304: */
! 305: int
! 306: mdoc_macroend(struct mdoc *mdoc)
! 307: {
! 308: struct mdoc_node *n;
! 309:
! 310: /* Scan for open explicit scopes. */
! 311:
! 312: n = MDOC_VALID & mdoc->last->flags ?
! 313: mdoc->last->parent : mdoc->last;
! 314:
! 315: for ( ; n; n = n->parent) {
! 316: if (MDOC_BLOCK != n->type)
! 317: continue;
! 318: if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags))
! 319: continue;
! 320: return(nerr(mdoc, n, EOPEN));
! 321: }
! 322:
! 323: return(rew_last(mdoc, mdoc->first));
! 324: }
! 325:
! 326: static int
! 327: lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p)
! 328: {
! 329: int res;
! 330:
! 331: res = mdoc_hash_find(mdoc->htab, p);
! 332: if (MDOC_PARSED & mdoc_macros[from].flags)
! 333: return(res);
! 334: if (MDOC_MAX == res)
! 335: return(res);
! 336: if ( ! pwarn(mdoc, line, pos, WMACPARM))
! 337: return(-1);
! 338: return(MDOC_MAX);
! 339: }
! 340:
! 341:
! 342: static int
! 343: rew_last(struct mdoc *mdoc, struct mdoc_node *to)
! 344: {
! 345:
! 346: assert(to);
! 347: mdoc->next = MDOC_NEXT_SIBLING;
! 348:
! 349: /* LINTED */
! 350: while (mdoc->last != to) {
! 351: if ( ! mdoc_valid_post(mdoc))
! 352: return(0);
! 353: if ( ! mdoc_action_post(mdoc))
! 354: return(0);
! 355: mdoc->last = mdoc->last->parent;
! 356: assert(mdoc->last);
! 357: }
! 358:
! 359: if ( ! mdoc_valid_post(mdoc))
! 360: return(0);
! 361: return(mdoc_action_post(mdoc));
! 362: }
! 363:
! 364:
! 365: static int
! 366: rew_alt(int tok)
! 367: {
! 368: switch (tok) {
! 369: case (MDOC_Ac):
! 370: return(MDOC_Ao);
! 371: case (MDOC_Bc):
! 372: return(MDOC_Bo);
! 373: case (MDOC_Brc):
! 374: return(MDOC_Bro);
! 375: case (MDOC_Dc):
! 376: return(MDOC_Do);
! 377: case (MDOC_Ec):
! 378: return(MDOC_Eo);
! 379: case (MDOC_Ed):
! 380: return(MDOC_Bd);
! 381: case (MDOC_Ef):
! 382: return(MDOC_Bf);
! 383: case (MDOC_Ek):
! 384: return(MDOC_Bk);
! 385: case (MDOC_El):
! 386: return(MDOC_Bl);
! 387: case (MDOC_Fc):
! 388: return(MDOC_Fo);
! 389: case (MDOC_Oc):
! 390: return(MDOC_Oo);
! 391: case (MDOC_Pc):
! 392: return(MDOC_Po);
! 393: case (MDOC_Qc):
! 394: return(MDOC_Qo);
! 395: case (MDOC_Re):
! 396: return(MDOC_Rs);
! 397: case (MDOC_Sc):
! 398: return(MDOC_So);
! 399: case (MDOC_Xc):
! 400: return(MDOC_Xo);
! 401: default:
! 402: break;
! 403: }
! 404: abort();
! 405: /* NOTREACHED */
! 406: }
! 407:
! 408:
! 409: /*
! 410: * Rewind rules. This indicates whether to stop rewinding
! 411: * (REWIND_HALT) without touching our current scope, stop rewinding and
! 412: * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT).
! 413: * The scope-closing and so on occurs in the various rew_* routines.
! 414: */
! 415: static int
! 416: rew_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p)
! 417: {
! 418:
! 419: if (MDOC_ROOT == p->type)
! 420: return(REWIND_HALT);
! 421: if (MDOC_VALID & p->flags)
! 422: return(REWIND_NOHALT);
! 423:
! 424: switch (tok) {
! 425: case (MDOC_Aq):
! 426: /* FALLTHROUGH */
! 427: case (MDOC_Bq):
! 428: /* FALLTHROUGH */
! 429: case (MDOC_Brq):
! 430: /* FALLTHROUGH */
! 431: case (MDOC_D1):
! 432: /* FALLTHROUGH */
! 433: case (MDOC_Dl):
! 434: /* FALLTHROUGH */
! 435: case (MDOC_Dq):
! 436: /* FALLTHROUGH */
! 437: case (MDOC_Op):
! 438: /* FALLTHROUGH */
! 439: case (MDOC_Pq):
! 440: /* FALLTHROUGH */
! 441: case (MDOC_Ql):
! 442: /* FALLTHROUGH */
! 443: case (MDOC_Qq):
! 444: /* FALLTHROUGH */
! 445: case (MDOC_Sq):
! 446: assert(MDOC_HEAD != type);
! 447: assert(MDOC_TAIL != type);
! 448: if (type == p->type && tok == p->tok)
! 449: return(REWIND_REWIND);
! 450: break;
! 451: case (MDOC_It):
! 452: assert(MDOC_TAIL != type);
! 453: if (type == p->type && tok == p->tok)
! 454: return(REWIND_REWIND);
! 455: if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
! 456: return(REWIND_HALT);
! 457: break;
! 458: case (MDOC_Sh):
! 459: if (type == p->type && tok == p->tok)
! 460: return(REWIND_REWIND);
! 461: break;
! 462: case (MDOC_Ss):
! 463: assert(MDOC_TAIL != type);
! 464: if (type == p->type && tok == p->tok)
! 465: return(REWIND_REWIND);
! 466: if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
! 467: return(REWIND_HALT);
! 468: break;
! 469: case (MDOC_Ao):
! 470: /* FALLTHROUGH */
! 471: case (MDOC_Bd):
! 472: /* FALLTHROUGH */
! 473: case (MDOC_Bf):
! 474: /* FALLTHROUGH */
! 475: case (MDOC_Bk):
! 476: /* FALLTHROUGH */
! 477: case (MDOC_Bl):
! 478: /* FALLTHROUGH */
! 479: case (MDOC_Bo):
! 480: /* FALLTHROUGH */
! 481: case (MDOC_Bro):
! 482: /* FALLTHROUGH */
! 483: case (MDOC_Do):
! 484: /* FALLTHROUGH */
! 485: case (MDOC_Eo):
! 486: /* FALLTHROUGH */
! 487: case (MDOC_Fo):
! 488: /* FALLTHROUGH */
! 489: case (MDOC_Oo):
! 490: /* FALLTHROUGH */
! 491: case (MDOC_Po):
! 492: /* FALLTHROUGH */
! 493: case (MDOC_Qo):
! 494: /* FALLTHROUGH */
! 495: case (MDOC_Rs):
! 496: /* FALLTHROUGH */
! 497: case (MDOC_So):
! 498: /* FALLTHROUGH */
! 499: case (MDOC_Xo):
! 500: if (type == p->type && tok == p->tok)
! 501: return(REWIND_REWIND);
! 502: break;
! 503:
! 504: /* Multi-line explicit scope close. */
! 505: case (MDOC_Ac):
! 506: /* FALLTHROUGH */
! 507: case (MDOC_Bc):
! 508: /* FALLTHROUGH */
! 509: case (MDOC_Brc):
! 510: /* FALLTHROUGH */
! 511: case (MDOC_Dc):
! 512: /* FALLTHROUGH */
! 513: case (MDOC_Ec):
! 514: /* FALLTHROUGH */
! 515: case (MDOC_Ed):
! 516: /* FALLTHROUGH */
! 517: case (MDOC_Ek):
! 518: /* FALLTHROUGH */
! 519: case (MDOC_El):
! 520: /* FALLTHROUGH */
! 521: case (MDOC_Fc):
! 522: /* FALLTHROUGH */
! 523: case (MDOC_Ef):
! 524: /* FALLTHROUGH */
! 525: case (MDOC_Oc):
! 526: /* FALLTHROUGH */
! 527: case (MDOC_Pc):
! 528: /* FALLTHROUGH */
! 529: case (MDOC_Qc):
! 530: /* FALLTHROUGH */
! 531: case (MDOC_Re):
! 532: /* FALLTHROUGH */
! 533: case (MDOC_Sc):
! 534: /* FALLTHROUGH */
! 535: case (MDOC_Xc):
! 536: if (type == p->type && rew_alt(tok) == p->tok)
! 537: return(REWIND_REWIND);
! 538: break;
! 539: default:
! 540: abort();
! 541: /* NOTREACHED */
! 542: }
! 543:
! 544: return(REWIND_NOHALT);
! 545: }
! 546:
! 547:
! 548: /*
! 549: * See if we can break an encountered scope (the rew_dohalt has returned
! 550: * REWIND_NOHALT).
! 551: */
! 552: static int
! 553: rew_dobreak(int tok, const struct mdoc_node *p)
! 554: {
! 555:
! 556: assert(MDOC_ROOT != p->type);
! 557: if (MDOC_ELEM == p->type)
! 558: return(1);
! 559: if (MDOC_TEXT == p->type)
! 560: return(1);
! 561: if (MDOC_VALID & p->flags)
! 562: return(1);
! 563:
! 564: switch (tok) {
! 565: case (MDOC_It):
! 566: return(MDOC_It == p->tok);
! 567: case (MDOC_Ss):
! 568: return(MDOC_Ss == p->tok);
! 569: case (MDOC_Sh):
! 570: if (MDOC_Ss == p->tok)
! 571: return(1);
! 572: return(MDOC_Sh == p->tok);
! 573: case (MDOC_El):
! 574: if (MDOC_It == p->tok)
! 575: return(1);
! 576: break;
! 577: case (MDOC_Oc):
! 578: /* XXX - experimental! */
! 579: if (MDOC_Op == p->tok)
! 580: return(1);
! 581: break;
! 582: default:
! 583: break;
! 584: }
! 585:
! 586: if (MDOC_EXPLICIT & mdoc_macros[tok].flags)
! 587: return(p->tok == rew_alt(tok));
! 588: else if (MDOC_BLOCK == p->type)
! 589: return(1);
! 590:
! 591: return(tok == p->tok);
! 592: }
! 593:
! 594:
! 595: static int
! 596: rew_elem(struct mdoc *mdoc, int tok)
! 597: {
! 598: struct mdoc_node *n;
! 599:
! 600: n = mdoc->last;
! 601: if (MDOC_ELEM != n->type)
! 602: n = n->parent;
! 603: assert(MDOC_ELEM == n->type);
! 604: assert(tok == n->tok);
! 605:
! 606: return(rew_last(mdoc, n));
! 607: }
! 608:
! 609:
! 610: static int
! 611: rew_subblock(enum mdoc_type type, struct mdoc *mdoc,
! 612: int tok, int line, int ppos)
! 613: {
! 614: struct mdoc_node *n;
! 615: int c;
! 616:
! 617: /* LINTED */
! 618: for (n = mdoc->last; n; n = n->parent) {
! 619: c = rew_dohalt(tok, type, n);
! 620: if (REWIND_HALT == c)
! 621: return(1);
! 622: if (REWIND_REWIND == c)
! 623: break;
! 624: else if (rew_dobreak(tok, n))
! 625: continue;
! 626: if ( ! swarn(mdoc, type, line, ppos, n))
! 627: return(0);
! 628: }
! 629:
! 630: assert(n);
! 631: return(rew_last(mdoc, n));
! 632: }
! 633:
! 634:
! 635: static int
! 636: rew_expblock(struct mdoc *mdoc, int tok, int line, int ppos)
! 637: {
! 638: struct mdoc_node *n;
! 639: int c;
! 640:
! 641: /* LINTED */
! 642: for (n = mdoc->last; n; n = n->parent) {
! 643: c = rew_dohalt(tok, MDOC_BLOCK, n);
! 644: if (REWIND_HALT == c)
! 645: return(perr(mdoc, line, ppos, ENOCTX));
! 646: if (REWIND_REWIND == c)
! 647: break;
! 648: else if (rew_dobreak(tok, n))
! 649: continue;
! 650: if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n))
! 651: return(0);
! 652: }
! 653:
! 654: assert(n);
! 655: return(rew_last(mdoc, n));
! 656: }
! 657:
! 658:
! 659: static int
! 660: rew_impblock(struct mdoc *mdoc, int tok, int line, int ppos)
! 661: {
! 662: struct mdoc_node *n;
! 663: int c;
! 664:
! 665: /* LINTED */
! 666: for (n = mdoc->last; n; n = n->parent) {
! 667: c = rew_dohalt(tok, MDOC_BLOCK, n);
! 668: if (REWIND_HALT == c)
! 669: return(1);
! 670: else if (REWIND_REWIND == c)
! 671: break;
! 672: else if (rew_dobreak(tok, n))
! 673: continue;
! 674: if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n))
! 675: return(0);
! 676: }
! 677:
! 678: assert(n);
! 679: return(rew_last(mdoc, n));
! 680: }
! 681:
! 682:
! 683: static int
! 684: append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
! 685: {
! 686: int c, lastarg;
! 687: char *p;
! 688:
! 689: if (0 == buf[*pos])
! 690: return(1);
! 691:
! 692: for (;;) {
! 693: lastarg = *pos;
! 694: c = mdoc_args(mdoc, line, pos, buf, 0, &p);
! 695: assert(ARGS_PHRASE != c);
! 696:
! 697: if (ARGS_ERROR == c)
! 698: return(0);
! 699: else if (ARGS_EOLN == c)
! 700: break;
! 701: assert(mdoc_isdelim(p));
! 702: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 703: return(0);
! 704: mdoc->next = MDOC_NEXT_SIBLING;
! 705: }
! 706:
! 707: return(1);
! 708: }
! 709:
! 710:
! 711: /*
! 712: * Close out block partial/full explicit.
! 713: */
! 714: static int
! 715: blk_exp_close(MACRO_PROT_ARGS)
! 716: {
! 717: int j, c, lastarg, maxargs, flushed;
! 718: char *p;
! 719:
! 720: switch (tok) {
! 721: case (MDOC_Ec):
! 722: maxargs = 1;
! 723: break;
! 724: default:
! 725: maxargs = 0;
! 726: break;
! 727: }
! 728:
! 729: if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
! 730: if (0 == buf[*pos]) {
! 731: if ( ! rew_subblock(MDOC_BODY, mdoc,
! 732: tok, line, ppos))
! 733: return(0);
! 734: return(rew_expblock(mdoc, tok, line, ppos));
! 735: }
! 736: return(perr(mdoc, line, ppos, ENOPARMS));
! 737: }
! 738:
! 739: if ( ! rew_subblock(MDOC_BODY, mdoc, tok, line, ppos))
! 740: return(0);
! 741:
! 742: if (maxargs > 0) {
! 743: if ( ! mdoc_tail_alloc(mdoc, line,
! 744: ppos, rew_alt(tok)))
! 745: return(0);
! 746: mdoc->next = MDOC_NEXT_CHILD;
! 747: }
! 748:
! 749: for (lastarg = ppos, flushed = j = 0; ; j++) {
! 750: lastarg = *pos;
! 751:
! 752: if (j == maxargs && ! flushed) {
! 753: if ( ! rew_expblock(mdoc, tok, line, ppos))
! 754: return(0);
! 755: flushed = 1;
! 756: }
! 757:
! 758: c = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 759:
! 760: if (ARGS_ERROR == c)
! 761: return(0);
! 762: if (ARGS_PUNCT == c)
! 763: break;
! 764: if (ARGS_EOLN == c)
! 765: break;
! 766:
! 767: if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
! 768: return(0);
! 769: else if (MDOC_MAX != c) {
! 770: if ( ! flushed) {
! 771: if ( ! rew_expblock(mdoc, tok,
! 772: line, ppos))
! 773: return(0);
! 774: flushed = 1;
! 775: }
! 776: if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
! 777: return(0);
! 778: break;
! 779: }
! 780:
! 781: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 782: return(0);
! 783: mdoc->next = MDOC_NEXT_SIBLING;
! 784: }
! 785:
! 786: if ( ! flushed && ! rew_expblock(mdoc, tok, line, ppos))
! 787: return(0);
! 788:
! 789: if (ppos > 1)
! 790: return(1);
! 791: return(append_delims(mdoc, line, pos, buf));
! 792: }
! 793:
! 794:
! 795: /*
! 796: * In-line macros where reserved words cause scope close-reopen.
! 797: */
! 798: static int
! 799: in_line(MACRO_PROT_ARGS)
! 800: {
! 801: int la, lastpunct, c, w, cnt, d, nc;
! 802: struct mdoc_arg *arg;
! 803: char *p;
! 804:
! 805: /*
! 806: * Whether we allow ignored elements (those without content,
! 807: * usually because of reserved words) to squeak by.
! 808: */
! 809: switch (tok) {
! 810: case (MDOC_Lp):
! 811: /* FALLTHROUGH */
! 812: case (MDOC_Pp):
! 813: /* FALLTHROUGH */
! 814: case (MDOC_Nm):
! 815: /* FALLTHROUGH */
! 816: case (MDOC_Fl):
! 817: /* FALLTHROUGH */
! 818: case (MDOC_Ar):
! 819: nc = 1;
! 820: break;
! 821: default:
! 822: nc = 0;
! 823: break;
! 824: }
! 825:
! 826: for (la = ppos, arg = NULL;; ) {
! 827: la = *pos;
! 828: c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
! 829:
! 830: if (ARGV_WORD == c) {
! 831: *pos = la;
! 832: break;
! 833: }
! 834: if (ARGV_EOLN == c)
! 835: break;
! 836: if (ARGV_ARG == c)
! 837: continue;
! 838:
! 839: mdoc_argv_free(arg);
! 840: return(0);
! 841: }
! 842:
! 843: for (cnt = 0, lastpunct = 1;; ) {
! 844: la = *pos;
! 845: w = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 846:
! 847: if (ARGS_ERROR == w)
! 848: return(0);
! 849: if (ARGS_EOLN == w)
! 850: break;
! 851: if (ARGS_PUNCT == w)
! 852: break;
! 853:
! 854: /* Quoted words shouldn't be looked-up. */
! 855:
! 856: c = ARGS_QWORD == w ? MDOC_MAX :
! 857: lookup(mdoc, line, la, tok, p);
! 858:
! 859: /*
! 860: * In this case, we've located a submacro and must
! 861: * execute it. Close out scope, if open. If no
! 862: * elements have been generated, either create one (nc)
! 863: * or raise a warning.
! 864: */
! 865:
! 866: if (MDOC_MAX != c && -1 != c) {
! 867: if (0 == lastpunct && ! rew_elem(mdoc, tok))
! 868: return(0);
! 869: if (nc && 0 == cnt) {
! 870: if ( ! mdoc_elem_alloc(mdoc, line, ppos,
! 871: tok, arg))
! 872: return(0);
! 873: mdoc->next = MDOC_NEXT_SIBLING;
! 874: } else if ( ! nc && 0 == cnt) {
! 875: mdoc_argv_free(arg);
! 876: if ( ! pwarn(mdoc, line, ppos, WIGNE))
! 877: return(0);
! 878: }
! 879: c = mdoc_macro(mdoc, c, line, la, pos, buf);
! 880: if (0 == c)
! 881: return(0);
! 882: if (ppos > 1)
! 883: return(1);
! 884: return(append_delims(mdoc, line, pos, buf));
! 885: } else if (-1 == c)
! 886: return(0);
! 887:
! 888: /*
! 889: * Non-quote-enclosed punctuation. Set up our scope, if
! 890: * a word; rewind the scope, if a delimiter; then append
! 891: * the word.
! 892: */
! 893:
! 894: d = mdoc_isdelim(p);
! 895:
! 896: if (ARGS_QWORD != w && d) {
! 897: if (0 == lastpunct && ! rew_elem(mdoc, tok))
! 898: return(0);
! 899: lastpunct = 1;
! 900: } else if (lastpunct) {
! 901: c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
! 902: if (0 == c)
! 903: return(0);
! 904: mdoc->next = MDOC_NEXT_CHILD;
! 905: lastpunct = 0;
! 906: }
! 907:
! 908: if ( ! d)
! 909: cnt++;
! 910: if ( ! mdoc_word_alloc(mdoc, line, la, p))
! 911: return(0);
! 912: mdoc->next = MDOC_NEXT_SIBLING;
! 913: }
! 914:
! 915: if (0 == lastpunct && ! rew_elem(mdoc, tok))
! 916: return(0);
! 917:
! 918: /*
! 919: * If no elements have been collected and we're allowed to have
! 920: * empties (nc), open a scope and close it out. Otherwise,
! 921: * raise a warning.
! 922: *
! 923: */
! 924: if (nc && 0 == cnt) {
! 925: c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
! 926: if (0 == c)
! 927: return(0);
! 928: mdoc->next = MDOC_NEXT_SIBLING;
! 929: } else if ( ! nc && 0 == cnt) {
! 930: mdoc_argv_free(arg);
! 931: if ( ! pwarn(mdoc, line, ppos, WIGNE))
! 932: return(0);
! 933: }
! 934:
! 935: if (ppos > 1)
! 936: return(1);
! 937: return(append_delims(mdoc, line, pos, buf));
! 938: }
! 939:
! 940:
! 941: /*
! 942: * Block full-explicit and full-implicit.
! 943: */
! 944: static int
! 945: blk_full(MACRO_PROT_ARGS)
! 946: {
! 947: int c, lastarg, reopen;
! 948: struct mdoc_arg *arg;
! 949: char *p;
! 950:
! 951: if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
! 952: if ( ! rew_subblock(MDOC_BODY, mdoc,
! 953: tok, line, ppos))
! 954: return(0);
! 955: if ( ! rew_impblock(mdoc, tok, line, ppos))
! 956: return(0);
! 957: }
! 958:
! 959: for (arg = NULL;; ) {
! 960: lastarg = *pos;
! 961: c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
! 962:
! 963: if (ARGV_WORD == c) {
! 964: *pos = lastarg;
! 965: break;
! 966: }
! 967:
! 968: if (ARGV_EOLN == c)
! 969: break;
! 970: if (ARGV_ARG == c)
! 971: continue;
! 972:
! 973: mdoc_argv_free(arg);
! 974: return(0);
! 975: }
! 976:
! 977: if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg))
! 978: return(0);
! 979: mdoc->next = MDOC_NEXT_CHILD;
! 980:
! 981: if (0 == buf[*pos]) {
! 982: if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
! 983: return(0);
! 984: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 985: tok, line, ppos))
! 986: return(0);
! 987: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 988: return(0);
! 989: mdoc->next = MDOC_NEXT_CHILD;
! 990: return(1);
! 991: }
! 992:
! 993: if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
! 994: return(0);
! 995: mdoc->next = MDOC_NEXT_CHILD;
! 996:
! 997: for (reopen = 0;; ) {
! 998: lastarg = *pos;
! 999: c = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 1000:
! 1001: if (ARGS_ERROR == c)
! 1002: return(0);
! 1003: if (ARGS_EOLN == c)
! 1004: break;
! 1005: if (ARGS_PHRASE == c) {
! 1006: if (reopen && ! mdoc_head_alloc
! 1007: (mdoc, line, ppos, tok))
! 1008: return(0);
! 1009: mdoc->next = MDOC_NEXT_CHILD;
! 1010: /*
! 1011: * Phrases are self-contained macro phrases used
! 1012: * in the columnar output of a macro. They need
! 1013: * special handling.
! 1014: */
! 1015: if ( ! phrase(mdoc, line, lastarg, buf))
! 1016: return(0);
! 1017: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 1018: tok, line, ppos))
! 1019: return(0);
! 1020:
! 1021: reopen = 1;
! 1022: continue;
! 1023: }
! 1024:
! 1025: if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
! 1026: return(0);
! 1027:
! 1028: if (MDOC_MAX == c) {
! 1029: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 1030: return(0);
! 1031: mdoc->next = MDOC_NEXT_SIBLING;
! 1032: continue;
! 1033: }
! 1034:
! 1035: if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
! 1036: return(0);
! 1037: break;
! 1038: }
! 1039:
! 1040: if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
! 1041: return(0);
! 1042: if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
! 1043: return(0);
! 1044:
! 1045: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1046: return(0);
! 1047: mdoc->next = MDOC_NEXT_CHILD;
! 1048:
! 1049: return(1);
! 1050: }
! 1051:
! 1052:
! 1053: /*
! 1054: * Block partial-imnplicit scope.
! 1055: */
! 1056: static int
! 1057: blk_part_imp(MACRO_PROT_ARGS)
! 1058: {
! 1059: int lastarg, c;
! 1060: char *p;
! 1061: struct mdoc_node *blk, *body, *n;
! 1062:
! 1063: if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
! 1064: return(0);
! 1065: mdoc->next = MDOC_NEXT_CHILD;
! 1066: blk = mdoc->last;
! 1067:
! 1068: if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
! 1069: return(0);
! 1070: mdoc->next = MDOC_NEXT_SIBLING;
! 1071:
! 1072: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1073: return(0);
! 1074: mdoc->next = MDOC_NEXT_CHILD;
! 1075: body = mdoc->last;
! 1076:
! 1077: /* XXX - no known argument macros. */
! 1078:
! 1079: for (lastarg = ppos;; ) {
! 1080: lastarg = *pos;
! 1081: c = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 1082: assert(ARGS_PHRASE != c);
! 1083:
! 1084: if (ARGS_ERROR == c)
! 1085: return(0);
! 1086: if (ARGS_PUNCT == c)
! 1087: break;
! 1088: if (ARGS_EOLN == c)
! 1089: break;
! 1090:
! 1091: if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
! 1092: return(0);
! 1093: else if (MDOC_MAX == c) {
! 1094: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 1095: return(0);
! 1096: mdoc->next = MDOC_NEXT_SIBLING;
! 1097: continue;
! 1098: }
! 1099:
! 1100: if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
! 1101: return(0);
! 1102: break;
! 1103: }
! 1104:
! 1105: /*
! 1106: * Since we know what our context is, we can rewind directly to
! 1107: * it. This allows us to accomodate for our scope being
! 1108: * violated by another token.
! 1109: */
! 1110:
! 1111: for (n = mdoc->last; n; n = n->parent)
! 1112: if (body == n)
! 1113: break;
! 1114:
! 1115: if (NULL == n && ! pwarn(mdoc, body->line, body->pos, WIMPBRK))
! 1116: return(0);
! 1117:
! 1118: if (n && ! rew_last(mdoc, body))
! 1119: return(0);
! 1120:
! 1121: if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
! 1122: return(0);
! 1123:
! 1124: if (n && ! rew_last(mdoc, blk))
! 1125: return(0);
! 1126:
! 1127: return(1);
! 1128: }
! 1129:
! 1130:
! 1131: /*
! 1132: * Block partial-explicit macros.
! 1133: */
! 1134: static int
! 1135: blk_part_exp(MACRO_PROT_ARGS)
! 1136: {
! 1137: int lastarg, flushed, j, c, maxargs;
! 1138: char *p;
! 1139:
! 1140: lastarg = ppos;
! 1141: flushed = 0;
! 1142:
! 1143: /*
! 1144: * Number of arguments (head arguments). Only `Eo' has these,
! 1145: */
! 1146:
! 1147: switch (tok) {
! 1148: case (MDOC_Eo):
! 1149: maxargs = 1;
! 1150: break;
! 1151: default:
! 1152: maxargs = 0;
! 1153: break;
! 1154: }
! 1155:
! 1156: if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
! 1157: return(0);
! 1158: mdoc->next = MDOC_NEXT_CHILD;
! 1159:
! 1160: if (0 == maxargs) {
! 1161: if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
! 1162: return(0);
! 1163: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 1164: tok, line, ppos))
! 1165: return(0);
! 1166: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1167: return(0);
! 1168: flushed = 1;
! 1169: } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
! 1170: return(0);
! 1171:
! 1172: mdoc->next = MDOC_NEXT_CHILD;
! 1173:
! 1174: for (j = 0; ; j++) {
! 1175: lastarg = *pos;
! 1176: if (j == maxargs && ! flushed) {
! 1177: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 1178: tok, line, ppos))
! 1179: return(0);
! 1180: flushed = 1;
! 1181: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1182: return(0);
! 1183: mdoc->next = MDOC_NEXT_CHILD;
! 1184: }
! 1185:
! 1186: c = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 1187: assert(ARGS_PHRASE != c);
! 1188:
! 1189: if (ARGS_ERROR == c)
! 1190: return(0);
! 1191: if (ARGS_PUNCT == c)
! 1192: break;
! 1193: if (ARGS_EOLN == c)
! 1194: break;
! 1195:
! 1196: if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
! 1197: return(0);
! 1198: else if (MDOC_MAX != c) {
! 1199: if ( ! flushed) {
! 1200: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 1201: tok, line, ppos))
! 1202: return(0);
! 1203: flushed = 1;
! 1204: if ( ! mdoc_body_alloc(mdoc, line,
! 1205: ppos, tok))
! 1206: return(0);
! 1207: mdoc->next = MDOC_NEXT_CHILD;
! 1208: }
! 1209: if ( ! mdoc_macro(mdoc, c, line, lastarg,
! 1210: pos, buf))
! 1211: return(0);
! 1212: break;
! 1213: }
! 1214:
! 1215: if ( ! flushed && mdoc_isdelim(p)) {
! 1216: if ( ! rew_subblock(MDOC_HEAD, mdoc,
! 1217: tok, line, ppos))
! 1218: return(0);
! 1219: flushed = 1;
! 1220: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1221: return(0);
! 1222: mdoc->next = MDOC_NEXT_CHILD;
! 1223: }
! 1224:
! 1225: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 1226: return(0);
! 1227: mdoc->next = MDOC_NEXT_SIBLING;
! 1228: }
! 1229:
! 1230: if ( ! flushed) {
! 1231: if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
! 1232: return(0);
! 1233: if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
! 1234: return(0);
! 1235: mdoc->next = MDOC_NEXT_CHILD;
! 1236: }
! 1237:
! 1238: if (ppos > 1)
! 1239: return(1);
! 1240: return(append_delims(mdoc, line, pos, buf));
! 1241: }
! 1242:
! 1243:
! 1244: /*
! 1245: * In-line macros where reserved words signal closure of the macro.
! 1246: * Macros also have a fixed number of arguments.
! 1247: */
! 1248: static int
! 1249: in_line_argn(MACRO_PROT_ARGS)
! 1250: {
! 1251: int lastarg, flushed, j, c, maxargs;
! 1252: struct mdoc_arg *arg;
! 1253: char *p;
! 1254:
! 1255:
! 1256: /*
! 1257: * Fixed maximum arguments per macro. Some of these have none
! 1258: * and close as soon as the invocation is parsed.
! 1259: */
! 1260:
! 1261: switch (tok) {
! 1262: case (MDOC_Ap):
! 1263: /* FALLTHROUGH */
! 1264: case (MDOC_No):
! 1265: /* FALLTHROUGH */
! 1266: case (MDOC_Ns):
! 1267: /* FALLTHROUGH */
! 1268: case (MDOC_Ux):
! 1269: maxargs = 0;
! 1270: break;
! 1271: default:
! 1272: maxargs = 1;
! 1273: break;
! 1274: }
! 1275:
! 1276: for (lastarg = ppos, arg = NULL;; ) {
! 1277: lastarg = *pos;
! 1278: c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
! 1279:
! 1280: if (ARGV_WORD == c) {
! 1281: *pos = lastarg;
! 1282: break;
! 1283: }
! 1284:
! 1285: if (ARGV_EOLN == c)
! 1286: break;
! 1287: if (ARGV_ARG == c)
! 1288: continue;
! 1289:
! 1290: mdoc_argv_free(arg);
! 1291: return(0);
! 1292: }
! 1293:
! 1294: if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
! 1295: return(0);
! 1296: mdoc->next = MDOC_NEXT_CHILD;
! 1297:
! 1298: for (flushed = j = 0; ; j++) {
! 1299: lastarg = *pos;
! 1300:
! 1301: if (j == maxargs && ! flushed) {
! 1302: if ( ! rew_elem(mdoc, tok))
! 1303: return(0);
! 1304: flushed = 1;
! 1305: }
! 1306:
! 1307: c = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 1308:
! 1309: if (ARGS_ERROR == c)
! 1310: return(0);
! 1311: if (ARGS_PUNCT == c)
! 1312: break;
! 1313: if (ARGS_EOLN == c)
! 1314: break;
! 1315:
! 1316: if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
! 1317: return(0);
! 1318: else if (MDOC_MAX != c) {
! 1319: if ( ! flushed && ! rew_elem(mdoc, tok))
! 1320: return(0);
! 1321: flushed = 1;
! 1322: if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
! 1323: return(0);
! 1324: break;
! 1325: }
! 1326:
! 1327: if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
! 1328: ! flushed && mdoc_isdelim(p)) {
! 1329: if ( ! rew_elem(mdoc, tok))
! 1330: return(0);
! 1331: flushed = 1;
! 1332: }
! 1333:
! 1334: if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
! 1335: return(0);
! 1336: mdoc->next = MDOC_NEXT_SIBLING;
! 1337: }
! 1338:
! 1339: if ( ! flushed && ! rew_elem(mdoc, tok))
! 1340: return(0);
! 1341:
! 1342: if (ppos > 1)
! 1343: return(1);
! 1344: return(append_delims(mdoc, line, pos, buf));
! 1345: }
! 1346:
! 1347:
! 1348: /*
! 1349: * In-line macro that spans an entire line. May be callable, but has no
! 1350: * subsequent parsed arguments.
! 1351: */
! 1352: static int
! 1353: in_line_eoln(MACRO_PROT_ARGS)
! 1354: {
! 1355: int c, w, la;
! 1356: struct mdoc_arg *arg;
! 1357: char *p;
! 1358:
! 1359: assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
! 1360:
! 1361: arg = NULL;
! 1362:
! 1363: for (;;) {
! 1364: la = *pos;
! 1365: c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
! 1366:
! 1367: if (ARGV_WORD == c) {
! 1368: *pos = la;
! 1369: break;
! 1370: }
! 1371: if (ARGV_EOLN == c)
! 1372: break;
! 1373: if (ARGV_ARG == c)
! 1374: continue;
! 1375:
! 1376: mdoc_argv_free(arg);
! 1377: return(0);
! 1378: }
! 1379:
! 1380: if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
! 1381: return(0);
! 1382:
! 1383: mdoc->next = MDOC_NEXT_CHILD;
! 1384:
! 1385: for (;;) {
! 1386: la = *pos;
! 1387: w = mdoc_args(mdoc, line, pos, buf, tok, &p);
! 1388:
! 1389: if (ARGS_ERROR == w)
! 1390: return(0);
! 1391: if (ARGS_EOLN == w)
! 1392: break;
! 1393:
! 1394: c = ARGS_QWORD == w ? MDOC_MAX :
! 1395: lookup(mdoc, line, la, tok, p);
! 1396:
! 1397: if (MDOC_MAX != c && -1 != c) {
! 1398: if ( ! rew_elem(mdoc, tok))
! 1399: return(0);
! 1400: return(mdoc_macro(mdoc, c, line, la, pos, buf));
! 1401: } else if (-1 == c)
! 1402: return(0);
! 1403:
! 1404: if ( ! mdoc_word_alloc(mdoc, line, la, p))
! 1405: return(0);
! 1406: mdoc->next = MDOC_NEXT_SIBLING;
! 1407: }
! 1408:
! 1409: return(rew_elem(mdoc, tok));
! 1410: }
! 1411:
! 1412:
! 1413: /* ARGSUSED */
! 1414: static int
! 1415: obsolete(MACRO_PROT_ARGS)
! 1416: {
! 1417:
! 1418: return(pwarn(mdoc, line, ppos, WOBS));
! 1419: }
! 1420:
! 1421:
! 1422: static int
! 1423: phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
! 1424: {
! 1425: int i, la, c, quoted;
! 1426:
! 1427: /*
! 1428: * Parse over words in a phrase. We have to handle this
! 1429: * specially because we assume no calling context -- in normal
! 1430: * circumstances, we switch argument parsing based on whether
! 1431: * the parent macro accepts quotes, tabs, etc. Here, anything
! 1432: * goes.
! 1433: */
! 1434:
! 1435: for (i = ppos; buf[i]; ) {
! 1436: assert(' ' != buf[i]);
! 1437: la = i;
! 1438: quoted = 0;
! 1439:
! 1440: /*
! 1441: * Read to next token. If quoted (check not escaped),
! 1442: * scan ahead to next unescaped quote. If not quoted or
! 1443: * escape-quoted, then scan ahead to next space.
! 1444: */
! 1445:
! 1446: if ((i && '\"' == buf[i] && '\\' != buf[i - 1]) ||
! 1447: (0 == i && '\"' == buf[i])) {
! 1448: for (la = ++i; buf[i]; i++)
! 1449: if ('\"' != buf[i])
! 1450: continue;
! 1451: else if ('\\' != buf[i - 1])
! 1452: break;
! 1453: if (0 == buf[i])
! 1454: return(perr(mdoc, line, la, EQUOT));
! 1455: quoted = 1;
! 1456: } else
! 1457: for ( ; buf[i]; i++)
! 1458: if (i && ' ' == buf[i]) {
! 1459: if ('\\' != buf[i - 1])
! 1460: break;
! 1461: } else if (' ' == buf[i])
! 1462: break;
! 1463:
! 1464: /* If not end-of-line, terminate argument. */
! 1465:
! 1466: if (buf[i])
! 1467: buf[i++] = 0;
! 1468:
! 1469: /* Read to next argument. */
! 1470:
! 1471: for ( ; buf[i] && ' ' == buf[i]; i++)
! 1472: /* Spin. */ ;
! 1473:
! 1474: /*
! 1475: * If we're a non-quoted string, try to look up the
! 1476: * value as a macro and execute it, if found.
! 1477: */
! 1478:
! 1479: c = quoted ? MDOC_MAX :
! 1480: mdoc_hash_find(mdoc->htab, &buf[la]);
! 1481:
! 1482: if (MDOC_MAX != c) {
! 1483: if ( ! mdoc_macro(mdoc, c, line, la, &i, buf))
! 1484: return(0);
! 1485: return(append_delims(mdoc, line, &i, buf));
! 1486: }
! 1487:
! 1488: /* A regular word or quoted string. */
! 1489:
! 1490: if ( ! mdoc_word_alloc(mdoc, line, la, &buf[la]))
! 1491: return(0);
! 1492: mdoc->next = MDOC_NEXT_SIBLING;
! 1493: }
! 1494:
! 1495: return(1);
! 1496: }