예제 #1
0
static void
blk_part_exp(MACRO_PROT_ARGS)
{
	int		  la, nl;
	enum margserr	  ac;
	struct mdoc_node *head; /* keep track of head */
	char		 *p;

	nl = MDOC_NEWLINE & mdoc->flags;

	/*
	 * The opening of an explicit macro having zero or more leading
	 * punctuation nodes; a head with optional single element (the
	 * case of `Eo'); and a body that may be empty.
	 */

	mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
	head = NULL;
	for (;;) {
		la = *pos;
		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
		if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
			break;

		/* Flush out leading punctuation. */

		if (head == NULL && ac != ARGS_QWORD &&
		    mdoc_isdelim(p) == DELIM_OPEN) {
			dword(mdoc, line, la, p, DELIM_OPEN, 0);
			continue;
		}

		if (head == NULL) {
			head = mdoc_head_alloc(mdoc, line, ppos, tok);
			if (tok == MDOC_Eo)  /* Not parsed. */
				dword(mdoc, line, la, p, DELIM_MAX, 0);
			rew_last(mdoc, head);
			mdoc_body_alloc(mdoc, line, ppos, tok);
			if (tok == MDOC_Eo)
				continue;
		}

		if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
			break;
	}

	/* Clean-up to leave in a consistent state. */

	if (head == NULL) {
		rew_last(mdoc, mdoc_head_alloc(mdoc, line, ppos, tok));
		mdoc_body_alloc(mdoc, line, ppos, tok);
	}
	if (nl)
		append_delims(mdoc, line, pos, buf);
}
예제 #2
0
/*
 * Rewind up to a specific block, including all blocks that broke it.
 */
static void
rew_pending(struct mdoc *mdoc, const struct mdoc_node *n)
{

	for (;;) {
		rew_last(mdoc, n);

		switch (n->type) {
		case MDOC_HEAD:
			mdoc_body_alloc(mdoc, n->line, n->pos, n->tok);
			return;
		case MDOC_BLOCK:
			break;
		default:
			return;
		}

		if ( ! (n->flags & MDOC_BROKEN))
			return;

		for (;;) {
			if ((n = n->parent) == NULL)
				return;

			if (n->type == MDOC_BLOCK ||
			    n->type == MDOC_HEAD) {
				if (n->flags & MDOC_ENDED)
					break;
				else
					return;
			}
		}
	}
}
예제 #3
0
/*
 * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
 * They're unusual because they're basically free-form text until a
 * macro is encountered.
 */
static void
phrase_ta(MACRO_PROT_ARGS)
{
	struct mdoc_node *body, *n;

	/* Make sure we are in a column list or ignore this macro. */

	body = NULL;
	for (n = mdoc->last; n != NULL; n = n->parent) {
		if (n->flags & MDOC_ENDED)
			continue;
		if (n->tok == MDOC_It && n->type == MDOC_BODY)
			body = n;
		if (n->tok == MDOC_Bl)
			break;
	}

	if (n == NULL || n->norm->Bl.type != LIST_column) {
		mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
		    line, ppos, "Ta");
		return;
	}

	/* Advance to the next column. */

	rew_last(mdoc, body);
	mdoc_body_alloc(mdoc, line, ppos, MDOC_It);
	parse_rest(mdoc, MDOC_MAX, line, pos, buf);
}
static int
rew_sub(enum mdoc_type t, struct mdoc *mdoc, 
		enum mdoct tok, int line, int ppos)
{
	struct mdoc_node *n;

	n = mdoc->last;
	while (n) {
		switch (rew_dohalt(tok, t, n)) {
		case (REWIND_NONE):
			return(1);
		case (REWIND_THIS):
			n->lastline = line -
			    (MDOC_NEWLINE & mdoc->flags &&
			     ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
			break;
		case (REWIND_FORCE):
			mandoc_vmsg(MANDOCERR_SCOPEBROKEN, mdoc->parse, 
					line, ppos, "%s breaks %s", 
					mdoc_macronames[tok],
					mdoc_macronames[n->tok]);
			/* FALLTHROUGH */
		case (REWIND_MORE):
			n->lastline = line -
			    (MDOC_NEWLINE & mdoc->flags ? 1 : 0);
			n = n->parent;
			continue;
		case (REWIND_LATER):
			if (make_pending(n, tok, mdoc, line, ppos) ||
			    MDOC_BLOCK != t)
				return(1);
			/* FALLTHROUGH */
		case (REWIND_ERROR):
			mdoc_pmsg(mdoc, line, ppos, MANDOCERR_NOSCOPE);
			return(1);
		}
		break;
	}

	assert(n);
	if ( ! rew_last(mdoc, n))
		return(0);

	/*
	 * The current block extends an enclosing block.
	 * Now that the current block ends, close the enclosing block, too.
	 */
	while (NULL != (n = n->pending)) {
		if ( ! rew_last(mdoc, n))
			return(0);
		if (MDOC_HEAD == n->type &&
		    ! mdoc_body_alloc(mdoc, n->line, n->pos, n->tok))
			return(0);
	}

	return(1);
}
/* ARGSUSED */
static int
phrase_ta(MACRO_PROT_ARGS)
{
	struct mdoc_node *n;
	int		  la;
	enum mdoct	  ntok;
	enum margserr	  ac;
	char		 *p;

	/* Make sure we are in a column list or ignore this macro. */
	n = mdoc->last;
	while (NULL != n && MDOC_Bl != n->tok)
		n = n->parent;
	if (NULL == n || LIST_column != n->norm->Bl.type) {
		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_STRAYTA);
		return(1);
	}

	/* Advance to the next column. */
	if ( ! rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos))
		return(0);
	if ( ! mdoc_body_alloc(mdoc, line, ppos, MDOC_It))
		return(0);

	for (;;) {
		la = *pos;
		ac = mdoc_zargs(mdoc, line, pos, buf, &p);

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

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);

		if (MDOC_MAX == ntok) {
			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
			    MDOC_JOIN & mdoc_macros[tok].flags))
				return(0);
			continue;
		}

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

	return(1);
}
예제 #6
0
static int
rew_sub(enum mdoc_type t, struct mdoc *m, 
		enum mdoct tok, int line, int ppos)
{
	struct mdoc_node *n;
	enum rew	  c;

	/* LINTED */
	for (n = m->last; n; n = n->parent) {
		c = rew_dohalt(tok, t, n);
		if (REWIND_HALT == c) {
			if (MDOC_BLOCK != t)
				return(1);
			if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags))
				return(1);
			/* FIXME: shouldn't raise an error */
			mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE);
			return(0);
		}
		if (REWIND_REWIND == c)
			break;
		else if (rew_dobreak(tok, n))
			continue;
		if ( ! swarn(m, t, line, ppos, n))
			return(0);
	}

	assert(n);
	if ( ! rew_last(m, n))
		return(0);

#ifdef	UGLY
	/*
	 * The current block extends an enclosing block beyond a line
	 * break.  Now that the current block ends, close the enclosing
	 * block, too.
	 */
	if (NULL != (n = n->pending)) {
		assert(MDOC_HEAD == n->type);
		if ( ! rew_last(m, n))
			return(0);
		if ( ! mdoc_body_alloc(m, n->line, n->pos, n->tok))
			return(0);
	}
#endif

	return(1);
}
예제 #7
0
파일: mdoc_macro.c 프로젝트: UNGLinux/Obase
/* ARGSUSED */
static int
phrase_ta(MACRO_PROT_ARGS)
{
	int		  la;
	enum mdoct	  ntok;
	enum margserr	  ac;
	char		 *p;

	/*
	 * FIXME: this is overly restrictive: if the `Ta' is unexpected,
	 * it should simply error out with ARGSLOST.
	 */

	if ( ! rew_sub(MDOC_BODY, m, MDOC_It, line, ppos))
		return(0);
	if ( ! mdoc_body_alloc(m, line, ppos, MDOC_It))
		return(0);

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

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

		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(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);
		return(append_delims(m, line, pos, buf));
	}

	return(1);
}
예제 #8
0
파일: mdoc_macro.c 프로젝트: UNGLinux/Obase
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);
}
예제 #9
0
파일: mdoc_macro.c 프로젝트: UNGLinux/Obase
static int
blk_part_exp(MACRO_PROT_ARGS)
{
	int		  la, nl;
	enum margserr	  ac;
	struct mdoc_node *head; /* keep track of head */
	struct mdoc_node *body; /* keep track of body */
	char		 *p;
	enum mdoct	  ntok;

	nl = MDOC_NEWLINE & m->flags;

	/*
	 * The opening of an explicit macro having zero or more leading
	 * punctuation nodes; a head with optional single element (the
	 * case of `Eo'); and a body that may be empty.
	 */

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

	for (head = body = NULL; ; ) {
		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;

		/* Flush out leading punctuation. */

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

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

		/*
		 * `Eo' gobbles any data into the head, but most other
		 * macros just immediately close out and begin the body.
		 */

		if (NULL == body) {
			assert(head);
			/* No check whether it's a macro! */
			if (MDOC_Eo == tok)
				if ( ! dword(m, line, la, p, DELIM_MAX))
					return(0);

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

			if (MDOC_Eo == tok)
				continue;
		}

		assert(NULL != head && NULL != body);

		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 ( ! mdoc_macro(m, ntok, line, la, pos, buf))
			return(0);
		break;
	}

	/* Clean-up to leave in a consistent state. */

	if (NULL == head)
		if ( ! mdoc_head_alloc(m, line, ppos, tok))
			return(0);

	if (NULL == body) {
		if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
			return(0);
		if ( ! mdoc_body_alloc(m, line, ppos, tok))
			return(0);
	}

	/* Standard appending of delimiters. */

	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}
예제 #10
0
파일: mdoc_macro.c 프로젝트: UNGLinux/Obase
static int
blk_part_imp(MACRO_PROT_ARGS)
{
	int		  la, nl;
	enum mdoct	  ntok;
	enum margserr	  ac;
	char		 *p;
	struct mdoc_node *blk; /* saved block context */
	struct mdoc_node *body; /* saved body context */
	struct mdoc_node *n;

	nl = MDOC_NEWLINE & m->flags;

	/*
	 * A macro that spans to the end of the line.  This is generally
	 * (but not necessarily) called as the first macro.  The block
	 * has a head as the immediate child, which is always empty,
	 * followed by zero or more opening punctuation nodes, then the
	 * body (which may be empty, depending on the macro), then zero
	 * or more closing punctuation nodes.
	 */

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

	blk = m->last;

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

	/*
	 * Open the body scope "on-demand", that is, after we've
	 * processed all our the leading delimiters (open parenthesis,
	 * etc.).
	 */

	for (body = NULL; ; ) {
		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;

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

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

		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 ( ! mdoc_macro(m, ntok, line, la, pos, buf))
			return(0);
		break;
	}

	/* Clean-ups to leave in a consistent state. */

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

	for (n = body->child; n && n->next; n = n->next)
		/* Do nothing. */ ;
	
	/* 
	 * End of sentence spacing: if the last node is a text node and
	 * has a trailing period, then mark it as being end-of-sentence.
	 */

	if (n && MDOC_TEXT == n->type && n->string)
		if (mandoc_eos(n->string, strlen(n->string), 1))
			n->flags |= MDOC_EOS;

	/* Up-propagate the end-of-space flag. */

	if (n && (MDOC_EOS & n->flags)) {
		body->flags |= MDOC_EOS;
		body->parent->flags |= MDOC_EOS;
	}

	/*
	 * If there is an open sub-block requiring explicit close-out,
	 * postpone closing out the current block
	 * until the rew_sub() call closing out the sub-block.
	 */
	for (n = m->last; n && n != body && n != blk->parent; n = n->parent) {
		if (MDOC_BLOCK == n->type &&
		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
		    ! (MDOC_VALID & n->flags)) {
			make_pending(n, tok, m, line, ppos);
			if ( ! mdoc_endbody_alloc(m, line, ppos,
			    tok, body, ENDBODY_NOSPACE))
				return(0);
			return(1);
		}
	}

	/* 
	 * If we can't rewind to our body, then our scope has already
	 * been closed by another macro (like `Oc' closing `Op').  This
	 * is ugly behaviour nodding its head to OpenBSD's overwhelming
	 * crufty use of `Op' breakage.
	 */
	if (n != body)
		mandoc_vmsg(MANDOCERR_SCOPENEST, m->parse, line, ppos, 
				"%s broken", mdoc_macronames[tok]);

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

	/* Standard appending of delimiters. */

	if (nl && ! append_delims(m, line, pos, buf))
		return(0);

	/* Rewind scope, if applicable. */

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

	return(1);
}
예제 #11
0
static void
blk_full(MACRO_PROT_ARGS)
{
	int		  la, nl, parsed;
	struct mdoc_arg	 *arg;
	struct mdoc_node *blk; /* Our own or a broken block. */
	struct mdoc_node *head; /* Our own head. */
	struct mdoc_node *body; /* Our own body. */
	struct mdoc_node *n;
	enum margserr	  ac, lac;
	char		 *p;

	nl = MDOC_NEWLINE & mdoc->flags;

	if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) {
		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
		    line, ppos, mdoc_macronames[tok]);
		return;
	}

	if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) {

		/* Here, tok is one of Sh Ss Nm Nd It. */

		blk = NULL;
		for (n = mdoc->last; n != NULL; n = n->parent) {
			if (n->flags & MDOC_ENDED) {
				if ( ! (n->flags & MDOC_VALID))
					n->flags |= MDOC_BROKEN;
				continue;
			}
			if (n->type != MDOC_BLOCK)
				continue;

			if (tok == MDOC_It && n->tok == MDOC_Bl) {
				if (blk != NULL) {
					mandoc_vmsg(MANDOCERR_BLK_BROKEN,
					    mdoc->parse, line, ppos,
					    "It breaks %s",
					    mdoc_macronames[blk->tok]);
					rew_pending(mdoc, blk);
				}
				break;
			}

			if (mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
				switch (tok) {
				case MDOC_Sh:
					/* FALLTHROUGH */
				case MDOC_Ss:
					mandoc_vmsg(MANDOCERR_BLK_BROKEN,
					    mdoc->parse, line, ppos,
					    "%s breaks %s",
					    mdoc_macronames[tok],
					    mdoc_macronames[n->tok]);
					rew_pending(mdoc, n);
					n = mdoc->last;
					continue;
				case MDOC_It:
					/* Delay in case it's astray. */
					blk = n;
					continue;
				default:
					break;
				}
				break;
			}

			/* Here, n is one of Sh Ss Nm Nd It. */

			if (tok != MDOC_Sh && (n->tok == MDOC_Sh ||
			    (tok != MDOC_Ss && (n->tok == MDOC_Ss ||
			     (tok != MDOC_It && n->tok == MDOC_It)))))
				break;

			/* Item breaking an explicit block. */

			if (blk != NULL) {
				mandoc_vmsg(MANDOCERR_BLK_BROKEN,
				    mdoc->parse, line, ppos,
				    "It breaks %s",
				    mdoc_macronames[blk->tok]);
				rew_pending(mdoc, blk);
				blk = NULL;
			}

			/* Close out prior implicit scopes. */

			rew_last(mdoc, n);
		}

		/* Skip items outside lists. */

		if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) {
			mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
			    line, ppos, "It %s", buf + *pos);
			mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL);
			rew_elem(mdoc, MDOC_br);
			return;
		}
	}

	/*
	 * 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.
	 */

	mdoc_argv(mdoc, line, tok, &arg, pos, buf);
	blk = mdoc_block_alloc(mdoc, line, ppos, tok, arg);
	head = body = NULL;

	/*
	 * Exception: Heads of `It' macros in `-diag' lists are not
	 * parsed, even though `It' macros in general are parsed.
	 */

	parsed = tok != MDOC_It ||
	    mdoc->last->parent->tok != MDOC_Bl ||
	    mdoc->last->parent->norm->Bl.type != LIST_diag;

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

	if (tok == MDOC_Nd) {
		head = mdoc_head_alloc(mdoc, line, ppos, tok);
		rew_last(mdoc, head);
		body = mdoc_body_alloc(mdoc, line, ppos, tok);
	}

	if (tok == MDOC_Bk)
		mdoc->flags |= MDOC_KEEP;

	ac = ARGS_PEND;
	for (;;) {
		la = *pos;
		lac = ac;
		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
		if (ac == ARGS_EOLN) {
			if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE)
				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 (body != NULL)
				rew_last(mdoc, body);
			body = mdoc_body_alloc(mdoc, line, ppos, tok);
			break;
		}
		if (tok == MDOC_Bd || tok == MDOC_Bk) {
			mandoc_vmsg(MANDOCERR_ARG_EXCESS,
			    mdoc->parse, line, la, "%s ... %s",
			    mdoc_macronames[tok], buf + la);
			break;
		}
		if (tok == MDOC_Rs) {
			mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
			    line, la, "Rs %s", buf + la);
			break;
		}
		if (ac == ARGS_PUNCT)
			break;

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

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

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

		if (head == NULL)
			head = mdoc_head_alloc(mdoc, line, ppos, tok);

		if (ac == ARGS_PHRASE ||
		    ac == ARGS_PEND ||
		    ac == ARGS_PPHRASE) {

			/*
			 * If we haven't opened a body yet, rewind the
			 * head; if we have, rewind that instead.
			 */

			rew_last(mdoc, body == NULL ? head : body);
			body = mdoc_body_alloc(mdoc, line, ppos, tok);

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

			if (ac == ARGS_PPHRASE)
				mdoc->flags |= MDOC_PPHRASE;
			if (ac == ARGS_PEND && lac == ARGS_PPHRASE)
				mdoc->flags |= MDOC_PPHRASE;
			parse_rest(mdoc, MDOC_MAX, line, &la, buf);
			mdoc->flags &= ~MDOC_PPHRASE;
			continue;
		}

		if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed))
			break;
	}

	if (blk->flags & MDOC_VALID)
		return;
	if (head == NULL)
		head = mdoc_head_alloc(mdoc, line, ppos, tok);
	if (nl && tok != MDOC_Bd && tok != MDOC_Bl && tok != MDOC_Rs)
		append_delims(mdoc, line, pos, buf);
	if (body != NULL)
		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_pending() call closing out that
	 * sub-block.
	 */
	for (n = mdoc->last; n && n != head; n = n->parent) {
		if (n->flags & MDOC_ENDED) {
			if ( ! (n->flags & MDOC_VALID))
				n->flags |= MDOC_BROKEN;
			continue;
		}
		if (n->type == MDOC_BLOCK &&
		    mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
			n->flags = MDOC_BROKEN;
			head->flags = MDOC_ENDED;
		}
	}
	if (head->flags & MDOC_ENDED)
		return;

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

	rew_last(mdoc, head);
	body = mdoc_body_alloc(mdoc, line, ppos, tok);
out:
	if (mdoc->flags & MDOC_FREECOL) {
		rew_last(mdoc, body);
		rew_last(mdoc, blk);
		mdoc->flags &= ~MDOC_FREECOL;
	}
}
예제 #12
0
static void
blk_part_imp(MACRO_PROT_ARGS)
{
	int		  la, nl;
	enum margserr	  ac;
	char		 *p;
	struct mdoc_node *blk; /* saved block context */
	struct mdoc_node *body; /* saved body context */
	struct mdoc_node *n;

	nl = MDOC_NEWLINE & mdoc->flags;

	/*
	 * A macro that spans to the end of the line.  This is generally
	 * (but not necessarily) called as the first macro.  The block
	 * has a head as the immediate child, which is always empty,
	 * followed by zero or more opening punctuation nodes, then the
	 * body (which may be empty, depending on the macro), then zero
	 * or more closing punctuation nodes.
	 */

	blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
	rew_last(mdoc, mdoc_head_alloc(mdoc, line, ppos, tok));

	/*
	 * Open the body scope "on-demand", that is, after we've
	 * processed all our the leading delimiters (open parenthesis,
	 * etc.).
	 */

	for (body = NULL; ; ) {
		la = *pos;
		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
		if (ac == ARGS_EOLN || ac == ARGS_PUNCT)
			break;

		if (body == NULL && ac != ARGS_QWORD &&
		    mdoc_isdelim(p) == DELIM_OPEN) {
			dword(mdoc, line, la, p, DELIM_OPEN, 0);
			continue;
		}

		if (body == NULL)
			body = mdoc_body_alloc(mdoc, line, ppos, tok);

		if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
			break;
	}
	if (body == NULL)
		body = mdoc_body_alloc(mdoc, line, ppos, tok);

	/*
	 * If there is an open sub-block requiring explicit close-out,
	 * postpone closing out the current block until the
	 * rew_pending() call closing out the sub-block.
	 */

	for (n = mdoc->last; n && n != body && n != blk->parent;
	     n = n->parent) {
		if (n->flags & MDOC_ENDED) {
			if ( ! (n->flags & MDOC_VALID))
				n->flags |= MDOC_BROKEN;
			continue;
		}
		if (n->type == MDOC_BLOCK &&
		    mdoc_macros[n->tok].flags & MDOC_EXPLICIT) {
			n->flags |= MDOC_BROKEN;
			if ( ! (body->flags & MDOC_ENDED)) {
				mandoc_vmsg(MANDOCERR_BLK_NEST,
				    mdoc->parse, line, ppos,
				    "%s breaks %s", mdoc_macronames[tok],
				    mdoc_macronames[n->tok]);
				mdoc_endbody_alloc(mdoc, line, ppos,
				    tok, body, ENDBODY_NOSPACE);
			}
		}
	}
	assert(n == body);
	if (body->flags & MDOC_ENDED)
		return;

	rew_last(mdoc, body);
	if (nl)
		append_delims(mdoc, line, pos, buf);
	rew_pending(mdoc, blk);

	/* Move trailing .Ns out of scope. */

	for (n = body->child; n && n->next; n = n->next)
		/* Do nothing. */ ;
	if (n && n->tok == MDOC_Ns)
		mdoc_node_relink(mdoc, n);
}