X-Git-Url: https://git.cameronkatri.com/mandoc.git/blobdiff_plain/d502d4a4bb59a31df7fdbcf4a9b59c21fc1aef1c..9425244d2b742390e69a9fedde12afadefb6802b:/html.c

diff --git a/html.c b/html.c
index ff71f253..138d3e4c 100644
--- a/html.c
+++ b/html.c
@@ -1,6 +1,6 @@
-/*	$Id: html.c,v 1.86 2009/11/14 12:00:24 kristaps Exp $ */
+/*	$Id: html.c,v 1.112 2010/09/04 20:18:53 kristaps Exp $ */
 /*
- * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
+ * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -14,6 +14,10 @@
  * 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>
@@ -25,45 +29,46 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "mandoc.h"
 #include "out.h"
 #include "chars.h"
 #include "html.h"
 #include "main.h"
 
-#define	UNCONST(a)	((void *)(uintptr_t)(const void *)(a))
-
-#define	DOCTYPE		"-//W3C//DTD HTML 4.01//EN"
-#define	DTD		"http://www.w3.org/TR/html4/strict.dtd"
-
 struct	htmldata {
 	const char	 *name;
 	int		  flags;
 #define	HTML_CLRLINE	 (1 << 0)
 #define	HTML_NOSTACK	 (1 << 1)
+#define	HTML_AUTOCLOSE	 (1 << 2) /* Tag has auto-closure. */
 };
 
 static	const struct htmldata htmltags[TAG_MAX] = {
 	{"html",	HTML_CLRLINE}, /* TAG_HTML */
 	{"head",	HTML_CLRLINE}, /* TAG_HEAD */
 	{"body",	HTML_CLRLINE}, /* TAG_BODY */
-	{"meta",	HTML_CLRLINE | HTML_NOSTACK}, /* TAG_META */
+	{"meta",	HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */
 	{"title",	HTML_CLRLINE}, /* TAG_TITLE */
 	{"div",		HTML_CLRLINE}, /* TAG_DIV */
 	{"h1",		0}, /* TAG_H1 */
 	{"h2",		0}, /* TAG_H2 */
-	{"p",		HTML_CLRLINE}, /* TAG_P */
 	{"span",	0}, /* TAG_SPAN */
-	{"link",	HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
-	{"br",		HTML_CLRLINE | HTML_NOSTACK}, /* TAG_LINK */
+	{"link",	HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */
+	{"br",		HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */
 	{"a",		0}, /* TAG_A */
 	{"table",	HTML_CLRLINE}, /* TAG_TABLE */
-	{"col",		HTML_CLRLINE | HTML_NOSTACK}, /* TAG_COL */
+	{"col",		HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */
 	{"tr",		HTML_CLRLINE}, /* TAG_TR */
 	{"td",		HTML_CLRLINE}, /* TAG_TD */
 	{"li",		HTML_CLRLINE}, /* TAG_LI */
 	{"ul",		HTML_CLRLINE}, /* TAG_UL */
 	{"ol",		HTML_CLRLINE}, /* TAG_OL */
-	{"base",	HTML_CLRLINE | HTML_NOSTACK}, /* TAG_BASE */
+};
+
+static	const char	*const htmlfonts[HTMLFONT_MAX] = {
+	"roman",
+	"bold",
+	"italic"
 };
 
 static	const char	*const htmlattrs[ATTR_MAX] = {
@@ -83,19 +88,21 @@ static	const char	*const htmlattrs[ATTR_MAX] = {
 	"summary",
 };
 
-#ifdef __linux__
-extern	int		  getsubopt(char **, char * const *, char **);
-#endif
-
-
-static	void		  print_spec(struct html *, const char *, size_t);
+static	void		  print_spec(struct html *, enum roffdeco,
+				const char *, size_t);
 static	void		  print_res(struct html *, const char *, size_t);
 static	void		  print_ctag(struct html *, enum htmltag);
-static	int		  print_encode(struct html *, const char *);
+static	void		  print_doctype(struct html *);
+static	void		  print_xmltype(struct html *);
+static	int		  print_encode(struct html *, const char *, int);
+static	void		  print_metaf(struct html *, enum roffdeco);
+static	void		  print_attr(struct html *, 
+				const char *, const char *);
+static	void		 *ml_alloc(char *, enum htmltype);
 
 
-void *
-html_alloc(char *outopts)
+static void *
+ml_alloc(char *outopts, enum htmltype type)
 {
 	struct html	*h;
 	const char	*toks[4];
@@ -109,9 +116,10 @@ html_alloc(char *outopts)
 	h = calloc(1, sizeof(struct html));
 	if (NULL == h) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit((int)MANDOCLEVEL_SYSERR);
 	}
 
+	h->type = type;
 	h->tags.head = NULL;
 	h->ords.head = NULL;
 	h->symtab = chars_init(CHARS_HTML);
@@ -134,6 +142,21 @@ html_alloc(char *outopts)
 	return(h);
 }
 
+void *
+html_alloc(char *outopts)
+{
+
+	return(ml_alloc(outopts, HTML_HTML_4_01_STRICT));
+}
+
+
+void *
+xhtml_alloc(char *outopts)
+{
+
+	return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT));
+}
+
 
 void
 html_free(void *p)
@@ -193,45 +216,103 @@ print_gen_head(struct html *h)
 
 
 static void
-print_spec(struct html *h, const char *p, size_t len)
+print_spec(struct html *h, enum roffdeco d, const char *p, size_t len)
 {
+	int		 cp;
 	const char	*rhs;
 	size_t		 sz;
 
-	rhs = chars_a2ascii(h->symtab, p, len, &sz);
-
-	if (NULL == rhs) 
+	if ((cp = chars_spec2cp(h->symtab, p, len)) > 0) {
+		printf("&#%d;", cp);
+		return;
+	} else if (-1 == cp && DECO_SSPECIAL == d) {
+		fwrite(p, 1, len, stdout);
 		return;
-	fwrite(rhs, 1, sz, stdout);
+	} else if (-1 == cp)
+		return;
+
+	if (NULL != (rhs = chars_spec2str(h->symtab, p, len, &sz)))
+		fwrite(rhs, 1, sz, stdout);
 }
 
 
 static void
 print_res(struct html *h, const char *p, size_t len)
 {
+	int		 cp;
 	const char	*rhs;
 	size_t		 sz;
 
-	rhs = chars_a2res(h->symtab, p, len, &sz);
-
-	if (NULL == rhs)
+	if ((cp = chars_res2cp(h->symtab, p, len)) > 0) {
+		printf("&#%d;", cp);
 		return;
-	fwrite(rhs, 1, sz, stdout);
+	} else if (-1 == cp)
+		return;
+
+	if (NULL != (rhs = chars_res2str(h->symtab, p, len, &sz)))
+		fwrite(rhs, 1, sz, stdout);
+}
+
+
+struct tag *
+print_ofont(struct html *h, enum htmlfont font)
+{
+	struct htmlpair	 tag;
+
+	h->metal = h->metac;
+	h->metac = font;
+
+	/* FIXME: DECO_ROMAN should just close out preexisting. */
+
+	if (h->metaf && h->tags.head == h->metaf)
+		print_tagq(h, h->metaf);
+
+	PAIR_CLASS_INIT(&tag, htmlfonts[font]);
+	h->metaf = print_otag(h, TAG_SPAN, 1, &tag);
+	return(h->metaf);
+}
+
+
+static void
+print_metaf(struct html *h, enum roffdeco deco)
+{
+	enum htmlfont	 font;
+
+	switch (deco) {
+	case (DECO_PREVIOUS):
+		font = h->metal;
+		break;
+	case (DECO_ITALIC):
+		font = HTMLFONT_ITALIC;
+		break;
+	case (DECO_BOLD):
+		font = HTMLFONT_BOLD;
+		break;
+	case (DECO_ROMAN):
+		font = HTMLFONT_NONE;
+		break;
+	default:
+		abort();
+		/* NOTREACHED */
+	}
+
+	(void)print_ofont(h, font);
 }
 
 
 static int
-print_encode(struct html *h, const char *p)
+print_encode(struct html *h, const char *p, int norecurse)
 {
 	size_t		 sz;
 	int		 len, nospace;
 	const char	*seq;
 	enum roffdeco	 deco;
+	static const char rejs[6] = { '\\', '<', '>', '&', ASCII_HYPH, '\0' };
 
 	nospace = 0;
 
 	for (; *p; p++) {
-		sz = strcspn(p, "\\<>&");
+		sz = strcspn(p, rejs);
 
 		fwrite(p, 1, sz, stdout);
 		p += /* LINTED */
@@ -246,6 +327,15 @@ print_encode(struct html *h, const char *p)
 		} else if ('&' == *p) {
 			printf("&amp;");
 			continue;
+		} else if (ASCII_HYPH == *p) {
+			/*
+			 * Note: "soft hyphens" aren't graphically
+			 * displayed when not breaking the text; we want
+			 * them to be displayed.
+			 */
+			/*printf("&#173;");*/
+			putchar('-');
+			continue;
 		} else if ('\0' == *p)
 			break;
 
@@ -256,8 +346,21 @@ print_encode(struct html *h, const char *p)
 		case (DECO_RESERVED):
 			print_res(h, seq, sz);
 			break;
+		case (DECO_SSPECIAL):
+			/* FALLTHROUGH */
 		case (DECO_SPECIAL):
-			print_spec(h, seq, sz);
+			print_spec(h, deco, seq, sz);
+			break;
+		case (DECO_PREVIOUS):
+			/* FALLTHROUGH */
+		case (DECO_BOLD):
+			/* FALLTHROUGH */
+		case (DECO_ITALIC):
+			/* FALLTHROUGH */
+		case (DECO_ROMAN):
+			if (norecurse)
+				break;
+			print_metaf(h, deco);
 			break;
 		default:
 			break;
@@ -273,6 +376,15 @@ print_encode(struct html *h, const char *p)
 }
 
 
+static void
+print_attr(struct html *h, const char *key, const char *val)
+{
+	printf(" %s=\"", key);
+	(void)print_encode(h, val, 1);
+	putchar('\"');
+}
+
+
 struct tag *
 print_otag(struct html *h, enum htmltag tag, 
 		int sz, const struct htmlpair *p)
@@ -280,11 +392,13 @@ print_otag(struct html *h, enum htmltag tag,
 	int		 i;
 	struct tag	*t;
 
+	/* Push this tags onto the stack of open scopes. */
+
 	if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
 		t = malloc(sizeof(struct tag));
 		if (NULL == t) {
 			perror(NULL);
-			exit(EXIT_FAILURE);
+			exit((int)MANDOCLEVEL_SYSERR);
 		}
 		t->tag = tag;
 		t->next = h->tags.head;
@@ -293,29 +407,53 @@ print_otag(struct html *h, enum htmltag tag,
 		t = NULL;
 
 	if ( ! (HTML_NOSPACE & h->flags))
-		if ( ! (HTML_CLRLINE & htmltags[tag].flags))
-			putchar(' ');
+		if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
+			/* Manage keeps! */
+			if ( ! (HTML_KEEP & h->flags)) {
+				if (HTML_PREKEEP & h->flags)
+					h->flags |= HTML_KEEP;
+				putchar(' ');
+			} else
+				printf("&#160;");
+		}
+
+	if ( ! (h->flags & HTML_NONOSPACE))
+		h->flags &= ~HTML_NOSPACE;
+	else
+		h->flags |= HTML_NOSPACE;
+
+	/* Print out the tag name and attributes. */
 
 	printf("<%s", htmltags[tag].name);
-	for (i = 0; i < sz; i++) {
-		printf(" %s=\"", htmlattrs[p[i].key]);
-		assert(p->val);
-		(void)print_encode(h, p[i].val);
-		putchar('\"');
+	for (i = 0; i < sz; i++)
+		print_attr(h, htmlattrs[p[i].key], p[i].val);
+
+	/* Add non-overridable attributes. */
+
+	if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) {
+		print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml");
+		print_attr(h, "xml:lang", "en");
+		print_attr(h, "lang", "en");
 	}
+
+	/* Accomodate for XML "well-formed" singleton escaping. */
+
+	if (HTML_AUTOCLOSE & htmltags[tag].flags)
+		switch (h->type) {
+		case (HTML_XHTML_1_0_STRICT):
+			putchar('/');
+			break;
+		default:
+			break;
+		}
+
 	putchar('>');
 
 	h->flags |= HTML_NOSPACE;
-	if (HTML_CLRLINE & htmltags[tag].flags)
-		h->flags |= HTML_NEWLINE;
-	else
-		h->flags &= ~HTML_NEWLINE;
-
 	return(t);
 }
 
 
-/* ARGSUSED */
 static void
 print_ctag(struct html *h, enum htmltag tag)
 {
@@ -323,28 +461,60 @@ print_ctag(struct html *h, enum htmltag tag)
 	printf("</%s>", htmltags[tag].name);
 	if (HTML_CLRLINE & htmltags[tag].flags) {
 		h->flags |= HTML_NOSPACE;
-		h->flags |= HTML_NEWLINE;
 		putchar('\n');
-	} else
-		h->flags &= ~HTML_NEWLINE;
+	} 
 }
 
 
-/* ARGSUSED */
 void
-print_gen_doctype(struct html *h)
+print_gen_decls(struct html *h)
 {
-	
-	printf("<!DOCTYPE HTML PUBLIC \"%s\" \"%s\">", DOCTYPE, DTD);
+
+	print_xmltype(h);
+	print_doctype(h);
+}
+
+
+static void
+print_xmltype(struct html *h)
+{
+
+	if (HTML_XHTML_1_0_STRICT == h->type)
+		printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+}
+
+
+static void
+print_doctype(struct html *h)
+{
+	const char	*doctype;
+	const char	*dtd;
+	const char	*name;
+
+	switch (h->type) {
+	case (HTML_HTML_4_01_STRICT):
+		name = "HTML";
+		doctype = "-//W3C//DTD HTML 4.01//EN";
+		dtd = "http://www.w3.org/TR/html4/strict.dtd";
+		break;
+	default:
+		name = "html";
+		doctype = "-//W3C//DTD XHTML 1.0 Strict//EN";
+		dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
+		break;
+	}
+
+	printf("<!DOCTYPE %s PUBLIC \"%s\" \"%s\">\n", 
+			name, doctype, dtd);
 }
 
 
 void
-print_text(struct html *h, const char *p)
+print_text(struct html *h, const char *word)
 {
 
-	if (*p && 0 == *(p + 1))
-		switch (*p) {
+	if (word[0] && '\0' == word[1])
+		switch (word[0]) {
 		case('.'):
 			/* FALLTHROUGH */
 		case(','):
@@ -360,8 +530,6 @@ print_text(struct html *h, const char *p)
 		case(')'):
 			/* FALLTHROUGH */
 		case(']'):
-			/* FALLTHROUGH */
-		case('}'):
 			if ( ! (HTML_IGNDELIM & h->flags))
 				h->flags |= HTML_NOSPACE;
 			break;
@@ -369,22 +537,30 @@ print_text(struct html *h, const char *p)
 			break;
 		}
 
-	if ( ! (h->flags & HTML_NOSPACE))
-		putchar(' ');
-
-	h->flags &= ~HTML_NEWLINE;
-
-	assert(p);
-	if ( ! print_encode(h, p))
-		h->flags &= ~HTML_NOSPACE;
+	if ( ! (HTML_NOSPACE & h->flags)) {
+		/* Manage keeps! */
+		if ( ! (HTML_KEEP & h->flags)) {
+			if (HTML_PREKEEP & h->flags)
+				h->flags |= HTML_KEEP;
+			putchar(' ');
+		} else
+			printf("&#160;");
+	}
 
-	if (*p && 0 == *(p + 1))
-		switch (*p) {
+	assert(word);
+	if ( ! print_encode(h, word, 0))
+		if ( ! (h->flags & HTML_NONOSPACE))
+			h->flags &= ~HTML_NOSPACE;
+
+	/* 
+	 * Note that we don't process the pipe: the parser sees it as
+	 * punctuation, but we don't in terms of typography.
+	 */
+	if (word[0] && '\0' == word[1])
+		switch (word[0]) {
 		case('('):
 			/* FALLTHROUGH */
 		case('['):
-			/* FALLTHROUGH */
-		case('{'):
 			h->flags |= HTML_NOSPACE;
 			break;
 		default:
@@ -399,6 +575,8 @@ print_tagq(struct html *h, const struct tag *until)
 	struct tag	*tag;
 
 	while ((tag = h->tags.head) != NULL) {
+		if (tag == h->metaf)
+			h->metaf = NULL;
 		print_ctag(h, tag->tag);
 		h->tags.head = tag->next;
 		free(tag);
@@ -416,6 +594,8 @@ print_stagq(struct html *h, const struct tag *suntil)
 	while ((tag = h->tags.head) != NULL) {
 		if (suntil && tag == suntil)
 			return;
+		if (tag == h->metaf)
+			h->metaf = NULL;
 		print_ctag(h, tag->tag);
 		h->tags.head = tag->next;
 		free(tag);
@@ -572,11 +752,11 @@ bufcat_su(struct html *h, const char *p, const struct roffsu *su)
 		break;
 	}
 
-	if (su->pt)
-		buffmt(h, "%s: %f%s;", p, v, u);
-	else
-		/* LINTED */
-		buffmt(h, "%s: %d%s;", p, (int)v, u);
+	/* 
+	 * XXX: the CSS spec isn't clear as to which types accept
+	 * integer or real numbers, so we just make them all decimals.
+	 */
+	buffmt(h, "%s: %.2f%s;", p, v, u);
 }