Пример #1
0
int
mdoc_macro(MACRO_PROT_ARGS)
{
    assert(tok < MDOC_MAX);

    /* If we're in the body, deny prologue calls. */

    if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
            MDOC_PBODY & m->flags) {
        mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY);
        return(1);
    }

    /* If we're in the prologue, deny "body" macros.  */

    if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
            ! (MDOC_PBODY & m->flags)) {
        mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG);
        if (NULL == m->meta.msec)
            m->meta.msec = mandoc_strdup("1");
        if (NULL == m->meta.title)
            m->meta.title = mandoc_strdup("UNKNOWN");
        if (NULL == m->meta.vol)
            m->meta.vol = mandoc_strdup("LOCAL");
        if (NULL == m->meta.os)
            m->meta.os = mandoc_strdup("LOCAL");
        if (0 == m->meta.date)
            m->meta.date = time(NULL);
        m->flags |= MDOC_PBODY;
    }

    return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf));
}
Пример #2
0
static void
check_text(struct mdoc *m, int ln, int pos, char *p)
{
	int		 c;
	size_t		 sz;

	for ( ; *p; p++, pos++) {
		sz = strcspn(p, "\t\\");
		p += (int)sz;

		if ('\0' == *p)
			break;

		pos += (int)sz;

		if ('\t' == *p) {
			if ( ! (MDOC_LITERAL & m->flags))
				mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB);
			continue;
		}

		if (0 == (c = mandoc_special(p))) {
			mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
			continue;
		}

		p += c - 1;
		pos += c - 1;
	}
}
Пример #3
0
/* ARGSUSED */
static int
obsolete(MACRO_PROT_ARGS)
{

	mdoc_pmsg(m, line, ppos, MANDOCERR_MACROOBS);
	return(1);
}
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
int
mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
{

    assert( ! (MDOC_HALT & m->flags));

    /* No text before an initial macro. */

    if (SEC_NONE == m->lastnamed) {
        /* FIXME: grab from span. */
        mdoc_pmsg(m, 0, 0, MANDOCERR_NOTEXT);
        return(1);
    }

    return(mdoc_span_alloc(m, sp));
}
Пример #8
0
static int
pre_an(PRE_ARGS)
{
	int		 i;

	if (NULL == n->args)
		return(1);
	
	for (i = 1; i < (int)n->args->argc; i++)
		mdoc_pmsg(mdoc, n->args->argv[i].line, 
			n->args->argv[i].pos, MANDOCERR_IGNARGV);

	if (MDOC_Split == n->args->argv[0].arg)
		n->norm->An.auth = AUTH_split;
	else if (MDOC_Nosplit == n->args->argv[0].arg)
		n->norm->An.auth = AUTH_nosplit;
	else
		abort();

	return(1);
}
Пример #9
0
int
mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
{
	struct mdoc_node *n;

	assert( ! (MDOC_HALT & m->flags));

	/* No text before an initial macro. */

	if (SEC_NONE == m->lastnamed) {
		mdoc_pmsg(m, sp->line, 0, MANDOCERR_NOTEXT);
		return(1);
	}

	n = node_alloc(m, sp->line, 0, MDOC_MAX, MDOC_TBL);
	n->span = sp;

	if ( ! node_append(m, n))
		return(0);

	m->next = MDOC_NEXT_SIBLING;
	return(1);
}
Пример #10
0
int
mdoc_addeqn(struct mdoc *m, const struct eqn *ep)
{
	struct mdoc_node *n;

	assert( ! (MDOC_HALT & m->flags));

	/* No text before an initial macro. */

	if (SEC_NONE == m->lastnamed) {
		mdoc_pmsg(m, ep->ln, ep->pos, MANDOCERR_NOTEXT);
		return(1);
	}

	n = node_alloc(m, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
	n->eqn = ep;

	if ( ! node_append(m, n))
		return(0);

	m->next = MDOC_NEXT_SIBLING;
	return(1);
}
Пример #11
0
static int
argv_single(struct mdoc *m, int line, 
		struct mdoc_argv *v, int *pos, char *buf)
{
	int		 ppos;
	enum margserr	 ac;
	char		*p;

	ppos = *pos;

	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
	if (ARGS_EOLN == ac) {
		mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
		return(0);
	} else if (ARGS_ERROR == ac)
		return(0);

	v->sz = 1;
	v->value = mandoc_malloc(sizeof(char *));
	v->value[0] = mandoc_strdup(p);

	return(1);
}
Пример #12
0
/*
 * Parse free-form text, that is, a line that does not begin with the
 * control character.
 */
static int
mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
{
    char		 *c, *ws, *end;
    struct mdoc_node *n;

    /* Ignore bogus comments. */

    if ('\\' == buf[offs] &&
            '.' == buf[offs + 1] &&
            '"' == buf[offs + 2]) {
        mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT);
        return(1);
    }

    /* No text before an initial macro. */

    if (SEC_NONE == m->lastnamed) {
        mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT);
        return(1);
    }

    assert(m->last);
    n = m->last;

    /*
     * Divert directly to list processing if we're encountering a
     * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
     * (a MDOC_BODY means it's already open, in which case we should
     * process within its context in the normal way).
     */

    if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
            LIST_column == n->norm->Bl.type) {
        /* `Bl' is open without any children. */
        m->flags |= MDOC_FREECOL;
        return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
    }

    if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
            NULL != n->parent &&
            MDOC_Bl == n->parent->tok &&
            LIST_column == n->parent->norm->Bl.type) {
        /* `Bl' has block-level `It' children. */
        m->flags |= MDOC_FREECOL;
        return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
    }

    /*
     * Search for the beginning of unescaped trailing whitespace (ws)
     * and for the first character not to be output (end).
     */

    /* FIXME: replace with strcspn(). */
    ws = NULL;
    for (c = end = buf + offs; *c; c++) {
        switch (*c) {
        case '-':
            if (mandoc_hyph(buf + offs, c))
                *c = ASCII_HYPH;
            ws = NULL;
            break;
        case ' ':
            if (NULL == ws)
                ws = c;
            continue;
        case '\t':
            /*
             * Always warn about trailing tabs,
             * even outside literal context,
             * where they should be put on the next line.
             */
            if (NULL == ws)
                ws = c;
            /*
             * Strip trailing tabs in literal context only;
             * outside, they affect the next line.
             */
            if (MDOC_LITERAL & m->flags)
                continue;
            break;
        case '\\':
            /* Skip the escaped character, too, if any. */
            if (c[1])
                c++;
        /* FALLTHROUGH */
        default:
            ws = NULL;
            break;
        }
        end = c + 1;
    }
    *end = '\0';

    if (ws)
        mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);

    if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
        mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN);

        /*
         * Insert a `sp' in the case of a blank line.  Technically,
         * blank lines aren't allowed, but enough manuals assume this
         * behaviour that we want to work around it.
         */
        if ( ! mdoc_elem_alloc(m, line, offs, MDOC_sp, NULL))
            return(0);

        m->next = MDOC_NEXT_SIBLING;
        return(1);
    }

    if ( ! mdoc_word_alloc(m, line, offs, buf+offs))
        return(0);

    if (MDOC_LITERAL & m->flags)
        return(1);

    /*
     * End-of-sentence check.  If the last character is an unescaped
     * EOS character, then flag the node as being the end of a
     * sentence.  The front-end will know how to interpret this.
     */

    assert(buf < end);

    if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0))
        m->last->flags |= MDOC_EOS;

    return(1);
}
Пример #13
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));
}
Пример #14
0
/*
 * Close out block partial/full explicit.  
 */
static int
blk_exp_close(MACRO_PROT_ARGS)
{
	int	 	 j, lastarg, maxargs, flushed, nl;
	enum margserr	 ac;
	enum mdoct	 ntok;
	char		*p;

	nl = MDOC_NEWLINE & m->flags;

	switch (tok) {
	case (MDOC_Ec):
		maxargs = 1;
		break;
	default:
		maxargs = 0;
		break;
	}

	if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
		/* FIXME: do this in validate */
		if (buf[*pos]) 
			if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST))
				return(0);

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

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

	if (maxargs > 0) 
		if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok)))
			return(0);

	for (flushed = j = 0; ; j++) {
		lastarg = *pos;

		if (j == maxargs && ! flushed) {
			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
				return(0);
			flushed = 1;
		}

		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;

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

		if (MDOC_MAX == ntok) {
			if ( ! mdoc_word_alloc(m, line, lastarg, p))
				return(0);
			continue;
		}

		if ( ! flushed) {
			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
				return(0);
			flushed = 1;
		}
		if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf))
			return(0);
		break;
	}

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

	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}
Пример #15
0
/*
 * Parse a macro line, that is, a line beginning with the control
 * character.
 */
static int
mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
{
    enum mdoct	  tok;
    int		  i, j, sv;
    char		  mac[5];
    struct mdoc_node *n;

    /* Empty lines are ignored. */

    offs++;

    if ('\0' == buf[offs])
        return(1);

    i = offs;

    /* Accept tabs/whitespace after the initial control char. */

    if (' ' == buf[i] || '\t' == buf[i]) {
        i++;
        while (buf[i] && (' ' == buf[i] || '\t' == buf[i]))
            i++;
        if ('\0' == buf[i])
            return(1);
    }

    sv = i;

    /*
     * Copy the first word into a nil-terminated buffer.
     * Stop copying when a tab, space, or eoln is encountered.
     */

    j = 0;
    while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i])
        mac[j++] = buf[i++];
    mac[j] = '\0';

    tok = (j > 1 || j < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
    if (MDOC_MAX == tok) {
        mdoc_vmsg(m, MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1);
        return(1);
    }

    /* Disregard the first trailing tab, if applicable. */

    if ('\t' == buf[i])
        i++;

    /* Jump to the next non-whitespace word. */

    while (buf[i] && ' ' == buf[i])
        i++;

    /*
     * Trailing whitespace.  Note that tabs are allowed to be passed
     * into the parser as "text", so we only warn about spaces here.
     */

    if ('\0' == buf[i] && ' ' == buf[i - 1])
        mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE);

    /*
     * If an initial macro or a list invocation, divert directly
     * into macro processing.
     */

    if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) {
        if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
            goto err;
        return(1);
    }

    n = m->last;
    assert(m->last);

    /*
     * If the first macro of a `Bl -column', open an `It' block
     * context around the parsed macro.
     */

    if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
            LIST_column == n->norm->Bl.type) {
        m->flags |= MDOC_FREECOL;
        if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
            goto err;
        return(1);
    }

    /*
     * If we're following a block-level `It' within a `Bl -column'
     * context (perhaps opened in the above block or in ptext()),
     * then open an `It' block context around the parsed macro.
     */

    if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
            NULL != n->parent &&
            MDOC_Bl == n->parent->tok &&
            LIST_column == n->parent->norm->Bl.type) {
        m->flags |= MDOC_FREECOL;
        if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
            goto err;
        return(1);
    }

    /* Normal processing of a macro. */

    if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
        goto err;

    return(1);

err:	/* Error out. */

    m->flags |= MDOC_HALT;
    return(0);
}
Пример #16
0
static enum margserr
args(struct mdoc *mdoc, int line, int *pos, 
		char *buf, enum argsflag fl, char **v)
{
	char		*p, *pp;
	int		 pairs;
	enum margserr	 rc;

	if ('\0' == buf[*pos]) {
		if (MDOC_PPHRASE & mdoc->flags)
			return(ARGS_EOLN);
		/*
		 * If we're not in a partial phrase and the flag for
		 * being a phrase literal is still set, the punctuation
		 * is unterminated.
		 */
		if (MDOC_PHRASELIT & mdoc->flags)
			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);

		mdoc->flags &= ~MDOC_PHRASELIT;
		return(ARGS_EOLN);
	}

	*v = &buf[*pos];

	if (ARGSFL_DELIM == fl)
		if (args_checkpunct(buf, *pos))
			return(ARGS_PUNCT);

	/*
	 * First handle TABSEP items, restricted to `Bl -column'.  This
	 * ignores conventional token parsing and instead uses tabs or
	 * `Ta' macros to separate phrases.  Phrases are parsed again
	 * for arguments at a later phase.
	 */

	if (ARGSFL_TABSEP == fl) {
		/* Scan ahead to tab (can't be escaped). */
		p = strchr(*v, '\t');
		pp = NULL;

		/* Scan ahead to unescaped `Ta'. */
		if ( ! (MDOC_PHRASELIT & mdoc->flags)) 
			for (pp = *v; ; pp++) {
				if (NULL == (pp = strstr(pp, "Ta")))
					break;
				if (pp > *v && ' ' != *(pp - 1))
					continue;
				if (' ' == *(pp + 2) || '\0' == *(pp + 2))
					break;
			}

		/* By default, assume a phrase. */
		rc = ARGS_PHRASE;

		/* 
		 * Adjust new-buffer position to be beyond delimiter
		 * mark (e.g., Ta -> end + 2).
		 */
		if (p && pp) {
			*pos += pp < p ? 2 : 1;
			rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE;
			p = pp < p ? pp : p;
		} else if (p && ! pp) {
			rc = ARGS_PPHRASE;
			*pos += 1;
		} else if (pp && ! p) {
			p = pp;
			*pos += 2;
		} else {
			rc = ARGS_PEND;
			p = strchr(*v, 0);
		}

		/* Whitespace check for eoln case... */
		if ('\0' == *p && ' ' == *(p - 1))
			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);

		*pos += (int)(p - *v);

		/* Strip delimiter's preceding whitespace. */
		pp = p - 1;
		while (pp > *v && ' ' == *pp) {
			if (pp > *v && '\\' == *(pp - 1))
				break;
			pp--;
		}
		*(pp + 1) = 0;

		/* Strip delimiter's proceeding whitespace. */
		for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++)
			/* Skip ahead. */ ;

		return(rc);
	}

	/*
	 * Process a quoted literal.  A quote begins with a double-quote
	 * and ends with a double-quote NOT preceded by a double-quote.
	 * NUL-terminate the literal in place.
	 * Collapse pairs of quotes inside quoted literals.
	 * Whitespace is NOT involved in literal termination.
	 */

	if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) {
		if ( ! (MDOC_PHRASELIT & mdoc->flags))
			*v = &buf[++(*pos)];

		if (MDOC_PPHRASE & mdoc->flags)
			mdoc->flags |= MDOC_PHRASELIT;

		pairs = 0;
		for ( ; buf[*pos]; (*pos)++) {
			/* Move following text left after quoted quotes. */
			if (pairs)
				buf[*pos - pairs] = buf[*pos];
			if ('\"' != buf[*pos])
				continue;
			/* Unquoted quotes end quoted args. */
			if ('\"' != buf[*pos + 1])
				break;
			/* Quoted quotes collapse. */
			pairs++;
			(*pos)++;
		}
		if (pairs)
			buf[*pos - pairs] = '\0';

		if ('\0' == buf[*pos]) {
			if (MDOC_PPHRASE & mdoc->flags)
				return(ARGS_QWORD);
			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
			return(ARGS_QWORD);
		}

		mdoc->flags &= ~MDOC_PHRASELIT;
		buf[(*pos)++] = '\0';

		if ('\0' == buf[*pos])
			return(ARGS_QWORD);

		while (' ' == buf[*pos])
			(*pos)++;

		if ('\0' == buf[*pos])
			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);

		return(ARGS_QWORD);
	}

	p = &buf[*pos];
	*v = mandoc_getarg(mdoc->parse, &p, line, pos);

	return(ARGS_WORD);
}
Пример #17
0
/*
 * Close out block partial/full explicit.  
 */
static int
blk_exp_close(MACRO_PROT_ARGS)
{
	struct mdoc_node *body;		/* Our own body. */
	struct mdoc_node *later;	/* A sub-block starting later. */
	struct mdoc_node *n;		/* For searching backwards. */

	int	 	 j, lastarg, maxargs, flushed, nl;
	enum margserr	 ac;
	enum mdoct	 atok, ntok;
	char		*p;

	nl = MDOC_NEWLINE & m->flags;

	switch (tok) {
	case (MDOC_Ec):
		maxargs = 1;
		break;
	default:
		maxargs = 0;
		break;
	}

	/*
	 * Search backwards for beginnings of blocks,
	 * both of our own and of pending sub-blocks.
	 */
	atok = rew_alt(tok);
	body = later = NULL;
	for (n = m->last; n; n = n->parent) {
		if (MDOC_VALID & n->flags)
			continue;

		/* Remember the start of our own body. */
		if (MDOC_BODY == n->type && atok == n->tok) {
			if (ENDBODY_NOT == n->end)
				body = n;
			continue;
		}

		if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok)
			continue;
		if (atok == n->tok) {
			assert(body);

			/*
			 * Found the start of our own block.
			 * When there is no pending sub block,
			 * just proceed to closing out.
			 */
			if (NULL == later)
				break;

			/* 
			 * When there is a pending sub block,
			 * postpone closing out the current block
			 * until the rew_sub() closing out the sub-block.
			 */
			make_pending(later, tok, m, line, ppos);

			/*
			 * Mark the place where the formatting - but not
			 * the scope - of the current block ends.
			 */
			if ( ! mdoc_endbody_alloc(m, line, ppos,
			    atok, body, ENDBODY_SPACE))
				return(0);
			break;
		}

		/*
		 * When finding an open sub block, remember the last
		 * open explicit block, or, in case there are only
		 * implicit ones, the first open implicit block.
		 */
		if (later &&
		    MDOC_EXPLICIT & mdoc_macros[later->tok].flags)
			continue;
		if (MDOC_CALLABLE & mdoc_macros[n->tok].flags)
			later = n;
	}

	if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
		/* FIXME: do this in validate */
		if (buf[*pos]) 
			mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST);

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

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

	if (NULL == later && maxargs > 0) 
		if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok)))
			return(0);

	for (flushed = j = 0; ; j++) {
		lastarg = *pos;

		if (j == maxargs && ! flushed) {
			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
				return(0);
			flushed = 1;
		}

		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;

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

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

		if ( ! flushed) {
			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
				return(0);
			flushed = 1;
		}
		if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf))
			return(0);
		break;
	}

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

	if ( ! nl)
		return(1);
	return(append_delims(m, line, pos, buf));
}