X-Git-Url: https://git.cameronkatri.com/mandoc.git/blobdiff_plain/dad3c2117152071099f857651e99f8673aa988dd..138483e8c7e6cccb5209979e94fec1fd87d0c343:/mdoc.c

diff --git a/mdoc.c b/mdoc.c
index 113a4df6..96b4bffd 100644
--- a/mdoc.c
+++ b/mdoc.c
@@ -1,4 +1,4 @@
-/*	$Id: mdoc.c,v 1.90 2009/07/12 20:50:08 kristaps Exp $ */
+/*	$Id: mdoc.c,v 1.116 2010/01/07 10:24:43 kristaps Exp $ */
 /*
  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
  *
@@ -14,6 +14,12 @@
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+
 #include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
@@ -22,16 +28,13 @@
 #include <string.h>
 
 #include "libmdoc.h"
+#include "libmandoc.h"
 
 const	char *const __mdoc_merrnames[MERRMAX] = {		 
 	"trailing whitespace", /* ETAILWS */
-	"empty last list column", /* ECOLEMPTY */
-	"argument-like parameter", /* EARGVPARM */
 	"unexpected quoted parameter", /* EQUOTPARM */
 	"unterminated quoted parameter", /* EQUOTTERM */
-	"system: malloc error", /* EMALLOC */
 	"argument parameter suggested", /* EARGVAL */
-	"macro not callable", /* ENOCALL */
 	"macro disallowed in prologue", /* EBODYPROL */
 	"macro disallowed in body", /* EPROLBODY */
 	"text disallowed in prologue", /* ETEXTPROL */
@@ -72,14 +75,14 @@ const	char *const __mdoc_merrnames[MERRMAX] = {
 	"superfluous width argument", /* ENOWIDTH */
 	"system: utsname error", /* EUTSNAME */
 	"obsolete macro", /* EOBS */
-	"macro-like parameter", /* EMACPARM */
 	"end-of-line scope violation", /* EIMPBRK */
 	"empty macro ignored", /* EIGNE */
 	"unclosed explicit scope", /* EOPEN */
 	"unterminated quoted phrase", /* EQUOTPHR */
 	"closure macro without prior context", /* ENOCTX */
-	"invalid whitespace after control character", /* ESPACE */
-	"no description found for library" /* ELIB */
+	"no description found for library", /* ELIB */
+	"bad child for parent context", /* EBADCHILD */
+	"list arguments preceding type", /* ENOTYPE */
 };
 
 const	char *const __mdoc_macronames[MDOC_MAX] = {		 
@@ -94,11 +97,11 @@ const	char *const __mdoc_macronames[MDOC_MAX] = {
 	"Nm",		"Op",		"Ot",		"Pa",
 	"Rv",		"St",		"Va",		"Vt",
 	/* LINTED */
-	"Xr",		"\%A",		"\%B",		"\%D",
+	"Xr",		"%A",		"%B",		"%D",
 	/* LINTED */
-	"\%I",		"\%J",		"\%N",		"\%O",
+	"%I",		"%J",		"%N",		"%O",
 	/* LINTED */
-	"\%P",		"\%R",		"\%T",		"\%V",
+	"%P",		"%R",		"%T",		"%V",
 	"Ac",		"Ao",		"Aq",		"At",
 	"Bc",		"Bf",		"Bo",		"Bq",
 	"Bsx",		"Bx",		"Db",		"Dc",
@@ -115,9 +118,11 @@ const	char *const __mdoc_macronames[MDOC_MAX] = {
 	"Fr",		"Ud",		"Lb",		"Lp",
 	"Lk",		"Mt",		"Brq",		"Bro",
 	/* LINTED */
-	"Brc",		"\%C",		"Es",		"En",
+	"Brc",		"%C",		"Es",		"En",
+	/* LINTED */
+	"Dx",		"%Q",		"br",		"sp",
 	/* LINTED */
-	"Dx",		"\%Q"
+	"%U"
 	};
 
 const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {		 
@@ -129,14 +134,14 @@ const	char *const __mdoc_argnames[MDOC_ARG_MAX] = {
 	"ohang",		"inset",		"column",	 
 	"width",		"compact",		"std",	 
 	"filled",		"words",		"emphasis",
-	"symbolic",		"nested"
+	"symbolic",		"nested",		"centered"
 	};
 
 const	char * const *mdoc_macronames = __mdoc_macronames;
 const	char * const *mdoc_argnames = __mdoc_argnames;
 
 static	void		  mdoc_free1(struct mdoc *);
-static	int		  mdoc_alloc1(struct mdoc *);
+static	void		  mdoc_alloc1(struct mdoc *);
 static	struct mdoc_node *node_alloc(struct mdoc *, int, int, 
 				int, enum mdoc_type);
 static	int		  node_append(struct mdoc *, 
@@ -144,7 +149,8 @@ static	int		  node_append(struct mdoc *,
 static	int		  parsetext(struct mdoc *, int, char *);
 static	int		  parsemacro(struct mdoc *, int, char *);
 static	int		  macrowarn(struct mdoc *, int, const char *);
-
+static	int		  pstring(struct mdoc *, int, int, 
+				const char *, size_t);
 
 const struct mdoc_node *
 mdoc_node(const struct mdoc *m)
@@ -187,21 +193,17 @@ mdoc_free1(struct mdoc *mdoc)
 /*
  * Allocate all volatile resources (parse tree, meta-data, fields).
  */
-static int
+static void
 mdoc_alloc1(struct mdoc *mdoc)
 {
 
-	bzero(&mdoc->meta, sizeof(struct mdoc_meta));
+	memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
 	mdoc->flags = 0;
 	mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
-	mdoc->last = calloc(1, sizeof(struct mdoc_node));
-	if (NULL == mdoc->last)
-		return(0);
-
+	mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
 	mdoc->first = mdoc->last;
 	mdoc->last->type = MDOC_ROOT;
 	mdoc->next = MDOC_NEXT_CHILD;
-	return(1);
 }
 
 
@@ -211,12 +213,12 @@ mdoc_alloc1(struct mdoc *mdoc)
  * and the parser is ready for re-invocation on a new tree; however,
  * cross-parse non-volatile data is kept intact.
  */
-int
+void
 mdoc_reset(struct mdoc *mdoc)
 {
 
 	mdoc_free1(mdoc);
-	return(mdoc_alloc1(mdoc));
+	mdoc_alloc1(mdoc);
 }
 
 
@@ -229,8 +231,6 @@ mdoc_free(struct mdoc *mdoc)
 {
 
 	mdoc_free1(mdoc);
-	if (mdoc->htab)
-		mdoc_hash_free(mdoc->htab);
 	free(mdoc);
 }
 
@@ -243,22 +243,17 @@ mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb)
 {
 	struct mdoc	*p;
 
-	if (NULL == (p = calloc(1, sizeof(struct mdoc))))
-		return(NULL);
+	p = mandoc_calloc(1, sizeof(struct mdoc));
+
 	if (cb)
-		(void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
+		memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
 
 	p->data = data;
 	p->pflags = pflags;
 
-	if (NULL == (p->htab = mdoc_hash_alloc())) {
-		free(p);
-		return(NULL);
-	} else if (mdoc_alloc1(p))
-		return(p);
-
-	free(p);
-	return(NULL);
+	mdoc_hash_init();
+	mdoc_alloc1(p);
+	return(p);
 }
 
 
@@ -349,7 +344,10 @@ int
 mdoc_macro(struct mdoc *m, int tok, 
 		int ln, int pp, int *pos, char *buf)
 {
-
+	/*
+	 * If we're in the prologue, deny "body" macros.  Similarly, if
+	 * we're in the body, deny prologue calls.
+	 */
 	if (MDOC_PROLOGUE & mdoc_macros[tok].flags && 
 			MDOC_PBODY & m->flags)
 		return(mdoc_perr(m, ln, pp, EPROLBODY));
@@ -357,9 +355,6 @@ mdoc_macro(struct mdoc *m, int tok,
 			! (MDOC_PBODY & m->flags))
 		return(mdoc_perr(m, ln, pp, EBODYPROL));
 
-	if (1 != pp && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
-		return(mdoc_perr(m, ln, pp, ENOCALL));
-
 	return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
 }
 
@@ -429,17 +424,13 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
 
 
 static struct mdoc_node *
-node_alloc(struct mdoc *mdoc, int line, 
+node_alloc(struct mdoc *m, int line, 
 		int pos, int tok, enum mdoc_type type)
 {
 	struct mdoc_node *p;
 
-	if (NULL == (p = calloc(1, sizeof(struct mdoc_node)))) {
-		(void)mdoc_nerr(mdoc, mdoc->last, EMALLOC);
-		return(NULL);
-	}
-
-	p->sec = mdoc->lastsec;
+	p = mandoc_calloc(1, sizeof(struct mdoc_node));
+	p->sec = m->lastsec;
 	p->line = line;
 	p->pos = pos;
 	p->tok = tok;
@@ -451,91 +442,106 @@ node_alloc(struct mdoc *mdoc, int line,
 
 
 int
-mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
+mdoc_tail_alloc(struct mdoc *m, int line, int pos, int tok)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
-	if (NULL == p)
+	p = node_alloc(m, line, pos, tok, MDOC_TAIL);
+	if ( ! node_append(m, p))
 		return(0);
-	return(node_append(mdoc, p));
+	m->next = MDOC_NEXT_CHILD;
+	return(1);
 }
 
 
 int
-mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
+mdoc_head_alloc(struct mdoc *m, int line, int pos, int tok)
 {
 	struct mdoc_node *p;
 
-	assert(mdoc->first);
-	assert(mdoc->last);
+	assert(m->first);
+	assert(m->last);
 
-	p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
-	if (NULL == p)
+	p = node_alloc(m, line, pos, tok, MDOC_HEAD);
+	if ( ! node_append(m, p))
 		return(0);
-	return(node_append(mdoc, p));
+	m->next = MDOC_NEXT_CHILD;
+	return(1);
 }
 
 
 int
-mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
+mdoc_body_alloc(struct mdoc *m, int line, int pos, int tok)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
-	if (NULL == p)
+	p = node_alloc(m, line, pos, tok, MDOC_BODY);
+	if ( ! node_append(m, p))
 		return(0);
-	return(node_append(mdoc, p));
+	m->next = MDOC_NEXT_CHILD;
+	return(1);
 }
 
 
 int
-mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, 
+mdoc_block_alloc(struct mdoc *m, int line, int pos, 
 		int tok, struct mdoc_arg *args)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
-	if (NULL == p)
-		return(0);
+	p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
 	p->args = args;
 	if (p->args)
 		(args->refcnt)++;
-	return(node_append(mdoc, p));
+	if ( ! node_append(m, p))
+		return(0);
+	m->next = MDOC_NEXT_CHILD;
+	return(1);
 }
 
 
 int
-mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos, 
+mdoc_elem_alloc(struct mdoc *m, int line, int pos, 
 		int tok, struct mdoc_arg *args)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
-	if (NULL == p)
-		return(0);
+	p = node_alloc(m, line, pos, tok, MDOC_ELEM);
 	p->args = args;
 	if (p->args)
 		(args->refcnt)++;
-	return(node_append(mdoc, p));
+	if ( ! node_append(m, p))
+		return(0);
+	m->next = MDOC_NEXT_CHILD;
+	return(1);
 }
 
 
-int
-mdoc_word_alloc(struct mdoc *mdoc, 
-		int line, int pos, const char *word)
+static int
+pstring(struct mdoc *m, int line, int pos, const char *p, size_t len)
 {
-	struct mdoc_node *p;
+	struct mdoc_node *n;
+	size_t		  sv;
 
-	p = node_alloc(mdoc, line, pos, -1, MDOC_TEXT);
-	if (NULL == p)
-		return(0);
-	if (NULL == (p->string = strdup(word))) {
-		(void)mdoc_nerr(mdoc, mdoc->last, EMALLOC);
+	n = node_alloc(m, line, pos, -1, MDOC_TEXT);
+	n->string = mandoc_malloc(len + 1);
+	sv = strlcpy(n->string, p, len + 1);
+
+	/* Prohibit truncation. */
+	assert(sv < len + 1);
+
+	if ( ! node_append(m, n))
 		return(0);
-	}
+	m->next = MDOC_NEXT_SIBLING;
+	return(1);
+}
 
-	return(node_append(mdoc, p));
+
+int
+mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
+{
+
+	return(pstring(m, line, pos, p, strlen(p)));
 }
 
 
@@ -574,14 +580,69 @@ mdoc_node_freelist(struct mdoc_node *p)
 static int
 parsetext(struct mdoc *m, int line, char *buf)
 {
+	int		 i, j;
+	char		 sv;
 
 	if (SEC_NONE == m->lastnamed)
 		return(mdoc_perr(m, line, 0, ETEXTPROL));
+	
+	/*
+	 * If in literal mode, then pass the buffer directly to the
+	 * back-end, as it should be preserved as a single term.
+	 */
+
+	if (MDOC_LITERAL & m->flags)
+		return(mdoc_word_alloc(m, line, 0, buf));
 
-	if (0 == buf[0] && ! (MDOC_LITERAL & m->flags))
+	/* Disallow blank/white-space lines in non-literal mode. */
+
+	for (i = 0; ' ' == buf[i]; i++)
+		/* Skip leading whitespace. */ ;
+
+	if ('\0' == buf[i])
 		return(mdoc_perr(m, line, 0, ENOBLANK));
 
-	if ( ! mdoc_word_alloc(m, line, 0, buf))
+	/*
+	 * Break apart a free-form line into tokens.  Spaces are
+	 * stripped out of the input.
+	 */
+
+	for (j = i; buf[i]; i++) {
+		if (' ' != buf[i])
+			continue;
+
+		/* Escaped whitespace. */
+		if (i && ' ' == buf[i] && '\\' == buf[i - 1])
+			continue;
+
+		sv = buf[i];
+		buf[i++] = '\0';
+
+		if ( ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
+			return(0);
+
+		/* Trailing whitespace?  Check at overwritten byte. */
+
+		if (' ' == sv && '\0' == buf[i])
+			if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
+				return(0);
+
+		for ( ; ' ' == buf[i]; i++)
+			/* Skip trailing whitespace. */ ;
+
+		j = i;
+
+		/* Trailing whitespace? */
+
+		if (' ' == buf[i - 1] && '\0' == buf[i])
+			if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
+				return(0);
+
+		if ('\0' == buf[i])
+			break;
+	}
+
+	if (j != i && ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
 		return(0);
 
 	m->next = MDOC_NEXT_SIBLING;
@@ -589,14 +650,15 @@ parsetext(struct mdoc *m, int line, char *buf)
 }
 
 
+
 static int
 macrowarn(struct mdoc *m, int ln, const char *buf)
 {
 	if ( ! (MDOC_IGN_MACRO & m->pflags))
-		return(mdoc_verr(m, ln, 1, 
+		return(mdoc_verr(m, ln, 0, 
 				"unknown macro: %s%s", 
 				buf, strlen(buf) > 3 ? "..." : ""));
-	return(mdoc_vwarn(m, ln, 1, "unknown macro: %s%s",
+	return(mdoc_vwarn(m, ln, 0, "unknown macro: %s%s",
 				buf, strlen(buf) > 3 ? "..." : ""));
 }
 
@@ -608,41 +670,50 @@ macrowarn(struct mdoc *m, int ln, const char *buf)
 int
 parsemacro(struct mdoc *m, int ln, char *buf)
 {
-	int		  i, c;
+	int		  i, j, c;
 	char		  mac[5];
 
 	/* Empty lines are ignored. */
 
-	if (0 == buf[1])
+	if ('\0' == buf[1])
 		return(1);
 
-	if (' ' == buf[1]) {
-		i = 2;
+	i = 1;
+
+	/* Accept whitespace after the initial control char. */
+
+	if (' ' == buf[i]) {
+		i++;
 		while (buf[i] && ' ' == buf[i])
 			i++;
-		if (0 == buf[i])
+		if ('\0' == buf[i])
 			return(1);
-		return(mdoc_perr(m, ln, 1, ESPACE));
 	}
 
 	/* Copy the first word into a nil-terminated buffer. */
 
-	for (i = 1; i < 5; i++) {
-		if (0 == (mac[i - 1] = buf[i]))
+	for (j = 0; j < 4; j++, i++) {
+		if ('\0' == (mac[j] = buf[i]))
 			break;
 		else if (' ' == buf[i])
 			break;
+
+		/* Check for invalid characters. */
+
+		if (isgraph((u_char)buf[i]))
+			continue;
+		return(mdoc_perr(m, ln, i, EPRINT));
 	}
 
-	mac[i - 1] = 0;
+	mac[j] = 0;
 
-	if (i == 5 || i <= 2) {
+	if (j == 4 || j < 2) {
 		if ( ! macrowarn(m, ln, mac))
 			goto err;
 		return(1);
 	} 
 	
-	if (MDOC_MAX == (c = mdoc_hash_find(m->htab, mac))) {
+	if (MDOC_MAX == (c = mdoc_hash_find(mac))) {
 		if ( ! macrowarn(m, ln, mac))
 			goto err;
 		return(1);
@@ -653,8 +724,16 @@ parsemacro(struct mdoc *m, int ln, char *buf)
 	while (buf[i] && ' ' == buf[i])
 		i++;
 
-	/* Begin recursive parse sequence. */
+	/* Trailing whitespace? */
 
+	if ('\0' == buf[i] && ' ' == buf[i - 1])
+		if ( ! mdoc_pwarn(m, ln, i - 1, ETAILWS))
+			goto err;
+
+	/* 
+	 * Begin recursive parse sequence.  Since we're at the start of
+	 * the line, we don't need to do callable/parseable checks.
+	 */
 	if ( ! mdoc_macro(m, c, ln, 1, &i, buf)) 
 		goto err;
 
@@ -665,3 +744,5 @@ err:	/* Error out. */
 	m->flags |= MDOC_HALT;
 	return(0);
 }
+
+