mdoc_html.c revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov/* $Id: mdoc_html.c,v 1.226 2015/03/03 21:11:34 schwarze Exp $ */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * copyright notice and this permission notice appear in all copies.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore#define MDOC_ARGS const struct mdoc_meta *meta, \
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore const struct mdoc_node *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void a2width(const char *, struct roffsu *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic const struct htmlmdoc mdocs[MDOC_MAX] = {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore {NULL, NULL}, /* Ec */ /* FIXME: no space */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic const char * const lists[LIST_MAX] = {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "list-inset",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "list-ohang",
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amorehtml_mdoc(void *arg, const struct mdoc *mdoc)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc)->child,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Calculate the scaling unit passed in a `-width' argument. This uses
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * either a native scaling unit (e.g., 1i, 2m) or the string length of
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * See the same function in mdoc_term.c for documentation.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoresynopsis_pre(struct html *h, const struct mdoc_node *n)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov while (n != NULL) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->type) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* No tables in this mode... */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Make sure that if we're in a literal mode already
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * (i.e., within a <PRE>) don't print the newline.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (' ' == *n->string && MDOC_LINE & n->flags)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * This will take care of initialising all of the table
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * state data for the first table, then tearing it down
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * for the last one.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Close out the current table, if it's open, and unset
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * the "meta" table state. This will be reopened on the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * next table element.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (h->flags & HTML_KEEP && n->flags & MDOC_LINE) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->type) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! mdocs[n->tok].post || n->flags & MDOC_ENDED)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov switch (n->type) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (n = n->child; n && MDOC_TEXT == n->type; ) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (n = n->child; n && MDOC_TEXT == n->type; ) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* `Cm' has no leading hyphen. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! (n->nchild == 0 &&
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* XXX: this tag in theory can contain block elements. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->type) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->tok) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * For each column, print out the <COL> tag with our
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * suggested width. The last column gets min-width, as
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * in terminal mode it auto-sizes to the width of the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * screen and we want to preserve that behaviour.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov (void)strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Set the block's left-hand margin. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov print_text(h, "on success, and\\~>0 if an error occurs.");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* BLOCKQUOTE needs a block body. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (n = n->child; n; ) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (nn = n; nn && ! comp; nn = nn->parent) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Handle the -offset argument. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* This can be recursive: save & set our literal state. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * If the printed node flushes its own line, then we
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * needn't do it here as well. This is hacky, but the
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * notion of selective eoln whitespace is pretty dumb
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * anyway, so don't sweat it.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->child && n->next && n->next->tok == MDOC_Fa) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * XXX This is broken and not easy to fix.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * When using -Oincludes, truncation may occur.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Dynamic allocation wouldn't help because
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * passing long strings to buffmt_includes()
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * does not work either.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov strlcpy(buf, '<' == *n->string || '"' == *n->string ?
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for ( ; n; n = n->next) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Split apart into type and name. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * FIXME: only refer to IDs that we know exist.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* So the div isn't empty: */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* XXX: we drop non-initial arguments as per groff. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * The first argument of the `In' gets special treatment as
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * being a linked value. Subsequent values are printed
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * afterward. groff does similarly. This also handles the case
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * of no children.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for ( ; n; n = n->next) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov " the value\\~0 is returned;");
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov print_text(h, "otherwise the value\\~\\-1 is returned"
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov " and the global variable");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * We want this to be inline-formatted, but needs to be div to
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * accept block children.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Needs a left-margin for spacing. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore print_text(h, "is currently in beta test.");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore print_text(h, "currently under development.");
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->tok) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL == n->next || MDOC__A != n->next->tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL == n->next->next || MDOC__A != n->next->next->tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL == n->prev || MDOC__A != n->prev->tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* TODO: %U */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (NULL == n->parent || MDOC_Rs != n->parent->tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->type) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->parent->args || 0 == n->prev->nchild)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->tok) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (n->tok) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov print_text(h, n->norm->Es->child->next->string);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov else if (n->end != ENDBODY_NOT ? n->child != NULL :
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n->parent->head->child != NULL && (n->child != NULL ||
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov (n->parent->tail != NULL && n->parent->tail->child != NULL)))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov body = n->child != NULL || n->parent->head->child != NULL;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov else if ( ! tail)