Annotation of mandoc/validate.c, Revision 1.8
1.8 ! kristaps 1: /* $Id: macro.c,v 1.12 2008/12/29 19:25:29 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 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 <ctype.h>
1.8 ! kristaps 21: #include <stdlib.h>
1.1 kristaps 22: #include <stdio.h>
23: #include <string.h>
1.8 ! kristaps 24: #ifdef __linux__
! 25: #include <time.h>
! 26: #endif
1.1 kristaps 27:
28: #include "private.h"
29:
1.8 ! kristaps 30: /* FIXME: maxlineargs should be per LINE, no per TOKEN. */
! 31: /* FIXME: prologue check should be in macro_call. */
1.2 kristaps 32:
1.8 ! kristaps 33: #define _CC(p) ((const char **)p)
1.1 kristaps 34:
1.8 ! kristaps 35: static int scope_rewind_exp(struct mdoc *, int, int, int);
! 36: static int scope_rewind_imp(struct mdoc *, int, int);
! 37: static int append_text(struct mdoc *, int,
! 38: int, int, char *[]);
! 39: static int append_const(struct mdoc *, int, int, int, char *[]);
! 40: static int append_constarg(struct mdoc *, int, int,
! 41: int, const struct mdoc_arg *);
! 42: static int append_scoped(struct mdoc *, int, int, int,
! 43: const char *[], int, const struct mdoc_arg *);
! 44: static int append_delims(struct mdoc *, int, int *, char *);
1.6 kristaps 45:
46:
47: static int
1.8 ! kristaps 48: append_delims(struct mdoc *mdoc, int tok, int *pos, char *buf)
1.6 kristaps 49: {
1.8 ! kristaps 50: int c, lastarg;
! 51: char *p;
! 52:
! 53: if (0 == buf[*pos])
! 54: return(1);
! 55:
! 56: mdoc_msg(mdoc, *pos, "`%s' flushing punctuation",
! 57: mdoc_macronames[tok]);
! 58:
! 59: for (;;) {
! 60: lastarg = *pos;
! 61: c = mdoc_args(mdoc, tok, pos, buf, 0, &p);
! 62: if (ARGS_ERROR == c)
! 63: return(0);
! 64: else if (ARGS_EOLN == c)
! 65: break;
! 66: assert(mdoc_isdelim(p));
! 67: mdoc_word_alloc(mdoc, lastarg, p);
! 68: }
1.6 kristaps 69:
1.8 ! kristaps 70: return(1);
1.6 kristaps 71: }
72:
73:
74: static int
1.8 ! kristaps 75: scope_rewind_imp(struct mdoc *mdoc, int ppos, int tok)
1.6 kristaps 76: {
1.8 ! kristaps 77: struct mdoc_node *n;
! 78: int t;
! 79:
! 80: n = mdoc->last ? mdoc->last->parent : NULL;
! 81:
! 82: /* LINTED */
! 83: for ( ; n; n = n->parent) {
! 84: if (MDOC_BLOCK != n->type)
! 85: continue;
! 86: if (tok == (t = n->data.block.tok))
! 87: break;
! 88: if ( ! (MDOC_EXPLICIT & mdoc_macros[t].flags))
! 89: continue;
! 90: return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
! 91: }
1.6 kristaps 92:
1.8 ! kristaps 93: if (n) {
! 94: mdoc->last = n;
! 95: mdoc_msg(mdoc, ppos, "scope: rewound implicit `%s'",
! 96: mdoc_macronames[tok]);
! 97: return(1);
! 98: }
! 99:
! 100: mdoc_msg(mdoc, ppos, "scope: new implicit `%s'",
! 101: mdoc_macronames[tok]);
! 102: return(1);
1.6 kristaps 103: }
1.1 kristaps 104:
105:
106: static int
1.8 ! kristaps 107: scope_rewind_exp(struct mdoc *mdoc, int ppos, int tok, int dst)
1.1 kristaps 108: {
1.8 ! kristaps 109: struct mdoc_node *n;
1.1 kristaps 110:
1.8 ! kristaps 111: assert(mdoc->last);
1.1 kristaps 112:
1.5 kristaps 113: /* LINTED */
1.8 ! kristaps 114: for (n = mdoc->last->parent; n; n = n->parent) {
! 115: if (MDOC_BLOCK != n->type)
! 116: continue;
! 117: if (dst == n->data.block.tok)
! 118: break;
! 119: return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
! 120: }
! 121:
! 122: if (NULL == (mdoc->last = n))
! 123: return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NOCTX));
! 124:
! 125: mdoc_msg(mdoc, ppos, "scope: rewound explicit `%s' to `%s'",
! 126: mdoc_macronames[tok], mdoc_macronames[dst]);
1.1 kristaps 127:
128: return(1);
129: }
130:
131:
132: static int
1.8 ! kristaps 133: append_constarg(struct mdoc *mdoc, int tok, int pos,
! 134: int argc, const struct mdoc_arg *argv)
1.1 kristaps 135: {
136:
1.8 ! kristaps 137: switch (tok) {
! 138: default:
! 139: break;
! 140: }
1.1 kristaps 141:
1.8 ! kristaps 142: mdoc_elem_alloc(mdoc, pos, tok, argc, argv, 0, NULL);
1.2 kristaps 143: return(1);
1.1 kristaps 144: }
145:
146:
1.8 ! kristaps 147: /*
! 148: * Append a node with implicit or explicit scoping ONLY. ALL macros
! 149: * with the implicit- or explicit-scope callback must be included here.
! 150: */
1.1 kristaps 151: static int
1.8 ! kristaps 152: append_scoped(struct mdoc *mdoc, int tok, int pos,
! 153: int sz, const char *args[],
! 154: int argc, const struct mdoc_arg *argv)
1.1 kristaps 155: {
1.8 ! kristaps 156: enum mdoc_sec sec;
! 157: struct mdoc_node *node;
! 158:
! 159: switch (tok) {
! 160: /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
! 161:
! 162: case (MDOC_Sh):
! 163: /*
! 164: * Check rules for section ordering. We can have
! 165: * "known" sections (like NAME and so on) and "custom"
! 166: * sections, which are unknown. If we have a known
! 167: * section, we should fall within the conventional
! 168: * section order.
! 169: */
! 170: if (0 == sz)
! 171: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
! 172:
! 173: sec = mdoc_atosec((size_t)sz, _CC(args));
! 174: if (SEC_CUSTOM != sec && sec < mdoc->sec_lastn)
! 175: if ( ! mdoc_warn(mdoc, tok, pos, WARN_SEC_OO))
! 176: return(0);
1.1 kristaps 177:
1.8 ! kristaps 178: if (SEC_BODY == mdoc->sec_last && SEC_NAME != sec)
! 179: return(mdoc_err(mdoc, tok, pos, ERR_SEC_NAME));
! 180: if (SEC_CUSTOM != sec)
! 181: mdoc->sec_lastn = sec;
! 182: mdoc->sec_last = sec;
! 183: break;
! 184:
! 185: case (MDOC_Ss):
! 186: if (0 != sz)
! 187: break;
! 188: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
! 189:
! 190: case (MDOC_Bd):
! 191: /*
! 192: * We can't be nested within any other block displays
! 193: * (or really any other kind of display, although Bd is
! 194: * the only multi-line one that will show up).
! 195: */
! 196: assert(mdoc->last);
! 197: node = mdoc->last->parent;
! 198: /* LINTED */
! 199: for ( ; node; node = node->parent) {
! 200: if (node->type != MDOC_BLOCK)
! 201: continue;
! 202: if (node->data.block.tok != MDOC_Bd)
! 203: continue;
! 204: break;
! 205: }
! 206: if (NULL == node)
! 207: break;
! 208: return(mdoc_err(mdoc, tok, pos, ERR_SCOPE_NONEST));
1.1 kristaps 209:
1.8 ! kristaps 210: case (MDOC_Bl):
! 211: break;
1.6 kristaps 212:
1.8 ! kristaps 213: /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
! 214: default:
! 215: abort();
! 216: /* NOTREACHED */
! 217: }
1.1 kristaps 218:
1.8 ! kristaps 219: mdoc_block_alloc(mdoc, pos, tok, (size_t)argc, argv);
! 220: mdoc_head_alloc(mdoc, pos, tok, (size_t)sz, _CC(args));
! 221: mdoc_body_alloc(mdoc, pos, tok);
! 222: return(1);
! 223: }
1.1 kristaps 224:
225:
1.8 ! kristaps 226: static int
! 227: append_const(struct mdoc *mdoc, int tok,
! 228: int pos, int sz, char *args[])
! 229: {
1.1 kristaps 230:
1.8 ! kristaps 231: switch (tok) {
! 232: /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
1.1 kristaps 233:
1.8 ! kristaps 234: /* FIXME: this is the ugliest part of this page. */
! 235: case (MDOC_At):
! 236: /* This needs special handling. */
! 237: if (0 == sz)
! 238: break;
! 239: else if (sz > 2)
! 240: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_LE2));
1.2 kristaps 241:
1.8 ! kristaps 242: if (ATT_DEFAULT != mdoc_atoatt(args[0])) {
! 243: mdoc_elem_alloc(mdoc, pos, tok, 0,
! 244: NULL, 1, _CC(&args[0]));
! 245: } else {
! 246: mdoc_elem_alloc(mdoc, pos, tok,
! 247: 0, NULL, 0, NULL);
! 248: if (mdoc_isdelim(args[0]))
! 249: return(mdoc_err(mdoc, tok, pos, ERR_SYNTAX_NOPUNCT));
! 250: mdoc_word_alloc(mdoc, pos, args[0]);
1.1 kristaps 251: }
252:
1.8 ! kristaps 253: if (1 == sz)
! 254: return(1);
! 255: if (mdoc_isdelim(args[1]))
! 256: return(mdoc_err(mdoc, tok, pos, ERR_SYNTAX_NOPUNCT));
! 257: mdoc_word_alloc(mdoc, pos, args[1]);
! 258: return(1);
1.1 kristaps 259:
1.8 ! kristaps 260: case (MDOC_Nd):
! 261: if (sz > 0)
! 262: break;
! 263: if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_GE1))
1.1 kristaps 264: return(0);
1.8 ! kristaps 265: break;
! 266:
! 267: case (MDOC_Hf):
! 268: if (1 == sz)
! 269: break;
! 270: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_EQ1));
! 271:
! 272: case (MDOC_Bx):
! 273: /* FALLTHROUGH */
! 274: case (MDOC_Bsx):
! 275: /* FALLTHROUGH */
! 276: case (MDOC_Os):
! 277: /* FALLTHROUGH */
! 278: case (MDOC_Fx):
! 279: /* FALLTHROUGH */
! 280: case (MDOC_Nx):
! 281: assert(sz <= 1);
! 282: break;
! 283:
! 284: case (MDOC_Ux):
! 285: assert(0 == sz);
! 286: break;
! 287:
! 288: case (MDOC_Bt):
! 289: /* FALLTHROUGH */
! 290: case (MDOC_Ud):
! 291: if (0 == sz)
! 292: break;
! 293: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_EQ0));
! 294:
! 295: /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
! 296: default:
! 297: abort();
! 298: /* NOTREACHED */
1.1 kristaps 299: }
300:
1.8 ! kristaps 301: mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args));
1.1 kristaps 302: return(1);
303: }
304:
305:
1.8 ! kristaps 306: static int
! 307: append_text(struct mdoc *mdoc, int tok,
! 308: int pos, int sz, char *args[])
1.1 kristaps 309: {
310:
1.8 ! kristaps 311: switch (tok) {
! 312: /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
! 313: case (MDOC_Pp):
! 314: if (0 == sz)
! 315: break;
! 316: if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_EQ0))
! 317: return(0);
! 318: break;
! 319:
! 320: case (MDOC_Ft):
! 321: /* FALLTHROUGH */
! 322: case (MDOC_Li):
! 323: /* FALLTHROUGH */
! 324: case (MDOC_Ms):
! 325: /* FALLTHROUGH */
! 326: case (MDOC_Pa):
! 327: /* FALLTHROUGH */
! 328: case (MDOC_Tn):
! 329: if (0 < sz)
! 330: break;
! 331: if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_GE1))
! 332: return(0);
! 333: break;
! 334:
! 335: case (MDOC_Ar):
! 336: /* FALLTHROUGH */
! 337: case (MDOC_Cm):
! 338: /* FALLTHROUGH */
! 339: case (MDOC_Fl):
! 340: /* These can have no arguments. */
! 341: break;
! 342:
! 343: case (MDOC_Ad):
! 344: /* FALLTHROUGH */
! 345: case (MDOC_Em):
! 346: /* FALLTHROUGH */
! 347: case (MDOC_Er):
! 348: /* FALLTHROUGH */
! 349: case (MDOC_Ev):
! 350: /* FALLTHROUGH */
! 351: case (MDOC_Fa):
! 352: /* FALLTHROUGH */
! 353: case (MDOC_Dv):
! 354: /* FALLTHROUGH */
! 355: case (MDOC_Ic):
! 356: /* FALLTHROUGH */
! 357: case (MDOC_Sy):
! 358: /* FALLTHROUGH */
! 359: case (MDOC_Sx):
! 360: /* FALLTHROUGH */
! 361: case (MDOC_Va):
! 362: /* FALLTHROUGH */
! 363: case (MDOC_Vt):
! 364: if (0 < sz)
! 365: break;
! 366: return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
! 367: /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
! 368: default:
! 369: abort();
! 370: /* NOTREACHED */
! 371: }
! 372:
! 373: mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args));
! 374: return(1);
1.1 kristaps 375: }
376:
377:
378: int
1.8 ! kristaps 379: macro_text(MACRO_PROT_ARGS)
1.1 kristaps 380: {
1.8 ! kristaps 381: int lastarg, lastpunct, c, j;
! 382: char *args[MDOC_LINEARG_MAX];
! 383:
! 384: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 385: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
1.1 kristaps 386:
1.8 ! kristaps 387: /* Token pre-processing. */
1.1 kristaps 388:
1.8 ! kristaps 389: switch (tok) {
! 390: case (MDOC_Pp):
! 391: /* `.Pp' ignored when following `.Sh' or `.Ss'. */
! 392: assert(mdoc->last);
! 393: if (MDOC_BODY != mdoc->last->type)
! 394: break;
! 395: switch (mdoc->last->data.body.tok) {
! 396: case (MDOC_Ss):
! 397: /* FALLTHROUGH */
! 398: case (MDOC_Sh):
! 399: if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_AFTER_BLK))
! 400: return(0);
! 401: return(1);
! 402: default:
! 403: break;
! 404: }
! 405: break;
! 406: default:
! 407: break;
! 408: }
! 409:
! 410: /* Process line parameters. */
! 411:
! 412: j = 0;
! 413: lastarg = ppos;
! 414: lastpunct = 0;
! 415:
! 416: again:
! 417: if (j == MDOC_LINEARG_MAX)
! 418: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
1.1 kristaps 419:
1.8 ! kristaps 420: /*
! 421: * Parse out the next argument, unquoted and unescaped. If
! 422: * we're a word (which may be punctuation followed eventually by
! 423: * a real word), then fall into checking for callables. If
! 424: * only punctuation remains and we're the first, then flush
! 425: * arguments, punctuation and exit; else, return to the caller.
! 426: */
1.1 kristaps 427:
1.8 ! kristaps 428: lastarg = *pos;
1.1 kristaps 429:
1.8 ! kristaps 430: switch (mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &args[j])) {
! 431: case (ARGS_ERROR):
! 432: return(0);
! 433: case (ARGS_WORD):
! 434: break;
! 435: case (ARGS_PUNCT):
! 436: if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
! 437: return(0);
! 438: if (ppos > 1)
! 439: return(1);
! 440: return(append_delims(mdoc, tok, pos, buf));
! 441: case (ARGS_EOLN):
! 442: if (lastpunct)
! 443: return(1);
! 444: return(append_text(mdoc, tok, ppos, j, args));
! 445: default:
! 446: abort();
! 447: /* NOTREACHED */
! 448: }
1.1 kristaps 449:
1.8 ! kristaps 450: /*
! 451: * Command found. First flush out arguments, then call the
! 452: * command. If we're the line macro when it exits, flush
! 453: * terminal punctuation.
! 454: */
1.1 kristaps 455:
1.8 ! kristaps 456: if (MDOC_MAX != (c = mdoc_find(mdoc, args[j]))) {
! 457: if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
! 458: return(0);
! 459: if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
! 460: return(0);
! 461: if (ppos > 1)
! 462: return(1);
! 463: return(append_delims(mdoc, tok, pos, buf));
! 464: }
1.1 kristaps 465:
1.8 ! kristaps 466: /* Word/non-term-punctuation found. */
1.1 kristaps 467:
1.8 ! kristaps 468: if ( ! mdoc_isdelim(args[j])) {
! 469: /* Words are appended to the array of arguments. */
! 470: j++;
! 471: lastpunct = 0;
! 472: goto again;
1.1 kristaps 473: }
474:
1.8 ! kristaps 475: /*
! 476: * For punctuation, flush all collected words, then flush
! 477: * punctuation, then start collecting again. Of course, this
! 478: * is non-terminal punctuation.
! 479: */
! 480:
! 481: if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
! 482: return(0);
! 483:
! 484: mdoc_word_alloc(mdoc, lastarg, args[j]);
! 485: j = 0;
! 486: lastpunct = 1;
! 487:
! 488: goto again;
! 489: /* NOTREACHED */
1.1 kristaps 490: }
491:
492:
1.8 ! kristaps 493: int
! 494: macro_prologue_dtitle(MACRO_PROT_ARGS)
1.1 kristaps 495: {
1.8 ! kristaps 496: int lastarg, j;
! 497: char *args[MDOC_LINEARG_MAX];
1.2 kristaps 498:
1.8 ! kristaps 499: if (SEC_PROLOGUE != mdoc->sec_lastn)
! 500: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
! 501: if (0 == mdoc->meta.date)
! 502: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
! 503: if (mdoc->meta.title[0])
! 504: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
! 505:
! 506: j = -1;
! 507: lastarg = ppos;
! 508:
! 509: again:
! 510: if (j == MDOC_LINEARG_MAX)
! 511: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 512:
! 513: lastarg = *pos;
! 514:
! 515: switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[++j])) {
! 516: case (ARGS_EOLN):
! 517: if (mdoc->meta.title)
! 518: return(1);
! 519: if ( ! mdoc_warn(mdoc, tok, ppos, WARN_ARGS_GE1))
! 520: return(0);
! 521: (void)xstrlcpy(mdoc->meta.title,
! 522: "UNTITLED", META_TITLE_SZ);
! 523: return(1);
! 524: case (ARGS_ERROR):
! 525: return(0);
! 526: default:
! 527: break;
! 528: }
1.2 kristaps 529:
1.8 ! kristaps 530: if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
! 531: (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
1.6 kristaps 532: return(0);
1.4 kristaps 533:
1.8 ! kristaps 534: if (0 == j) {
! 535: if (xstrlcpy(mdoc->meta.title, args[0], META_TITLE_SZ))
! 536: goto again;
! 537: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
! 538:
! 539: } else if (1 == j) {
! 540: mdoc->meta.msec = mdoc_atomsec(args[1]);
! 541: if (MSEC_DEFAULT != mdoc->meta.msec)
! 542: goto again;
! 543: return(mdoc_err(mdoc, tok, -1, ERR_SYNTAX_ARGFORM));
! 544:
! 545: } else if (2 == j) {
! 546: mdoc->meta.vol = mdoc_atovol(args[2]);
! 547: if (VOL_DEFAULT != mdoc->meta.vol)
! 548: goto again;
! 549: mdoc->meta.arch = mdoc_atoarch(args[2]);
! 550: if (ARCH_DEFAULT != mdoc->meta.arch)
! 551: goto again;
! 552: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
! 553: }
! 554:
! 555: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
1.1 kristaps 556: }
557:
558:
1.8 ! kristaps 559: int
! 560: macro_prologue_os(MACRO_PROT_ARGS)
1.1 kristaps 561: {
1.8 ! kristaps 562: int lastarg, j;
! 563: char *args[MDOC_LINEARG_MAX];
1.1 kristaps 564:
1.8 ! kristaps 565: if (SEC_PROLOGUE != mdoc->sec_lastn)
! 566: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
! 567: if (0 == mdoc->meta.title[0])
! 568: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
! 569: if (mdoc->meta.os[0])
! 570: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
! 571:
! 572: j = -1;
! 573: lastarg = ppos;
! 574:
! 575: again:
! 576: if (j == MDOC_LINEARG_MAX)
! 577: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 578:
! 579: lastarg = *pos;
! 580:
! 581: switch (mdoc_args(mdoc, tok, pos, buf,
! 582: ARGS_QUOTED, &args[++j])) {
! 583: case (ARGS_EOLN):
! 584: mdoc->sec_lastn = mdoc->sec_last = SEC_BODY;
! 585: return(1);
! 586: case (ARGS_ERROR):
1.2 kristaps 587: return(0);
1.8 ! kristaps 588: default:
! 589: break;
! 590: }
! 591:
! 592: if ( ! xstrlcat(mdoc->meta.os, args[j], sizeof(mdoc->meta.os)))
! 593: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
! 594: if ( ! xstrlcat(mdoc->meta.os, " ", sizeof(mdoc->meta.os)))
! 595: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.6 kristaps 596:
1.8 ! kristaps 597: goto again;
! 598: /* NOTREACHED */
1.1 kristaps 599: }
600:
601:
1.8 ! kristaps 602: int
! 603: macro_prologue_ddate(MACRO_PROT_ARGS)
1.1 kristaps 604: {
1.8 ! kristaps 605: int lastarg, j;
! 606: char *args[MDOC_LINEARG_MAX], date[64];
! 607:
! 608: if (SEC_PROLOGUE != mdoc->sec_lastn)
! 609: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
! 610: if (mdoc->meta.title[0])
! 611: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
! 612: if (mdoc->meta.date)
! 613: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
! 614:
! 615: j = -1;
! 616: date[0] = 0;
! 617: lastarg = ppos;
! 618:
! 619: again:
! 620: if (j == MDOC_LINEARG_MAX)
! 621: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 622:
! 623: lastarg = *pos;
! 624: switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[++j])) {
! 625: case (ARGS_EOLN):
! 626: if (mdoc->meta.date)
! 627: return(1);
! 628: mdoc->meta.date = mdoc_atotime(date);
! 629: if (mdoc->meta.date)
! 630: return(1);
! 631: return(mdoc_err(mdoc, tok, ppos, ERR_SYNTAX_ARGFORM));
! 632: case (ARGS_ERROR):
! 633: return(0);
! 634: default:
! 635: break;
! 636: }
! 637:
! 638: if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
! 639: (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
! 640: return(0);
! 641:
! 642: if (0 == j) {
! 643: if (xstrcmp("$Mdocdate$", args[j])) {
! 644: mdoc->meta.date = time(NULL);
! 645: goto again;
! 646: } else if (xstrcmp("$Mdocdate:", args[j]))
! 647: goto again;
! 648: } else if (4 == j)
! 649: if ( ! xstrcmp("$", args[j]))
! 650: goto again;
! 651:
! 652: if ( ! xstrlcat(date, args[j], sizeof(date)))
! 653: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
! 654: if ( ! xstrlcat(date, " ", sizeof(date)))
! 655: return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.1 kristaps 656:
1.8 ! kristaps 657: goto again;
! 658: /* NOTREACHED */
1.1 kristaps 659: }
660:
661:
1.8 ! kristaps 662: int
! 663: macro_scoped_explicit(MACRO_PROT_ARGS)
1.1 kristaps 664: {
1.8 ! kristaps 665: int c, lastarg, j;
! 666: struct mdoc_arg argv[MDOC_LINEARG_MAX];
! 667: struct mdoc_node *n;
! 668:
! 669: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 670: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
! 671:
! 672: /*
! 673: * First close out the explicit scope. The `end' tags (such as
! 674: * `.El' to `.Bl' don't cause anything to happen: we merely
! 675: * readjust our last parse point.
! 676: */
! 677:
! 678: switch (tok) {
! 679: case (MDOC_El):
! 680: return(scope_rewind_exp(mdoc, ppos, tok, MDOC_Bl));
! 681: case (MDOC_Ed):
! 682: return(scope_rewind_exp(mdoc, ppos, tok, MDOC_Bd));
! 683: default:
! 684: break;
! 685: }
1.1 kristaps 686:
1.8 ! kristaps 687: assert(MDOC_EXPLICIT & mdoc_macros[tok].flags);
1.1 kristaps 688:
1.8 ! kristaps 689: /* Token pre-processing. */
! 690:
! 691: switch (tok) {
! 692: case (MDOC_Bl):
! 693: /* FALLTHROUGH */
! 694: case (MDOC_Bd):
! 695: /* `.Pp' ignored when preceding `.Bl' or `.Bd'. */
! 696: assert(mdoc->last);
! 697: if (MDOC_ELEM != mdoc->last->type)
! 698: break;
! 699: if (MDOC_Pp != mdoc->last->data.elem.tok)
! 700: break;
! 701: if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_BEFORE_BLK))
1.1 kristaps 702: return(0);
1.8 ! kristaps 703: assert(mdoc->last->prev);
! 704: n = mdoc->last;
! 705: mdoc->last = mdoc->last->prev;
! 706: mdoc->last->next = NULL;
! 707: mdoc_node_free(n);
! 708: break;
! 709: default:
! 710: break;
! 711: }
! 712:
! 713: lastarg = *pos;
! 714:
! 715: for (j = 0; j < MDOC_LINEARG_MAX; j++) {
! 716: lastarg = *pos;
! 717: c = mdoc_argv(mdoc, tok, &argv[j], pos, buf);
! 718: if (0 == c)
! 719: break;
! 720: else if (1 == c)
! 721: continue;
! 722:
! 723: mdoc_argv_free(j, argv);
! 724: return(0);
! 725: }
! 726:
! 727: if (MDOC_LINEARG_MAX == j) {
! 728: mdoc_argv_free(j, argv);
! 729: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 730: }
! 731:
! 732: c = append_scoped(mdoc, tok, ppos, 0, NULL, j, argv);
! 733: mdoc_argv_free(j, argv);
! 734: return(c);
! 735: }
! 736:
! 737:
! 738: /*
! 739: * Implicity-scoped macros, like `.Ss', have a scope that terminates
! 740: * with a subsequent call to the same macro. Implicit macros cannot
! 741: * break the scope of explicitly-scoped macros; however, they can break
! 742: * the scope of other implicit macros (so `.Sh' can break `.Ss'). This
! 743: * is ok with macros like `.It' because they exist only within an
! 744: * explicit context.
! 745: *
! 746: * These macros put line arguments (which it's allowed to have) into the
! 747: * HEAD section and open a BODY scope to be used until the macro scope
! 748: * closes.
! 749: */
! 750: int
! 751: macro_scoped_implicit(MACRO_PROT_ARGS)
! 752: {
! 753: int lastarg, j;
! 754: char *args[MDOC_LINEARG_MAX];
! 755: struct mdoc_node *n;
! 756:
! 757: assert( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
! 758:
! 759: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 760: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
! 761:
! 762: /* Token pre-processing. */
! 763:
! 764: switch (tok) {
! 765: case (MDOC_Ss):
! 766: /* FALLTHROUGH */
! 767: case (MDOC_Sh):
! 768: /* `.Pp' ignored when preceding `.Ss' or `.Sh'. */
! 769: if (NULL == mdoc->last)
! 770: break;
! 771: if (MDOC_ELEM != mdoc->last->type)
! 772: break;
! 773: if (MDOC_Pp != mdoc->last->data.elem.tok)
! 774: break;
! 775: if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_BEFORE_BLK))
1.1 kristaps 776: return(0);
1.8 ! kristaps 777: assert(mdoc->last->prev);
! 778: n = mdoc->last;
! 779: mdoc_msg(mdoc, ppos, "removing prior `Pp' macro");
! 780: mdoc->last = mdoc->last->prev;
! 781: mdoc->last->next = NULL;
! 782: mdoc_node_free(n);
! 783: break;
! 784: default:
! 785: break;
! 786: }
! 787:
! 788: /* Rewind our scope. */
! 789:
! 790: if ( ! scope_rewind_imp(mdoc, ppos, tok))
1.2 kristaps 791: return(0);
1.1 kristaps 792:
1.8 ! kristaps 793: j = 0;
! 794: lastarg = ppos;
! 795:
! 796: /*
! 797: * Process until we hit a line. Note that current implicit
! 798: * macros don't have any arguments, so we don't need to do any
! 799: * argument processing.
! 800: */
! 801:
! 802: again:
! 803: if (j == MDOC_LINEARG_MAX)
! 804: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 805:
! 806: lastarg = *pos;
! 807:
! 808: switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[j])) {
! 809: case (ARGS_ERROR):
1.1 kristaps 810: return(0);
1.8 ! kristaps 811: case (ARGS_EOLN):
! 812: return(append_scoped(mdoc, tok, ppos, j, _CC(args), 0, NULL));
! 813: default:
! 814: break;
! 815: }
1.5 kristaps 816:
1.8 ! kristaps 817: if (MDOC_MAX != mdoc_find(mdoc, args[j]))
! 818: if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
1.5 kristaps 819: return(0);
1.8 ! kristaps 820:
! 821: j++;
! 822: goto again;
! 823: /* NOTREACHED */
! 824: }
! 825:
! 826:
! 827: /*
! 828: * A line-scoped macro opens a scope for the contents of its line, which
! 829: * are placed under the HEAD node. Punctuation trailing the line is put
! 830: * as a sibling to the HEAD node, under the BLOCK node.
! 831: */
! 832: int
! 833: macro_scoped_line(MACRO_PROT_ARGS)
! 834: {
! 835: int lastarg, c, j;
! 836: char *p;
! 837: struct mdoc_node *n;
! 838:
! 839: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 840: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
! 841:
! 842: assert(1 == ppos);
! 843:
! 844: /* Token pre-processing. */
! 845:
! 846: switch (tok) {
! 847: case (MDOC_D1):
! 848: /* FALLTHROUGH */
! 849: case (MDOC_Dl):
! 850: /* These can't be nested in a display block. */
! 851: assert(mdoc->last);
! 852: for (n = mdoc->last->parent ; n; n = n->parent)
! 853: if (MDOC_BLOCK != n->type)
! 854: continue;
! 855: else if (MDOC_Bd == n->data.block.tok)
! 856: break;
! 857: if (NULL == n)
! 858: break;
! 859: return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NONEST));
! 860: default:
! 861: break;
! 862: }
! 863:
! 864: /*
! 865: * All line-scoped macros have a HEAD and optionally a BODY
! 866: * section. We open our scope here; when we exit this function,
! 867: * we'll rewind our scope appropriately.
! 868: */
! 869:
! 870: mdoc_block_alloc(mdoc, ppos, tok, 0, NULL);
! 871: mdoc_head_alloc(mdoc, ppos, tok, 0, NULL);
! 872:
! 873: /* Process line parameters. */
! 874:
! 875: j = 0;
! 876: lastarg = ppos;
! 877:
! 878: again:
! 879: if (j == MDOC_LINEARG_MAX)
! 880: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 881:
! 882: lastarg = *pos;
! 883: c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p);
! 884:
! 885: switch (c) {
! 886: case (ARGS_ERROR):
! 887: return(0);
! 888: case (ARGS_WORD):
! 889: break;
! 890: case (ARGS_PUNCT):
! 891: if ( ! append_delims(mdoc, tok, pos, buf))
1.5 kristaps 892: return(0);
1.8 ! kristaps 893: return(scope_rewind_imp(mdoc, ppos, tok));
! 894: case (ARGS_EOLN):
! 895: return(scope_rewind_imp(mdoc, ppos, tok));
! 896: default:
! 897: abort();
! 898: /* NOTREACHED */
! 899: }
! 900:
! 901: if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
! 902: if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
1.5 kristaps 903: return(0);
1.8 ! kristaps 904: if ( ! append_delims(mdoc, tok, pos, buf))
1.5 kristaps 905: return(0);
1.8 ! kristaps 906: return(scope_rewind_imp(mdoc, ppos, tok));
1.5 kristaps 907: }
908:
1.8 ! kristaps 909: if (mdoc_isdelim(p))
! 910: j = 0;
1.1 kristaps 911:
1.8 ! kristaps 912: mdoc_word_alloc(mdoc, lastarg, p);
! 913: goto again;
! 914: /* NOTREACHED */
1.1 kristaps 915: }
916:
917:
1.8 ! kristaps 918: /*
! 919: * Partial-line scope is identical to line scope (macro_scoped_line())
! 920: * except that trailing punctuation is appended to the BLOCK, instead of
! 921: * contained within the HEAD.
! 922: */
! 923: int
! 924: macro_scoped_pline(MACRO_PROT_ARGS)
1.1 kristaps 925: {
1.8 ! kristaps 926: int lastarg, c, j;
! 927: char *p;
1.1 kristaps 928:
1.8 ! kristaps 929: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 930: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
1.1 kristaps 931:
1.8 ! kristaps 932: /* Token pre-processing. */
1.2 kristaps 933:
1.8 ! kristaps 934: switch (tok) {
! 935: case (MDOC_Ql):
! 936: if ( ! mdoc_warn(mdoc, tok, ppos, WARN_COMPAT_TROFF))
1.2 kristaps 937: return(0);
1.8 ! kristaps 938: break;
! 939: default:
! 940: break;
! 941: }
! 942:
! 943: mdoc_block_alloc(mdoc, ppos, tok, 0, NULL);
! 944: mdoc_head_alloc(mdoc, ppos, tok, 0, NULL);
! 945:
! 946: /* Process line parameters. */
! 947:
! 948: j = 0;
! 949: lastarg = ppos;
! 950:
! 951: again:
! 952: if (j == MDOC_LINEARG_MAX)
! 953: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 954:
! 955: lastarg = *pos;
! 956: c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p);
! 957:
! 958: switch (c) {
! 959: case (ARGS_ERROR):
! 960: return(0);
! 961: case (ARGS_WORD):
! 962: break;
! 963: case (ARGS_PUNCT):
! 964: if ( ! scope_rewind_imp(mdoc, ppos, tok))
! 965: return(0);
! 966: if (ppos > 1)
! 967: return(1);
! 968: return(append_delims(mdoc, tok, pos, buf));
! 969: case (ARGS_EOLN):
! 970: return(scope_rewind_imp(mdoc, ppos, tok));
! 971: default:
! 972: abort();
! 973: /* NOTREACHED */
! 974: }
! 975:
! 976: if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
! 977: if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
! 978: return(0);
! 979: if ( ! scope_rewind_imp(mdoc, ppos, tok))
1.2 kristaps 980: return(0);
1.8 ! kristaps 981: if (ppos > 1)
! 982: return(1);
! 983: return(append_delims(mdoc, tok, pos, buf));
! 984: }
1.1 kristaps 985:
1.8 ! kristaps 986: if (mdoc_isdelim(p))
! 987: j = 0;
1.1 kristaps 988:
1.8 ! kristaps 989: mdoc_word_alloc(mdoc, lastarg, p);
! 990: goto again;
! 991: /* NOTREACHED */
1.1 kristaps 992: }
993:
994:
1.8 ! kristaps 995: /*
! 996: * A delimited-constant macro is similar to a general text macro: the
! 997: * macro is followed by a 0 or 1 arguments (possibly-unspecified) then
! 998: * terminating punctuation, other words, or another callable macro.
! 999: */
! 1000: int
! 1001: macro_constant_delimited(MACRO_PROT_ARGS)
1.1 kristaps 1002: {
1.8 ! kristaps 1003: int lastarg, flushed, c, maxargs;
! 1004: char *p;
! 1005:
! 1006: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 1007: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
! 1008:
! 1009: /* Process line parameters. */
1.2 kristaps 1010:
1.8 ! kristaps 1011: lastarg = ppos;
! 1012: flushed = 0;
1.2 kristaps 1013:
1.8 ! kristaps 1014: /* Token pre-processing. */
! 1015:
! 1016: switch (tok) {
! 1017: case (MDOC_Ux):
! 1018: maxargs = 0;
! 1019: break;
! 1020: default:
! 1021: maxargs = 1;
! 1022: break;
! 1023: }
1.2 kristaps 1024:
1.8 ! kristaps 1025: again:
! 1026: lastarg = *pos;
1.5 kristaps 1027:
1.8 ! kristaps 1028: switch (mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p)) {
! 1029: case (ARGS_ERROR):
1.2 kristaps 1030: return(0);
1.8 ! kristaps 1031: case (ARGS_WORD):
! 1032: break;
! 1033: case (ARGS_PUNCT):
! 1034: if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p))
! 1035: return(0);
! 1036: if (ppos > 1)
! 1037: return(1);
! 1038: return(append_delims(mdoc, tok, pos, buf));
! 1039: case (ARGS_EOLN):
! 1040: if (flushed)
! 1041: return(1);
! 1042: return(append_const(mdoc, tok, ppos, 0, &p));
! 1043: default:
! 1044: abort();
! 1045: /* NOTREACHED */
! 1046: }
! 1047:
! 1048: /* Accepts no arguments: flush out symbol and continue. */
1.5 kristaps 1049:
1.8 ! kristaps 1050: if (0 == maxargs) {
! 1051: if ( ! append_const(mdoc, tok, ppos, 0, &p))
1.5 kristaps 1052: return(0);
1.8 ! kristaps 1053: flushed = 1;
! 1054: }
! 1055:
! 1056: if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
! 1057: if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p))
1.5 kristaps 1058: return(0);
1.8 ! kristaps 1059: if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
1.5 kristaps 1060: return(0);
1.8 ! kristaps 1061: if (ppos > 1)
! 1062: return(1);
! 1063: return(append_delims(mdoc, tok, pos, buf));
! 1064: }
! 1065:
! 1066: /*
! 1067: * We only accept one argument; subsequent tokens are considered
! 1068: * as literal words (until a macro).
! 1069: */
! 1070:
! 1071: if ( ! flushed && ! mdoc_isdelim(p)) {
! 1072: if ( ! append_const(mdoc, tok, ppos, 1, &p))
1.5 kristaps 1073: return(0);
1.8 ! kristaps 1074: flushed = 1;
! 1075: goto again;
! 1076: } else if ( ! flushed) {
! 1077: if ( ! append_const(mdoc, tok, ppos, 0, &p))
1.5 kristaps 1078: return(0);
1.8 ! kristaps 1079: flushed = 1;
1.5 kristaps 1080: }
1081:
1.8 ! kristaps 1082: mdoc_word_alloc(mdoc, lastarg, p);
! 1083: goto again;
! 1084: /* NOTREACHED */
1.1 kristaps 1085: }
1086:
1087:
1.8 ! kristaps 1088: int
! 1089: macro_constant(MACRO_PROT_ARGS)
1.1 kristaps 1090: {
1.8 ! kristaps 1091: int lastarg, j;
! 1092: char *args[MDOC_LINEARG_MAX];
1.2 kristaps 1093:
1.8 ! kristaps 1094: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 1095: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
1.2 kristaps 1096:
1.8 ! kristaps 1097: j = 0;
! 1098: lastarg = ppos;
1.2 kristaps 1099:
1.8 ! kristaps 1100: again:
! 1101: if (j == MDOC_LINEARG_MAX)
! 1102: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
1.1 kristaps 1103:
1.8 ! kristaps 1104: lastarg = *pos;
1.1 kristaps 1105:
1.8 ! kristaps 1106: switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[j])) {
! 1107: case (ARGS_ERROR):
! 1108: return(0);
! 1109: case (ARGS_WORD):
1.1 kristaps 1110: break;
1.8 ! kristaps 1111: case (ARGS_EOLN):
! 1112: return(append_const(mdoc, tok, ppos, j, args));
1.1 kristaps 1113: default:
1114: abort();
1.8 ! kristaps 1115: /* NOTREACHED */
1.1 kristaps 1116: }
1117:
1.8 ! kristaps 1118: if (MDOC_MAX != mdoc_find(mdoc, args[j]))
! 1119: if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
! 1120: return(0);
! 1121:
! 1122: j++;
! 1123: goto again;
! 1124: /* NOTREACHED */
1.1 kristaps 1125: }
1126:
1127:
1.8 ! kristaps 1128: int
! 1129: macro_constant_argv(MACRO_PROT_ARGS)
1.1 kristaps 1130: {
1.8 ! kristaps 1131: int c, lastarg, j;
! 1132: struct mdoc_arg argv[MDOC_LINEARG_MAX];
! 1133:
! 1134: if (SEC_PROLOGUE == mdoc->sec_lastn)
! 1135: return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
1.1 kristaps 1136:
1.8 ! kristaps 1137: lastarg = *pos;
! 1138:
! 1139: for (j = 0; j < MDOC_LINEARG_MAX; j++) {
! 1140: lastarg = *pos;
! 1141: c = mdoc_argv(mdoc, tok, &argv[j], pos, buf);
! 1142: if (0 == c)
! 1143: break;
! 1144: else if (1 == c)
! 1145: continue;
! 1146:
! 1147: mdoc_argv_free(j, argv);
! 1148: return(0);
! 1149: }
! 1150:
! 1151: if (MDOC_LINEARG_MAX == j) {
! 1152: mdoc_argv_free(j, argv);
! 1153: return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
! 1154: }
! 1155:
! 1156: c = append_constarg(mdoc, tok, ppos, j, argv);
! 1157: mdoc_argv_free(j, argv);
! 1158: return(c);
1.1 kristaps 1159: }
CVSweb