=================================================================== RCS file: /cvs/mandoc/man_html.c,v retrieving revision 1.167 retrieving revision 1.186 diff -u -p -r1.167 -r1.186 --- mandoc/man_html.c 2019/01/07 07:26:29 1.167 +++ mandoc/man_html.c 2023/10/18 16:11:33 1.186 @@ -1,7 +1,7 @@ -/* $Id: man_html.c,v 1.167 2019/01/07 07:26:29 schwarze Exp $ */ +/* $Id: man_html.c,v 1.186 2023/10/18 16:11:33 schwarze Exp $ */ /* + * Copyright (c) 2013-2015,2017-2020,2022 Ingo Schwarze * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze * * 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,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * HTML formatter for man(7) used by mandoc(1). */ #include "config.h" @@ -34,7 +36,7 @@ #include "main.h" #define MAN_ARGS const struct roff_meta *man, \ - const struct roff_node *n, \ + struct roff_node *n, \ struct html *h struct man_html_act { @@ -46,6 +48,8 @@ static void print_man_head(const struct roff_meta * struct html *); static void print_man_nodelist(MAN_ARGS); static void print_man_node(MAN_ARGS); +static char list_continues(const struct roff_node *, + const struct roff_node *); static int man_B_pre(MAN_ARGS); static int man_IP_pre(MAN_ARGS); static int man_I_pre(MAN_ARGS); @@ -56,7 +60,6 @@ static int man_SH_pre(MAN_ARGS); static int man_SM_pre(MAN_ARGS); static int man_SY_pre(MAN_ARGS); static int man_UR_pre(MAN_ARGS); -static int man_abort_pre(MAN_ARGS); static int man_alt_pre(MAN_ARGS); static int man_ign_pre(MAN_ARGS); static int man_in_pre(MAN_ARGS); @@ -71,9 +74,9 @@ static const struct man_html_act man_html_acts[MAN_MAX { man_SH_pre, NULL }, /* SS */ { man_IP_pre, NULL }, /* TP */ { man_IP_pre, NULL }, /* TQ */ - { man_abort_pre, NULL }, /* LP */ + { man_PP_pre, NULL }, /* LP */ { man_PP_pre, NULL }, /* PP */ - { man_abort_pre, NULL }, /* P */ + { man_PP_pre, NULL }, /* P */ { man_IP_pre, NULL }, /* IP */ { man_PP_pre, NULL }, /* HP */ { man_SM_pre, NULL }, /* SM */ @@ -119,16 +122,16 @@ html_man(void *arg, const struct roff_meta *man) if ((h->oflags & HTML_FRAGMENT) == 0) { print_gen_decls(h); print_otag(h, TAG_HTML, ""); - if (n->type == ROFFT_COMMENT) - print_gen_comment(h, n); t = print_otag(h, TAG_HEAD, ""); print_man_head(man, h); print_tagq(h, t); + if (n != NULL && n->type == ROFFT_COMMENT) + print_gen_comment(h, n); print_otag(h, TAG_BODY, ""); } man_root_pre(man, h); - t = print_otag(h, TAG_DIV, "c", "manual-text"); + t = print_otag(h, TAG_MAIN, "c", "manual-text"); print_man_nodelist(man, n, h); print_tagq(h, t); man_root_post(man, h); @@ -165,13 +168,14 @@ print_man_node(MAN_ARGS) if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + if ((n->flags & NODE_NOFILL) == 0) + html_fillmode(h, ROFF_fi); + else if (html_fillmode(h, ROFF_nf) == ROFF_nf && + n->tok != ROFF_fi && n->flags & NODE_LINE && + (n->prev == NULL || n->prev->tok != MAN_YS)) + print_endline(h); child = 1; - t = h->tag; - if (t->tag == TAG_P || t->tag == TAG_PRE) - t = t->next; - switch (n->type) { case ROFFT_TEXT: if (*n->string == '\0') { @@ -180,12 +184,16 @@ print_man_node(MAN_ARGS) } if (*n->string == ' ' && n->flags & NODE_LINE && (h->flags & HTML_NONEWLINE) == 0) - print_endline(h); + print_otag(h, TAG_BR, ""); else if (n->flags & NODE_DELIMC) h->flags |= HTML_NOSPACE; + t = h->tag; + t->refcnt++; print_text(h, n->string); break; case ROFFT_EQN: + t = h->tag; + t->refcnt++; print_eqn(h, n->eqn); break; case ROFFT_TBL: @@ -201,9 +209,9 @@ print_man_node(MAN_ARGS) * Close out scope of font prior to opening a macro * scope. */ - if (HTMLFONT_NONE != h->metac) { + if (h->metac != ESCAPE_FONTROMAN) { h->metal = h->metac; - h->metac = HTMLFONT_NONE; + h->metac = ESCAPE_FONTROMAN; } /* @@ -211,12 +219,13 @@ print_man_node(MAN_ARGS) * the "meta" table state. This will be reopened on the * next table element. */ - if (h->tblt != NULL) { + if (h->tblt != NULL) print_tblclose(h); - t = h->tag; - } + t = h->tag; + t->refcnt++; if (n->tok < ROFF_MAX) { roff_html_pre(h, n); + t->refcnt--; print_stagq(h, t); return; } @@ -231,39 +240,48 @@ print_man_node(MAN_ARGS) print_man_nodelist(man, n->child, h); /* This will automatically close out any font scope. */ - print_stagq(h, t); - - if (n->flags & NODE_NOFILL && n->tok != MAN_YS && - (n->next != NULL && n->next->flags & NODE_LINE)) { - /* In .nf =
, print even empty lines. */
-		h->col++;
-		print_endline(h);
+	t->refcnt--;
+	if (n->type == ROFFT_BLOCK &&
+	    (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
+		t = h->tag;
+		while (t->tag != TAG_DL && t->tag != TAG_UL)
+			t = t->next;
+		/*
+		 * Close the list if no further item of the same type
+		 * follows; otherwise, close the item only.
+		 */
+		if (list_continues(n, roff_node_next(n)) == '\0') {
+			print_tagq(h, t);
+			t = NULL;
+		}
 	}
+	if (t != NULL)
+		print_stagq(h, t);
 }
 
 static void
 man_root_pre(const struct roff_meta *man, struct html *h)
 {
-	struct tag	*t, *tt;
+	struct tag	*t;
 	char		*title;
 
 	assert(man->title);
 	assert(man->msec);
 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
 
-	t = print_otag(h, TAG_TABLE, "c", "head");
-	tt = print_otag(h, TAG_TR, "");
+	t = print_otag(h, TAG_DIV, "cr?", "head", "doc-pageheader",
+	    "aria-label", "Manual header line");
 
-	print_otag(h, TAG_TD, "c", "head-ltitle");
+	print_otag(h, TAG_SPAN, "c", "head-ltitle");
 	print_text(h, title);
-	print_stagq(h, tt);
+	print_stagq(h, t);
 
-	print_otag(h, TAG_TD, "c", "head-vol");
+	print_otag(h, TAG_SPAN, "c", "head-vol");
 	if (man->vol != NULL)
 		print_text(h, man->vol);
-	print_stagq(h, tt);
+	print_stagq(h, t);
 
-	print_otag(h, TAG_TD, "c", "head-rtitle");
+	print_otag(h, TAG_SPAN, "c", "head-rtitle");
 	print_text(h, title);
 	print_tagq(h, t);
 	free(title);
@@ -272,16 +290,19 @@ man_root_pre(const struct roff_meta *man, struct html 
 static void
 man_root_post(const struct roff_meta *man, struct html *h)
 {
-	struct tag	*t, *tt;
+	struct tag	*t;
 
-	t = print_otag(h, TAG_TABLE, "c", "foot");
-	tt = print_otag(h, TAG_TR, "");
+	t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
+	    "aria-label", "Manual footer line");
 
-	print_otag(h, TAG_TD, "c", "foot-date");
+	print_otag(h, TAG_SPAN, "c", "foot-left");
+	print_stagq(h, t);
+
+	print_otag(h, TAG_SPAN, "c", "foot-date");
 	print_text(h, man->date);
-	print_stagq(h, tt);
+	print_stagq(h, t);
 
-	print_otag(h, TAG_TD, "c", "foot-os");
+	print_otag(h, TAG_SPAN, "c", "foot-os");
 	if (man->os != NULL)
 		print_text(h, man->os);
 	print_tagq(h, t);
@@ -290,20 +311,23 @@ man_root_post(const struct roff_meta *man, struct html
 static int
 man_SH_pre(MAN_ARGS)
 {
-	char	*id;
+	const char	*class;
+	enum htmltag	 tag;
 
+	if (n->tok == MAN_SH) {
+		tag = TAG_H2;
+		class = "Sh";
+	} else {
+		tag = TAG_H3;
+		class = "Ss";
+	}
 	switch (n->type) {
 	case ROFFT_BLOCK:
 		html_close_paragraph(h);
+		print_otag(h, TAG_SECTION, "c", class);
 		break;
 	case ROFFT_HEAD:
-		id = html_make_id(n, 1);
-		if (n->tok == MAN_SH)
-			print_otag(h, TAG_H1, "cTi", "Sh", id);
-		else
-			print_otag(h, TAG_H2, "cTi", "Ss", id);
-		if (id != NULL)
-			print_otag(h, TAG_A, "chR", "permalink", id);
+		print_otag_id(h, tag, class, n);
 		break;
 	case ROFFT_BODY:
 		break;
@@ -381,7 +405,7 @@ man_PP_pre(MAN_ARGS)
 		if (n->child != NULL &&
 		    (n->child->flags & NODE_NOFILL) == 0)
 			print_otag(h, TAG_P, "c",
-			    n->tok == MAN_PP ? "Pp" : "Pp HP");
+			    n->tok == MAN_HP ? "Pp HP" : "Pp");
 		break;
 	default:
 		abort();
@@ -389,26 +413,90 @@ man_PP_pre(MAN_ARGS)
 	return 1;
 }
 
+static char
+list_continues(const struct roff_node *n1, const struct roff_node *n2)
+{
+	const char *s1, *s2;
+	char c1, c2;
+
+	if (n1 == NULL || n1->type != ROFFT_BLOCK ||
+	    n2 == NULL || n2->type != ROFFT_BLOCK)
+		return '\0';
+	if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
+	    (n2->tok == MAN_TP || n2->tok == MAN_TQ))
+		return ' ';
+	if (n1->tok != MAN_IP || n2->tok != MAN_IP)
+		return '\0';
+	n1 = n1->head->child;
+	n2 = n2->head->child;
+	s1 = n1 == NULL ? "" : n1->string;
+	s2 = n2 == NULL ? "" : n2->string;
+	c1 = strcmp(s1, "*") == 0 ? '*' :
+	     strcmp(s1, "\\-") == 0 ? '-' :
+	     strcmp(s1, "\\(bu") == 0 ? 'b' :
+	     strcmp(s1, "\\[bu]") == 0 ? 'b' : ' ';
+	c2 = strcmp(s2, "*") == 0 ? '*' :
+	     strcmp(s2, "\\-") == 0 ? '-' :
+	     strcmp(s2, "\\(bu") == 0 ? 'b' :
+	     strcmp(s2, "\\[bu]") == 0 ? 'b' : ' ';
+	return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
+}
+
 static int
 man_IP_pre(MAN_ARGS)
 {
-	const struct roff_node	*nn;
+	struct roff_node	*nn;
+	const char		*list_class;
+	enum htmltag		 list_elem, body_elem;
+	char			 list_type;
 
+	nn = n->type == ROFFT_BLOCK ? n : n->parent;
+	list_type = list_continues(roff_node_prev(nn), nn);
+	if (list_type == '\0') {
+		/* Start a new list. */
+		list_type = list_continues(nn, roff_node_next(nn));
+		if (list_type == '\0')
+			list_type = ' ';
+		switch (list_type) {
+		case ' ':
+			list_class = "Bl-tag";
+			list_elem = TAG_DL;
+			break;
+		case '*':
+			list_class = "Bl-bullet";
+			list_elem = TAG_UL;
+			break;
+		case '-':
+			list_class = "Bl-dash";
+			list_elem = TAG_UL;
+			break;
+		default:
+			abort();
+		}
+	} else {
+		/* Continue a list that was started earlier. */
+		list_class = NULL;
+		list_elem = TAG_MAX;
+	}
+	body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
+
 	switch (n->type) {
 	case ROFFT_BLOCK:
 		html_close_paragraph(h);
-		print_otag(h, TAG_DL, "c", "Bl-tag");
+		if (list_elem != TAG_MAX)
+			print_otag(h, list_elem, "c", list_class);
 		return 1;
 	case ROFFT_HEAD:
-		print_otag(h, TAG_DT, "");
+		if (body_elem == TAG_LI)
+			return 0;
+		print_otag_id(h, TAG_DT, NULL, n);
 		break;
 	case ROFFT_BODY:
-		print_otag(h, TAG_DD, "");
+		print_otag(h, body_elem, "");
 		return 1;
 	default:
 		abort();
 	}
-
 	switch(n->tok) {
 	case MAN_IP:  /* Only print the first header element. */
 		if (n->child != NULL)
@@ -513,7 +601,7 @@ man_SY_pre(MAN_ARGS)
 		break;
 	case ROFFT_HEAD:
 		print_otag(h, TAG_TD, "");
-		print_otag(h, TAG_CODE, "cT", "Nm");
+		print_otag(h, TAG_CODE, "c", "Nm");
 		break;
 	case ROFFT_BODY:
 		print_otag(h, TAG_TD, "");
@@ -535,10 +623,10 @@ man_UR_pre(MAN_ARGS)
 		assert(n->child->type == ROFFT_TEXT);
 		if (n->tok == MAN_MT) {
 			mandoc_asprintf(&cp, "mailto:%s", n->child->string);
-			print_otag(h, TAG_A, "cTh", "Mt", cp);
+			print_otag(h, TAG_A, "ch", "Mt", cp);
 			free(cp);
 		} else
-			print_otag(h, TAG_A, "cTh", "Lk", n->child->string);
+			print_otag(h, TAG_A, "ch", "Lk", n->child->string);
 	}
 
 	assert(n->next->type == ROFFT_BODY);
@@ -547,10 +635,4 @@ man_UR_pre(MAN_ARGS)
 
 	print_man_nodelist(man, n->child, h);
 	return 0;
-}
-
-static int
-man_abort_pre(MAN_ARGS)
-{
-	abort();
 }