Beispiel #1
0
static int
blk_full(MACRO_PROT_ARGS)
{
	int		  la, nl, nparsed;
	struct mdoc_arg	 *arg;
	struct mdoc_node *head; /* save of head macro */
	struct mdoc_node *body; /* save of body macro */
	struct mdoc_node *n;
	enum mdoc_type	  mtt;
	enum mdoct	  ntok;
	enum margserr	  ac, lac;
	enum margverr	  av;
	char		 *p;

	nl = MDOC_NEWLINE & m->flags;

	/* Close out prior implicit scope. */

	if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
		if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
			return(0);
		if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
			return(0);
	}

	/*
	 * This routine accommodates implicitly- and explicitly-scoped
	 * macro openings.  Implicit ones first close out prior scope
	 * (seen above).  Delay opening the head until necessary to
	 * allow leading punctuation to print.  Special consideration
	 * for `It -column', which has phrase-part syntax instead of
	 * regular child nodes.
	 */

	for (arg = NULL;; ) {
		la = *pos;
		av = mdoc_argv(m, line, tok, &arg, pos, buf);

		if (ARGV_WORD == av) {
			*pos = la;
			break;
		} 

		if (ARGV_EOLN == av)
			break;
		if (ARGV_ARG == av)
			continue;

		mdoc_argv_free(arg);
		return(0);
	}

	if ( ! mdoc_block_alloc(m, line, ppos, tok, arg))
		return(0);

	head = body = NULL;

	/*
	 * Exception: Heads of `It' macros in `-diag' lists are not
	 * parsed, even though `It' macros in general are parsed.
	 */
	nparsed = MDOC_It == tok &&
		MDOC_Bl == m->last->parent->tok &&
		LIST_diag == m->last->parent->norm->Bl.type;

	/*
	 * The `Nd' macro has all arguments in its body: it's a hybrid
	 * of block partial-explicit and full-implicit.  Stupid.
	 */

	if (MDOC_Nd == tok) {
		if ( ! mdoc_head_alloc(m, line, ppos, tok))
			return(0);
		head = m->last;
		if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
			return(0);
		if ( ! mdoc_body_alloc(m, line, ppos, tok))
			return(0);
		body = m->last;
	} 

	ac = ARGS_ERROR;

	for ( ; ; ) {
		la = *pos;
		/* Initialise last-phrase-type with ARGS_PEND. */
		lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
		ac = mdoc_args(m, line, pos, buf, tok, &p);

		if (ARGS_PUNCT == ac)
			break;

		if (ARGS_ERROR == ac)
			return(0);

		if (ARGS_EOLN == ac) {
			if (ARGS_PPHRASE != lac && ARGS_PHRASE != lac)
				break;
			/*
			 * This is necessary: if the last token on a
			 * line is a `Ta' or tab, then we'll get
			 * ARGS_EOLN, so we must be smart enough to
			 * reopen our scope if the last parse was a
			 * phrase or partial phrase.
			 */
			if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
				return(0);
			if ( ! mdoc_body_alloc(m, line, ppos, tok))
				return(0);
			body = m->last;
			break;
		}

		/* 
		 * Emit leading punctuation (i.e., punctuation before
		 * the MDOC_HEAD) for non-phrase types.
		 */

		if (NULL == head && 
				ARGS_PEND != ac &&
				ARGS_PHRASE != ac &&
				ARGS_PPHRASE != ac &&
				ARGS_QWORD != ac &&
				DELIM_OPEN == mdoc_isdelim(p)) {
			if ( ! dword(m, line, la, p, DELIM_OPEN))
				return(0);
			continue;
		}

		/* Open a head if one hasn't been opened. */

		if (NULL == head) {
			if ( ! mdoc_head_alloc(m, line, ppos, tok))
				return(0);
			head = m->last;
		}

		if (ARGS_PHRASE == ac || 
				ARGS_PEND == ac ||
				ARGS_PPHRASE == ac) {
			/*
			 * If we haven't opened a body yet, rewind the
			 * head; if we have, rewind that instead.
			 */

			mtt = body ? MDOC_BODY : MDOC_HEAD;
			if ( ! rew_sub(mtt, m, tok, line, ppos))
				return(0);
			
			/* Then allocate our body context. */

			if ( ! mdoc_body_alloc(m, line, ppos, tok))
				return(0);
			body = m->last;

			/*
			 * Process phrases: set whether we're in a
			 * partial-phrase (this effects line handling)
			 * then call down into the phrase parser.
			 */

			if (ARGS_PPHRASE == ac)
				m->flags |= MDOC_PPHRASE;
			if (ARGS_PEND == ac && ARGS_PPHRASE == lac)
				m->flags |= MDOC_PPHRASE;

			if ( ! phrase(m, line, la, buf))
				return(0);

			m->flags &= ~MDOC_PPHRASE;
			continue;
		}

		ntok = nparsed || ARGS_QWORD == ac ? 
			MDOC_MAX : lookup(tok, p);

		if (MDOC_MAX == ntok) {
			if ( ! dword(m, line, la, p, DELIM_MAX))
				return(0);
			continue;
		}

		if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
			return(0);
		break;
	}

	if (NULL == head) {
		if ( ! mdoc_head_alloc(m, line, ppos, tok))
			return(0);
		head = m->last;
	}
	
	if (nl && ! append_delims(m, line, pos, buf))
		return(0);

	/* If we've already opened our body, exit now. */

	if (NULL != body)
		goto out;

	/*
	 * If there is an open (i.e., unvalidated) sub-block requiring
	 * explicit close-out, postpone switching the current block from
	 * head to body until the rew_sub() call closing out that
	 * sub-block.
	 */
	for (n = m->last; n && n != head; n = n->parent) {
		if (MDOC_BLOCK == n->type && 
				MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
				! (MDOC_VALID & n->flags)) {
			n->pending = head;
			return(1);
		}
	}

	/* Close out scopes to remain in a consistent state. */

	if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
		return(0);
	if ( ! mdoc_body_alloc(m, line, ppos, tok))
		return(0);

out:
	if ( ! (MDOC_FREECOL & m->flags))
		return(1);

	if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
		return(0);
	if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
		return(0);

	m->flags &= ~MDOC_FREECOL;
	return(1);
}
Beispiel #2
0
static int
in_line(MACRO_PROT_ARGS)
{
	int		 la, scope, cnt, nc, nl;
	enum margverr	 av;
	enum mdoct	 ntok;
	enum margserr	 ac;
	enum mdelim	 d;
	struct mdoc_arg	*arg;
	char		*p;

	nl = MDOC_NEWLINE & m->flags;

	/*
	 * Whether we allow ignored elements (those without content,
	 * usually because of reserved words) to squeak by.
	 */

	switch (tok) {
	case (MDOC_An):
		/* FALLTHROUGH */
	case (MDOC_Ar):
		/* FALLTHROUGH */
	case (MDOC_Fl):
		/* FALLTHROUGH */
	case (MDOC_Mt):
		/* FALLTHROUGH */
	case (MDOC_Nm):
		/* FALLTHROUGH */
	case (MDOC_Pa):
		nc = 1;
		break;
	default:
		nc = 0;
		break;
	}

	for (arg = NULL;; ) {
		la = *pos;
		av = mdoc_argv(m, line, tok, &arg, pos, buf);

		if (ARGV_WORD == av) {
			*pos = la;
			break;
		} 
		if (ARGV_EOLN == av)
			break;
		if (ARGV_ARG == av)
			continue;

		mdoc_argv_free(arg);
		return(0);
	}

	for (cnt = scope = 0;; ) {
		la = *pos;
		ac = mdoc_args(m, line, pos, buf, tok, &p);

		if (ARGS_ERROR == ac)
			return(0);
		if (ARGS_EOLN == ac)
			break;
		if (ARGS_PUNCT == ac)
			break;

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);

		/* 
		 * In this case, we've located a submacro and must
		 * execute it.  Close out scope, if open.  If no
		 * elements have been generated, either create one (nc)
		 * or raise a warning.
		 */

		if (MDOC_MAX != ntok) {
			if (scope && ! rew_elem(m, tok))
				return(0);
			if (nc && 0 == cnt) {
				if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
					return(0);
				if ( ! rew_last(m, m->last))
					return(0);
			} else if ( ! nc && 0 == cnt) {
				mdoc_argv_free(arg);
				mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY);
			}

			if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
				return(0);
			if ( ! nl)
				return(1);
			return(append_delims(m, line, pos, buf));
		} 

		/* 
		 * Non-quote-enclosed punctuation.  Set up our scope, if
		 * a word; rewind the scope, if a delimiter; then append
		 * the word. 
		 */

		d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p);

		if (DELIM_NONE != d) {
			/*
			 * If we encounter closing punctuation, no word
			 * has been omitted, no scope is open, and we're
			 * allowed to have an empty element, then start
			 * a new scope.  `Ar', `Fl', and `Li', only do
			 * this once per invocation.  There may be more
			 * of these (all of them?).
			 */
			if (0 == cnt && (nc || MDOC_Li == tok) && 
					DELIM_CLOSE == d && ! scope) {
				if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
					return(0);
				if (MDOC_Ar == tok || MDOC_Li == tok || 
						MDOC_Fl == tok)
					cnt++;
				scope = 1;
			}
			/*
			 * Close out our scope, if one is open, before
			 * any punctuation.
			 */
			if (scope && ! rew_elem(m, tok))
				return(0);
			scope = 0;
		} else if ( ! scope) {
			if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
				return(0);
			scope = 1;
		}

		if (DELIM_NONE == d)
			cnt++;

		if ( ! dword(m, line, la, p, d))
			return(0);

		/*
		 * `Fl' macros have their scope re-opened with each new
		 * word so that the `-' can be added to each one without
		 * having to parse out spaces.
		 */
		if (scope && MDOC_Fl == tok) {
			if ( ! rew_elem(m, tok))
				return(0);
			scope = 0;
		}
	}

	if (scope && ! rew_elem(m, tok))
		return(0);

	/*
	 * If no elements have been collected and we're allowed to have
	 * empties (nc), open a scope and close it out.  Otherwise,
	 * raise a warning.
	 */

	if (nc && 0 == cnt) {
		if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
			return(0);
		if ( ! rew_last(m, m->last))
			return(0);
	} else if ( ! nc && 0 == cnt) {
		mdoc_argv_free(arg);
		mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY);
	}

	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}
Beispiel #3
0
static int
in_line_eoln(MACRO_PROT_ARGS)
{
	int		 la;
	enum margserr	 ac;
	enum margverr	 av;
	struct mdoc_arg	*arg;
	char		*p;
	enum mdoct	 ntok;

	assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));

	if (tok == MDOC_Pp)
		rew_sub(MDOC_BLOCK, m, MDOC_Nm, line, ppos);

	/* Parse macro arguments. */

	for (arg = NULL; ; ) {
		la = *pos;
		av = mdoc_argv(m, line, tok, &arg, pos, buf);

		if (ARGV_WORD == av) {
			*pos = la;
			break;
		}
		if (ARGV_EOLN == av) 
			break;
		if (ARGV_ARG == av)
			continue;

		mdoc_argv_free(arg);
		return(0);
	}

	/* Open element scope. */

	if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
		return(0);

	/* Parse argument terms. */

	for (;;) {
		la = *pos;
		ac = mdoc_args(m, line, pos, buf, tok, &p);

		if (ARGS_ERROR == ac)
			return(0);
		if (ARGS_EOLN == ac)
			break;

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);

		if (MDOC_MAX == ntok) {
			if ( ! dword(m, line, la, p, DELIM_MAX))
				return(0);
			continue;
		}

		if ( ! rew_elem(m, tok))
			return(0);
		return(mdoc_macro(m, ntok, line, la, pos, buf));
	}

	/* Close out (no delimiters). */

	return(rew_elem(m, tok));
}
Beispiel #4
0
/* ARGSUSED */
static int
in_line_argn(MACRO_PROT_ARGS)
{
	int		 la, flushed, j, maxargs, nl;
	enum margserr	 ac;
	enum margverr	 av;
	struct mdoc_arg	*arg;
	char		*p;
	enum mdoct	 ntok;

	nl = MDOC_NEWLINE & m->flags;

	/*
	 * A line macro that has a fixed number of arguments (maxargs).
	 * Only open the scope once the first non-leading-punctuation is
	 * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
	 * keep it open until the maximum number of arguments are
	 * exhausted.
	 */

	switch (tok) {
	case (MDOC_Ap):
		/* FALLTHROUGH */
	case (MDOC_No):
		/* FALLTHROUGH */
	case (MDOC_Ns):
		/* FALLTHROUGH */
	case (MDOC_Ux):
		maxargs = 0;
		break;
	case (MDOC_Bx):
		/* FALLTHROUGH */
	case (MDOC_Xr):
		maxargs = 2;
		break;
	default:
		maxargs = 1;
		break;
	}

	for (arg = NULL; ; ) {
		la = *pos;
		av = mdoc_argv(m, line, tok, &arg, pos, buf);

		if (ARGV_WORD == av) {
			*pos = la;
			break;
		} 

		if (ARGV_EOLN == av)
			break;
		if (ARGV_ARG == av)
			continue;

		mdoc_argv_free(arg);
		return(0);
	}

	for (flushed = j = 0; ; ) {
		la = *pos;
		ac = mdoc_args(m, line, pos, buf, tok, &p);

		if (ARGS_ERROR == ac)
			return(0);
		if (ARGS_PUNCT == ac)
			break;
		if (ARGS_EOLN == ac)
			break;

		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && 
				ARGS_QWORD != ac && 0 == j && 
				DELIM_OPEN == mdoc_isdelim(p)) {
			if ( ! dword(m, line, la, p, DELIM_OPEN))
				return(0);
			continue;
		} else if (0 == j)
		       if ( ! mdoc_elem_alloc(m, line, la, tok, arg))
			       return(0);

		if (j == maxargs && ! flushed) {
			if ( ! rew_elem(m, tok))
				return(0);
			flushed = 1;
		}

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);

		if (MDOC_MAX != ntok) {
			if ( ! flushed && ! rew_elem(m, tok))
				return(0);
			flushed = 1;
			if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
				return(0);
			j++;
			break;
		}

		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
				ARGS_QWORD != ac &&
				! flushed &&
				DELIM_NONE != mdoc_isdelim(p)) {
			if ( ! rew_elem(m, tok))
				return(0);
			flushed = 1;
		}

		if ( ! dword(m, line, la, p, DELIM_MAX))
			return(0);
		j++;
	}

	if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg))
	       return(0);

	/* Close out in a consistent state. */

	if ( ! flushed && ! rew_elem(m, tok))
		return(0);
	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}
static void
in_line(MACRO_PROT_ARGS)
{
	int		 la, scope, cnt, firstarg, mayopen, nc, nl;
	enum mdoct	 ntok;
	enum margserr	 ac;
	enum mdelim	 d;
	struct mdoc_arg	*arg;
	char		*p;

	nl = MDOC_NEWLINE & mdoc->flags;

	/*
	 * Whether we allow ignored elements (those without content,
	 * usually because of reserved words) to squeak by.
	 */

	switch (tok) {
	case MDOC_An:
		/* FALLTHROUGH */
	case MDOC_Ar:
		/* FALLTHROUGH */
	case MDOC_Fl:
		/* FALLTHROUGH */
	case MDOC_Mt:
		/* FALLTHROUGH */
	case MDOC_Nm:
		/* FALLTHROUGH */
	case MDOC_Pa:
		nc = 1;
		break;
	default:
		nc = 0;
		break;
	}

	mdoc_argv(mdoc, line, tok, &arg, pos, buf);

	d = DELIM_NONE;
	firstarg = 1;
	mayopen = 1;
	for (cnt = scope = 0;; ) {
		la = *pos;
		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);

		/*
		 * At the end of a macro line,
		 * opening delimiters do not suppress spacing.
		 */

		if (ac == ARGS_EOLN) {
			if (d == DELIM_OPEN)
				mdoc->last->flags &= ~MDOC_DELIMO;
			break;
		}

		/*
		 * The rest of the macro line is only punctuation,
		 * to be handled by append_delims().
		 * If there were no other arguments,
		 * do not allow the first one to suppress spacing,
		 * even if it turns out to be a closing one.
		 */

		if (ac == ARGS_PUNCT) {
			if (cnt == 0 && (nc == 0 || tok == MDOC_An))
				mdoc->flags |= MDOC_NODELIMC;
			break;
		}

		ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ?
		    MDOC_MAX : lookup(mdoc, tok, line, la, p);

		/*
		 * In this case, we've located a submacro and must
		 * execute it.  Close out scope, if open.  If no
		 * elements have been generated, either create one (nc)
		 * or raise a warning.
		 */

		if (ntok != MDOC_MAX) {
			if (scope)
				rew_elem(mdoc, tok);
			if (nc && ! cnt) {
				mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
				rew_last(mdoc, mdoc->last);
			} else if ( ! nc && ! cnt) {
				mdoc_argv_free(arg);
				mandoc_msg(MANDOCERR_MACRO_EMPTY,
				    mdoc->parse, line, ppos,
				    mdoc_macronames[tok]);
			}
			mdoc_macro(mdoc, ntok, line, la, pos, buf);
			if (nl)
				append_delims(mdoc, line, pos, buf);
			return;
		}

		/*
		 * Non-quote-enclosed punctuation.  Set up our scope, if
		 * a word; rewind the scope, if a delimiter; then append
		 * the word.
		 */

		d = ac == ARGS_QWORD ? DELIM_NONE : mdoc_isdelim(p);

		if (DELIM_NONE != d) {
			/*
			 * If we encounter closing punctuation, no word
			 * has been emitted, no scope is open, and we're
			 * allowed to have an empty element, then start
			 * a new scope.
			 */
			if ((d == DELIM_CLOSE ||
			     (d == DELIM_MIDDLE && tok == MDOC_Fl)) &&
			    !cnt && !scope && nc && mayopen) {
				mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
				scope = 1;
				cnt++;
				if (tok == MDOC_Nm)
					mayopen = 0;
			}
			/*
			 * Close out our scope, if one is open, before
			 * any punctuation.
			 */
			if (scope)
				rew_elem(mdoc, tok);
			scope = 0;
			if (tok == MDOC_Fn)
				mayopen = 0;
		} else if (mayopen && !scope) {
			mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
			scope = 1;
			cnt++;
		}

		dword(mdoc, line, la, p, d,
		    MDOC_JOIN & mdoc_macros[tok].flags);

		/*
		 * If the first argument is a closing delimiter,
		 * do not suppress spacing before it.
		 */

		if (firstarg && d == DELIM_CLOSE && !nc)
			mdoc->last->flags &= ~MDOC_DELIMC;
		firstarg = 0;

		/*
		 * `Fl' macros have their scope re-opened with each new
		 * word so that the `-' can be added to each one without
		 * having to parse out spaces.
		 */
		if (scope && tok == MDOC_Fl) {
			rew_elem(mdoc, tok);
			scope = 0;
		}
	}

	if (scope)
		rew_elem(mdoc, tok);

	/*
	 * If no elements have been collected and we're allowed to have
	 * empties (nc), open a scope and close it out.  Otherwise,
	 * raise a warning.
	 */

	if ( ! cnt) {
		if (nc) {
			mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
			rew_last(mdoc, mdoc->last);
		} else {
			mdoc_argv_free(arg);
			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
			    line, ppos, mdoc_macronames[tok]);
		}
	}
	if (nl)
		append_delims(mdoc, line, pos, buf);
}
Beispiel #6
0
/* ARGSUSED */
static int
in_line_argn(MACRO_PROT_ARGS)
{
	int		 la, flushed, j, maxargs, nl;
	enum margserr	 ac;
	enum margverr	 av;
	struct mdoc_arg	*arg;
	char		*p;
	enum mdoct	 ntok;

	nl = MDOC_NEWLINE & m->flags;

	/*
	 * A line macro that has a fixed number of arguments (maxargs).
	 * Only open the scope once the first non-leading-punctuation is
	 * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
	 * keep it open until the maximum number of arguments are
	 * exhausted.
	 */

	switch (tok) {
	case (MDOC_Ap):
		/* FALLTHROUGH */
	case (MDOC_No):
		/* FALLTHROUGH */
	case (MDOC_Ns):
		/* FALLTHROUGH */
	case (MDOC_Ux):
		maxargs = 0;
		break;
	case (MDOC_Xr):
		maxargs = 2;
		break;
	default:
		maxargs = 1;
		break;
	}

	for (arg = NULL; ; ) {
		la = *pos;
		av = mdoc_argv(m, line, tok, &arg, pos, buf);

		if (ARGV_WORD == av) {
			*pos = la;
			break;
		} 

		if (ARGV_EOLN == av)
			break;
		if (ARGV_ARG == av)
			continue;

		mdoc_argv_free(arg);
		return(0);
	}

	for (flushed = j = 0; ; ) {
		la = *pos;
		ac = mdoc_args(m, line, pos, buf, tok, &p);

		if (ARGS_ERROR == ac)
			return(0);
		if (ARGS_PUNCT == ac)
			break;
		if (ARGS_EOLN == ac)
			break;

		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && 
				ARGS_QWORD != ac &&
				0 == j && DELIM_OPEN == mdoc_isdelim(p)) {
			if ( ! mdoc_word_alloc(m, line, la, p))
				return(0);
			continue;
		} else if (0 == j)
		       if ( ! mdoc_elem_alloc(m, line, la, tok, arg))
			       return(0);

		if (j == maxargs && ! flushed) {
			if ( ! rew_elem(m, tok))
				return(0);
			flushed = 1;
		}

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);

		if (MDOC_MAX != ntok) {
			if ( ! flushed && ! rew_elem(m, tok))
				return(0);
			flushed = 1;
			if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
				return(0);
			j++;
			break;
		}

		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
				ARGS_QWORD != ac &&
				! flushed &&
				DELIM_NONE != mdoc_isdelim(p)) {
			if ( ! rew_elem(m, tok))
				return(0);
			flushed = 1;
		}

		/* 
		 * XXX: this is a hack to work around groff's ugliness
		 * as regards `Xr' and extraneous arguments.  It should
		 * ideally be deprecated behaviour, but because this is
		 * code is no here, it's unlikely to be removed.
		 */

#ifdef __OpenBSD__
		if (MDOC_Xr == tok && j == maxargs) {
			if ( ! mdoc_elem_alloc(m, line, la, MDOC_Ns, NULL))
				return(0);
			if ( ! rew_elem(m, MDOC_Ns))
				return(0);
		}
#endif

		if ( ! mdoc_word_alloc(m, line, la, p))
			return(0);
		j++;
	}

	if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg))
	       return(0);

	/* Close out in a consistent state. */

	if ( ! flushed && ! rew_elem(m, tok))
		return(0);
	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}