Пример #1
0
static void
append_delims(struct roff_man *mdoc, int line, int *pos, char *buf)
{
	char		*p;
	int		 la;

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

	for (;;) {
		la = *pos;
		if (mdoc_args(mdoc, line, pos, buf, TOKEN_NONE, &p) ==
		    ARGS_EOLN)
			break;
		dword(mdoc, line, la, p, DELIM_MAX, 1);

		/*
		 * If we encounter end-of-sentence symbols, then trigger
		 * the double-space.
		 *
		 * XXX: it's easy to allow this to propagate outward to
		 * the last symbol, such that `. )' will cause the
		 * correct double-spacing.  However, (1) groff isn't
		 * smart enough to do this and (2) it would require
		 * knowing which symbols break this behaviour, for
		 * example, `.  ;' shouldn't propagate the double-space.
		 */

		if (mandoc_eos(p, strlen(p)))
			mdoc->last->flags |= MDOC_EOS;
	}
}
Пример #2
0
static int
append_delims(struct mdoc *m, int line, int *pos, char *buf)
{
	int		 la;
	enum margserr	 ac;
	char		*p;

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

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

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

		assert(DELIM_NONE != mdoc_isdelim(p));
		if ( ! mdoc_word_alloc(m, line, la, p))
			return(0);

		/*
		 * If we encounter end-of-sentence symbols, then trigger
		 * the double-space.
		 *
		 * XXX: it's easy to allow this to propogate outward to
		 * the last symbol, such that `. )' will cause the
		 * correct double-spacing.  However, (1) groff isn't
		 * smart enough to do this and (2) it would require
		 * knowing which symbols break this behaviour, for
		 * example, `.  ;' shouldn't propogate the double-space.
		 */
		if (mandoc_eos(p, strlen(p)))
			m->last->flags |= MDOC_EOS;
	}

	return(1);
}
Пример #3
0
static int
man_ptext(struct man *man, int line, char *buf, int offs)
{
	int		 i;

	/* Literal free-form text whitespace is preserved. */

	if (MAN_LITERAL & man->flags) {
		if ( ! man_word_alloc(man, line, offs, buf + offs))
			return(0);
		return(man_descope(man, line, offs));
	}

	for (i = offs; ' ' == buf[i]; i++)
		/* Skip leading whitespace. */ ;

	/*
	 * Blank lines are ignored right after headings
	 * but add a single vertical space elsewhere.
	 */

	if ('\0' == buf[i]) {
		/* Allocate a blank entry. */
		if (MAN_SH != man->last->tok &&
		    MAN_SS != man->last->tok) {
			if ( ! man_elem_alloc(man, line, offs, MAN_sp))
				return(0);
			man->next = MAN_NEXT_SIBLING;
		}
		return(1);
	}

	/* 
	 * Warn if the last un-escaped character is whitespace. Then
	 * strip away the remaining spaces (tabs stay!).   
	 */

	i = (int)strlen(buf);
	assert(i);

	if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
		if (i > 1 && '\\' != buf[i - 2])
			man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE);

		for (--i; i && ' ' == buf[i]; i--)
			/* Spin back to non-space. */ ;

		/* Jump ahead of escaped whitespace. */
		i += '\\' == buf[i] ? 2 : 1;

		buf[i] = '\0';
	}

	if ( ! man_word_alloc(man, line, offs, buf + offs))
		return(0);

	/*
	 * 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(i);
	if (mandoc_eos(buf, (size_t)i, 0))
		man->last->flags |= MAN_EOS;

	return(man_descope(man, line, offs));
}
Пример #4
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);
}
Пример #5
0
static int
man_ptext(struct man *m, int line, char *buf, int offs)
{
	int		 i;

	/* Literal free-form text whitespace is preserved. */

	if (MAN_LITERAL & m->flags) {
		if ( ! man_word_alloc(m, line, offs, buf + offs))
			return(0);
		return(man_descope(m, line, offs));
	}

	/* Pump blank lines directly into the backend. */

	for (i = offs; ' ' == buf[i]; i++)
		/* Skip leading whitespace. */ ;

	if ('\0' == buf[i]) {
		/* Allocate a blank entry. */
		if ( ! man_word_alloc(m, line, offs, ""))
			return(0);
		return(man_descope(m, line, offs));
	}

	/* 
	 * Warn if the last un-escaped character is whitespace. Then
	 * strip away the remaining spaces (tabs stay!).   
	 */

	i = (int)strlen(buf);
	assert(i);

	if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
		if (i > 1 && '\\' != buf[i - 2])
			man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);

		for (--i; i && ' ' == buf[i]; i--)
			/* Spin back to non-space. */ ;

		/* Jump ahead of escaped whitespace. */
		i += '\\' == buf[i] ? 2 : 1;

		buf[i] = '\0';
	}

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

	/*
	 * 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(i);
	if (mandoc_eos(buf, (size_t)i, 0))
		m->last->flags |= MAN_EOS;

	return(man_descope(m, line, offs));
}
Пример #6
0
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);
}
Пример #7
0
void
in_line_eoln(MACRO_PROT_ARGS)
{
	int		 la;
	char		*p;
	struct roff_node *n;

	roff_elem_alloc(man, line, ppos, tok);
	n = man->last;

	for (;;) {
		if (buf[*pos] != '\0' && (tok == MAN_br ||
		    tok == MAN_fi || tok == MAN_nf)) {
			mandoc_vmsg(MANDOCERR_ARG_SKIP,
			    man->parse, line, *pos, "%s %s",
			    man_macronames[tok], buf + *pos);
			break;
		}
		if (buf[*pos] != '\0' && man->last != n &&
		    (tok == MAN_PD || tok == MAN_ft || tok == MAN_sp)) {
			mandoc_vmsg(MANDOCERR_ARG_EXCESS,
			    man->parse, line, *pos, "%s ... %s",
			    man_macronames[tok], buf + *pos);
			break;
		}
		la = *pos;
		if ( ! man_args(man, line, pos, buf, &p))
			break;
		if (man_macros[tok].flags & MAN_JOIN &&
		    man->last->type == ROFFT_TEXT)
			roff_word_append(man, p);
		else
			roff_word_alloc(man, line, la, p);
	}

	/*
	 * Append MAN_EOS in case the last snipped argument
	 * ends with a dot, e.g. `.IR syslog (3).'
	 */

	if (n != man->last &&
	    mandoc_eos(man->last->string, strlen(man->last->string)))
		man->last->flags |= MAN_EOS;

	/*
	 * If no arguments are specified and this is MAN_SCOPED (i.e.,
	 * next-line scoped), then set our mode to indicate that we're
	 * waiting for terms to load into our context.
	 */

	if (n == man->last && man_macros[tok].flags & MAN_SCOPED) {
		assert( ! (man_macros[tok].flags & MAN_NSCOPED));
		man->flags |= MAN_ELINE;
		return;
	}

	assert(man->last->type != ROFFT_ROOT);
	man->next = ROFF_NEXT_SIBLING;

	/* Rewind our element scope. */

	for ( ; man->last; man->last = man->last->parent) {
		man_state(man, man->last);
		if (man->last == n)
			break;
	}
}
Пример #8
0
static int
man_ptext(struct roff_man *man, int line, char *buf, int offs)
{
	int		 i;

	/* Literal free-form text whitespace is preserved. */

	if (man->flags & MAN_LITERAL) {
		roff_word_alloc(man, line, offs, buf + offs);
		man_descope(man, line, offs);
		return 1;
	}

	for (i = offs; buf[i] == ' '; i++)
		/* Skip leading whitespace. */ ;

	/*
	 * Blank lines are ignored in next line scope and right
	 * after headings but add a single vertical space elsewhere.
	 */

	if (buf[i] == '\0') {
		if (man->flags & (MAN_ELINE | MAN_BLINE))
			mandoc_msg(MANDOCERR_BLK_BLANK, man->parse,
			    line, 0, NULL);
		else if (man->last->tok != MAN_SH &&
		    man->last->tok != MAN_SS) {
			roff_elem_alloc(man, line, offs, ROFF_sp);
			man->next = ROFF_NEXT_SIBLING;
		}
		return 1;
	}

	/*
	 * Warn if the last un-escaped character is whitespace. Then
	 * strip away the remaining spaces (tabs stay!).
	 */

	i = (int)strlen(buf);
	assert(i);

	if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
		if (i > 1 && '\\' != buf[i - 2])
			mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
			    line, i - 1, NULL);

		for (--i; i && ' ' == buf[i]; i--)
			/* Spin back to non-space. */ ;

		/* Jump ahead of escaped whitespace. */
		i += '\\' == buf[i] ? 2 : 1;

		buf[i] = '\0';
	}
	roff_word_alloc(man, line, offs, buf + offs);

	/*
	 * 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(i);
	if (mandoc_eos(buf, (size_t)i))
		man->last->flags |= NODE_EOS;

	man_descope(man, line, offs);
	return 1;
}
Пример #9
0
/* ARGSUSED */
int
in_line_eoln(MACRO_PROT_ARGS)
{
	int		 la;
	char		*p;
	struct man_node	*n;

	if ( ! man_elem_alloc(man, line, ppos, tok))
		return(0);

	n = man->last;

	for (;;) {
		la = *pos;
		if ( ! man_args(man, line, pos, buf, &p))
			break;
		if ( ! man_word_alloc(man, line, la, p))
			return(0);
	}

	/*
	 * Append MAN_EOS in case the last snipped argument
	 * ends with a dot, e.g. `.IR syslog (3).'
	 */

	if (n != man->last &&
	    mandoc_eos(man->last->string, strlen(man->last->string)))
		man->last->flags |= MAN_EOS;

	/*
	 * If no arguments are specified and this is MAN_SCOPED (i.e.,
	 * next-line scoped), then set our mode to indicate that we're
	 * waiting for terms to load into our context.
	 */

	if (n == man->last && MAN_SCOPED & man_macros[tok].flags) {
		assert( ! (MAN_NSCOPED & man_macros[tok].flags));
		man->flags |= MAN_ELINE;
		return(1);
	} 

	/* Set ignorable context, if applicable. */

	if (MAN_NSCOPED & man_macros[tok].flags) {
		assert( ! (MAN_SCOPED & man_macros[tok].flags));
		man->flags |= MAN_ILINE;
	}

	assert(MAN_ROOT != man->last->type);
	man->next = MAN_NEXT_SIBLING;
	
	/*
	 * Rewind our element scope.  Note that when TH is pruned, we'll
	 * be back at the root, so make sure that we don't clobber as
	 * its sibling.
	 */

	for ( ; man->last; man->last = man->last->parent) {
		if (man->last == n)
			break;
		if (man->last->type == MAN_ROOT)
			break;
		if ( ! man_valid_post(man))
			return(0);
	}

	assert(man->last);

	/*
	 * Same here regarding whether we're back at the root. 
	 */

	if (man->last->type != MAN_ROOT && ! man_valid_post(man))
		return(0);

	return(1);
}