/* $Id: mdoc_html.c,v 1.240 2016/01/08 17:48:09 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* 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.
*/
#include "config.h"
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mandoc_aux.h"
#include "roff.h"
#include "mdoc.h"
#include "out.h"
#include "html.h"
#include "main.h"
struct roff_node *n, \
struct html *h
#ifndef MIN
#endif
struct htmlmdoc {
};
static void print_mdoc_head(MDOC_ARGS);
static void print_mdoc_node(MDOC_ARGS);
static void print_mdoc_nodelist(MDOC_ARGS);
static void synopsis_pre(struct html *,
const struct roff_node *);
static void mdoc_root_post(MDOC_ARGS);
static int mdoc_root_pre(MDOC_ARGS);
static void mdoc__x_post(MDOC_ARGS);
static int mdoc__x_pre(MDOC_ARGS);
static int mdoc_ad_pre(MDOC_ARGS);
static int mdoc_an_pre(MDOC_ARGS);
static int mdoc_ap_pre(MDOC_ARGS);
static int mdoc_ar_pre(MDOC_ARGS);
static int mdoc_bd_pre(MDOC_ARGS);
static int mdoc_bf_pre(MDOC_ARGS);
static void mdoc_bk_post(MDOC_ARGS);
static int mdoc_bk_pre(MDOC_ARGS);
static int mdoc_bl_pre(MDOC_ARGS);
static int mdoc_bt_pre(MDOC_ARGS);
static int mdoc_bx_pre(MDOC_ARGS);
static int mdoc_cd_pre(MDOC_ARGS);
static int mdoc_d1_pre(MDOC_ARGS);
static int mdoc_dv_pre(MDOC_ARGS);
static int mdoc_fa_pre(MDOC_ARGS);
static int mdoc_fd_pre(MDOC_ARGS);
static int mdoc_fl_pre(MDOC_ARGS);
static int mdoc_fn_pre(MDOC_ARGS);
static int mdoc_ft_pre(MDOC_ARGS);
static int mdoc_em_pre(MDOC_ARGS);
static void mdoc_eo_post(MDOC_ARGS);
static int mdoc_eo_pre(MDOC_ARGS);
static int mdoc_er_pre(MDOC_ARGS);
static int mdoc_ev_pre(MDOC_ARGS);
static int mdoc_ex_pre(MDOC_ARGS);
static void mdoc_fo_post(MDOC_ARGS);
static int mdoc_fo_pre(MDOC_ARGS);
static int mdoc_ic_pre(MDOC_ARGS);
static int mdoc_igndelim_pre(MDOC_ARGS);
static int mdoc_in_pre(MDOC_ARGS);
static int mdoc_it_pre(MDOC_ARGS);
static int mdoc_lb_pre(MDOC_ARGS);
static int mdoc_li_pre(MDOC_ARGS);
static int mdoc_lk_pre(MDOC_ARGS);
static int mdoc_mt_pre(MDOC_ARGS);
static int mdoc_ms_pre(MDOC_ARGS);
static int mdoc_nd_pre(MDOC_ARGS);
static int mdoc_nm_pre(MDOC_ARGS);
static int mdoc_no_pre(MDOC_ARGS);
static int mdoc_ns_pre(MDOC_ARGS);
static int mdoc_pa_pre(MDOC_ARGS);
static void mdoc_pf_post(MDOC_ARGS);
static int mdoc_pp_pre(MDOC_ARGS);
static void mdoc_quote_post(MDOC_ARGS);
static int mdoc_quote_pre(MDOC_ARGS);
static int mdoc_rs_pre(MDOC_ARGS);
static int mdoc_rv_pre(MDOC_ARGS);
static int mdoc_sh_pre(MDOC_ARGS);
static int mdoc_skip_pre(MDOC_ARGS);
static int mdoc_sm_pre(MDOC_ARGS);
static int mdoc_sp_pre(MDOC_ARGS);
static int mdoc_ss_pre(MDOC_ARGS);
static int mdoc_sx_pre(MDOC_ARGS);
static int mdoc_sy_pre(MDOC_ARGS);
static int mdoc_ud_pre(MDOC_ARGS);
static int mdoc_va_pre(MDOC_ARGS);
static int mdoc_vt_pre(MDOC_ARGS);
static int mdoc_xr_pre(MDOC_ARGS);
static int mdoc_xx_pre(MDOC_ARGS);
};
NULL,
"list-bul",
"list-col",
"list-dash",
"list-diag",
"list-enum",
"list-hang",
"list-hyph",
"list-inset",
"list-item",
"list-ohang",
"list-tag"
};
/*
* Calculate the scaling unit passed in a `-width' argument. This uses
* either a native scaling unit (e.g., 1i, 2m) or the string length of
* the value.
*/
static void
{
}
/*
* See the same function in mdoc_term.c for documentation.
*/
static void
{
return;
return;
}
case MDOC_Fd:
case MDOC_Fn:
case MDOC_Fo:
case MDOC_In:
case MDOC_Vt:
print_paragraph(h);
break;
case MDOC_Ft:
print_paragraph(h);
break;
}
/* FALLTHROUGH */
default:
break;
}
}
void
{
struct html *h;
if ( ! (HTML_FRAGMENT & h->oflags)) {
print_gen_decls(h);
print_tagq(h, tt);
} else
print_tagq(h, t);
putchar('\n');
}
static void
{
print_gen_head(h);
bufinit(h);
print_text(h, h->buf);
}
static void
{
while (n != NULL) {
print_mdoc_node(meta, n, h);
n = n->next;
}
}
static void
{
int child;
struct tag *t;
child = 1;
n->flags &= ~MDOC_ENDED;
switch (n->type) {
case ROFFT_TEXT:
/* No tables in this mode... */
/*
* Make sure that if we're in a literal mode already
* (i.e., within a <PRE>) don't print the newline.
*/
if ( ! (HTML_LITERAL & h->flags))
if (MDOC_DELIMC & n->flags)
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
if (MDOC_DELIMO & n->flags)
h->flags |= HTML_NOSPACE;
return;
case ROFFT_EQN:
putchar('\n');
break;
case ROFFT_TBL:
/*
* This will take care of initialising all of the table
* state data for the first table, then tearing it down
* for the last one.
*/
return;
default:
/*
* Close out the current table, if it's open, and unset
* the "meta" table state. This will be reopened on the
* next table element.
*/
print_tblclose(h);
}
break;
}
h->flags |= HTML_PREKEEP;
}
print_stagq(h, t);
switch (n->type) {
case ROFFT_EQN:
break;
default:
break;
if (n->end != ENDBODY_NOT)
if (n->end == ENDBODY_NOSPACE)
h->flags |= HTML_NOSPACE;
break;
}
}
static void
{
print_stagq(h, tt);
print_tagq(h, t);
}
static int
{
else
else
print_text(h, title);
print_stagq(h, tt);
print_text(h, volume);
print_stagq(h, tt);
print_text(h, title);
print_tagq(h, t);
return 1;
}
static int
{
switch (n->type) {
case ROFFT_BLOCK:
return 1;
case ROFFT_BODY:
if (n->sec == SEC_AUTHORS)
return 1;
default:
break;
}
bufinit(h);
bufcat_id(h, " ");
}
if (NULL == n) {
} else
return 1;
}
static int
{
if (n->type == ROFFT_BLOCK) {
return 1;
} else if (n->type == ROFFT_BODY)
return 1;
bufinit(h);
bufcat_id(h, " ");
}
if (NULL == n) {
} else
return 1;
}
static int
{
/* `Cm' has no leading hyphen. */
return 1;
print_text(h, "\\-");
h->flags |= HTML_NOSPACE;
return 1;
}
static int
{
if (n->type != ROFFT_BODY)
return 1;
/* XXX: this tag in theory can contain block elements. */
print_text(h, "\\(em");
return 1;
}
static int
{
int len;
switch (n->type) {
case ROFFT_HEAD:
/* FALLTHROUGH */
case ROFFT_ELEM:
return 1;
case ROFFT_BODY:
return 1;
default:
break;
}
synopsis_pre(h, n);
if (n->type == ROFFT_TEXT)
bufinit(h);
PAIR_STYLE_INIT(&tag, h);
return 1;
}
static int
{
return 0;
if (h->base_man) {
} else
n = n->child;
print_text(h, n->string);
return 0;
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
h->flags |= HTML_NOSPACE;
print_text(h, ")");
return 0;
}
static int
{
h->flags |= HTML_NOSPACE;
return 1;
}
static int
{
return 1;
}
static int
{
const char *pp;
int flags;
switch (n->tok) {
case MDOC_Bsx:
break;
case MDOC_Dx:
pp = "DragonFly";
break;
case MDOC_Fx:
pp = "FreeBSD";
break;
case MDOC_Nx:
pp = "NetBSD";
break;
case MDOC_Ox:
pp = "OpenBSD";
break;
case MDOC_Ux:
pp = "UNIX";
break;
default:
return 1;
}
print_text(h, pp);
if (n->child) {
}
return 0;
}
static int
{
print_text(h, n->string);
h->flags |= HTML_NOSPACE;
print_text(h, "BSD");
} else {
print_text(h, "BSD");
return 0;
}
h->flags |= HTML_NOSPACE;
print_text(h, "-");
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
}
return 0;
}
static int
{
bufinit(h);
if (n->type == ROFFT_HEAD) {
switch (type) {
case LIST_bullet:
case LIST_dash:
case LIST_item:
case LIST_hyphen:
case LIST_enum:
return 0;
case LIST_diag:
case LIST_hang:
case LIST_inset:
case LIST_ohang:
case LIST_tag:
break;
break;
case LIST_column:
break;
default:
break;
}
} else if (n->type == ROFFT_BODY) {
switch (type) {
case LIST_bullet:
case LIST_hyphen:
case LIST_dash:
case LIST_enum:
case LIST_item:
break;
case LIST_diag:
case LIST_hang:
case LIST_inset:
case LIST_ohang:
case LIST_tag:
break;
}
break;
case LIST_column:
break;
default:
break;
}
} else {
switch (type) {
case LIST_column:
break;
default:
break;
}
}
return 1;
}
static int
{
int i;
if (n->type == ROFFT_BODY) {
return 1;
}
if (n->type == ROFFT_HEAD) {
return 0;
/*
* For each column, print out the <COL> tag with our
* suggested width. The last column gets min-width, as
* in terminal mode it auto-sizes to the width of the
* screen and we want to preserve that behaviour.
*/
bufinit(h);
else
PAIR_STYLE_INIT(&tag[0], h);
}
return 0;
}
SCALE_VS_INIT(&su, 0);
bufinit(h);
PAIR_STYLE_INIT(&tag[0], h);
/* Set the block's left-hand margin. */
}
case LIST_bullet:
case LIST_dash:
case LIST_hyphen:
case LIST_item:
break;
case LIST_enum:
break;
case LIST_diag:
case LIST_hang:
case LIST_inset:
case LIST_ohang:
case LIST_tag:
break;
case LIST_column:
break;
default:
abort();
}
return 1;
}
static int
{
struct tag *t;
if (n->prev)
print_text(h, "The");
print_tagq(h, t);
continue;
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
print_text(h, "and");
}
print_text(h, "utilities exit\\~0");
else
print_text(h, "utility exits\\~0");
print_text(h, "on success, and\\~>0 if an error occurs.");
return 0;
}
static int
{
return 1;
}
static int
{
if (n->type != ROFFT_BLOCK)
return 1;
SCALE_VS_INIT(&su, 0);
bufinit(h);
PAIR_STYLE_INIT(&tag[0], h);
/* BLOCKQUOTE needs a block body. */
}
return 1;
}
static int
{
bufinit(h);
bufcat(h, "#");
for (n = n->child; n; ) {
bufcat_id(h, " ");
}
return 1;
}
static int
{
if (n->type == ROFFT_HEAD)
return 0;
if (n->type == ROFFT_BLOCK) {
continue;
comp = 1;
break;
}
if ( ! comp)
print_paragraph(h);
return 1;
}
/* Handle the -offset argument. */
SCALE_HS_INIT(&su, 0);
else
bufinit(h);
PAIR_STYLE_INIT(&tag[0], h);
return 1;
}
/* This can be recursive: save & set our literal state. */
h->flags |= HTML_LITERAL;
/*
* If the printed node flushes its own line, then we
* needn't do it here as well. This is hacky, but the
* notion of selective eoln whitespace is pretty dumb
* anyway, so don't sweat it.
*/
case MDOC_Sm:
case MDOC_br:
case MDOC_sp:
case MDOC_Bl:
case MDOC_D1:
case MDOC_Dl:
case MDOC_Lp:
case MDOC_Pp:
continue;
default:
break;
}
if (h->flags & HTML_NONEWLINE ||
continue;
print_text(h, "\n");
h->flags |= HTML_NOSPACE;
}
if (0 == sv)
h->flags &= ~HTML_LITERAL;
return 0;
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
h->flags &= ~HTML_NOSPLIT;
h->flags |= HTML_SPLIT;
return 0;
}
h->flags &= ~HTML_SPLIT;
h->flags |= HTML_NOSPLIT;
return 0;
}
if (h->flags & HTML_SPLIT)
h->flags |= HTML_SPLIT;
return 1;
}
static int
{
synopsis_pre(h, n);
return 1;
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
struct tag *t;
return 1;
}
print_tagq(h, t);
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
}
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
return 0;
}
static int
{
int i;
struct tag *t;
synopsis_pre(h, n);
return 0;
return 1;
}
print_text(h, n->string);
/*
* XXX This is broken and not easy to fix.
* When using -Oincludes, truncation may occur.
* Dynamic allocation wouldn't help because
* passing long strings to buffmt_includes()
* does not work either.
*/
i = 1;
if (h->base_includes) {
buffmt_includes(h, buf);
i++;
}
print_text(h, n->string);
print_tagq(h, t);
n = n->next;
}
for ( ; n; n = n->next) {
print_text(h, n->string);
}
return 0;
}
static int
{
if (n->type == ROFFT_BLOCK) {
synopsis_pre(h, n);
return 1;
} else if (n->type == ROFFT_ELEM) {
synopsis_pre(h, n);
} else if (n->type == ROFFT_HEAD)
return 0;
return 1;
}
static int
{
synopsis_pre(h, n);
return 1;
}
static int
{
struct tag *t;
synopsis_pre(h, n);
/* Split apart into type and name. */
while (ep) {
print_text(h, nbuf);
}
print_tagq(h, t);
}
/*
* FIXME: only refer to IDs that we know exist.
*/
#if 0
if (MDOC_SYNPRETTY & n->flags) {
nbuf[0] = '\0';
} else {
}
#endif
if (sp)
print_text(h, sp);
print_tagq(h, t);
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
bufinit(h);
i = 1;
if (MDOC_SYNPRETTY & n->flags)
i = 2;
print_text(h, n->string);
print_tagq(h, t);
if (n->next) {
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
}
h->flags |= HTML_NOSPACE;
print_text(h, ")");
if (pretty) {
h->flags |= HTML_NOSPACE;
print_text(h, ";");
}
return 0;
}
static int
{
h->flags ^= HTML_NONOSPACE;
h->flags &= ~HTML_NONOSPACE;
else
h->flags |= HTML_NONOSPACE;
if ( ! (HTML_NONOSPACE & h->flags))
h->flags &= ~HTML_NOSPACE;
return 0;
}
static int
{
return 0;
}
static int
{
print_paragraph(h);
return 0;
}
static int
{
}
} else
bufinit(h);
PAIR_STYLE_INIT(&tag, h);
/* So the div isn't empty: */
print_text(h, "\\~");
return 0;
}
static int
{
return 0;
print_text(h, n->string);
print_text(h, n->string);
return 0;
}
static int
{
struct tag *t;
bufinit(h);
bufcat(h, "mailto:");
print_text(h, n->string);
print_tagq(h, t);
}
return 0;
}
static int
{
struct tag *t;
if (n->type == ROFFT_BODY) {
h->flags |= HTML_NOSPACE;
print_text(h, "(");
h->flags |= HTML_NOSPACE;
return 1;
} else if (n->type == ROFFT_BLOCK) {
synopsis_pre(h, n);
return 1;
}
return 0;
print_tagq(h, t);
return 0;
}
static void
{
if (n->type != ROFFT_BODY)
return;
h->flags |= HTML_NOSPACE;
print_text(h, ")");
h->flags |= HTML_NOSPACE;
print_text(h, ";");
}
static int
{
struct tag *t;
int i;
synopsis_pre(h, n);
/*
* The first argument of the `In' gets special treatment as
* being a linked value. Subsequent values are printed
* afterward. groff does similarly. This also handles the case
* of no children.
*/
print_text(h, "#include");
print_text(h, "<");
h->flags |= HTML_NOSPACE;
i = 1;
if (h->base_includes) {
buffmt_includes(h, n->string);
i++;
}
print_text(h, n->string);
print_tagq(h, t);
n = n->next;
}
h->flags |= HTML_NOSPACE;
print_text(h, ">");
for ( ; n; n = n->next) {
print_text(h, n->string);
}
return 0;
}
static int
{
return 1;
}
static int
{
struct tag *t;
if (n->prev)
print_text(h, "The");
print_tagq(h, t);
h->flags |= HTML_NOSPACE;
print_text(h, "()");
continue;
h->flags |= HTML_NOSPACE;
print_text(h, ",");
}
print_text(h, "and");
}
print_text(h, "functions return");
else
print_text(h, "function returns");
print_text(h, "the value\\~0 if successful;");
} else
print_text(h, "Upon successful completion,"
" the value\\~0 is returned;");
print_text(h, "otherwise the value\\~\\-1 is returned"
" and the global variable");
print_text(h, "errno");
print_tagq(h, t);
print_text(h, "is set to indicate the error.");
return 0;
}
static int
{
return 1;
}
static int
{
h->flags |= HTML_NOSPACE;
print_text(h, "\\(aq");
h->flags |= HTML_NOSPACE;
return 1;
}
static int
{
if (n->type == ROFFT_HEAD)
return 0;
else if (n->type != ROFFT_BODY)
return 1;
else
/*
* We want this to be inline-formatted, but needs to be div to
* accept block children.
*/
bufinit(h);
/* Needs a left-margin for spacing. */
return 1;
}
static int
{
return 1;
}
static int
{
h->flags |= HTML_IGNDELIM;
return 1;
}
static void
{
h->flags |= HTML_NOSPACE;
}
static int
{
if (n->type != ROFFT_BLOCK)
return 1;
print_paragraph(h);
return 1;
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
return 1;
}
static int
{
print_text(h, "is currently in beta test.");
return 0;
}
static int
{
print_text(h, "currently under development.");
return 0;
}
static int
{
return 1;
}
static int
{
enum htmltag t;
t = TAG_SPAN;
switch (n->tok) {
case MDOC__A:
print_text(h, "and");
break;
case MDOC__B:
t = TAG_I;
break;
case MDOC__C:
break;
case MDOC__D:
break;
case MDOC__I:
t = TAG_I;
break;
case MDOC__J:
t = TAG_I;
break;
case MDOC__N:
break;
case MDOC__O:
break;
case MDOC__P:
break;
case MDOC__Q:
break;
case MDOC__R:
break;
case MDOC__T:
break;
case MDOC__U:
break;
case MDOC__V:
break;
default:
abort();
}
return 1;
}
return 1;
}
static void
{
return;
/* TODO: %U */
return;
h->flags |= HTML_NOSPACE;
}
static int
{
switch (n->type) {
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
return 0;
case ROFFT_BODY:
h->flags |= HTML_PREKEEP;
break;
default:
abort();
}
return 1;
}
static void
{
if (n->type == ROFFT_BODY)
}
static int
{
if (n->type != ROFFT_BODY)
return 1;
switch (n->tok) {
case MDOC_Ao:
case MDOC_Aq:
break;
case MDOC_Bro:
case MDOC_Brq:
print_text(h, "\\(lC");
break;
case MDOC_Bo:
case MDOC_Bq:
print_text(h, "\\(lB");
break;
case MDOC_Oo:
case MDOC_Op:
print_text(h, "\\(lB");
h->flags |= HTML_NOSPACE;
break;
case MDOC_En:
return 1;
break;
case MDOC_Do:
case MDOC_Dq:
case MDOC_Qo:
case MDOC_Qq:
print_text(h, "\\(lq");
break;
case MDOC_Po:
case MDOC_Pq:
print_text(h, "(");
break;
case MDOC_Ql:
print_text(h, "\\(oq");
h->flags |= HTML_NOSPACE;
break;
case MDOC_So:
case MDOC_Sq:
print_text(h, "\\(oq");
break;
default:
abort();
}
h->flags |= HTML_NOSPACE;
return 1;
}
static void
{
return;
h->flags |= HTML_NOSPACE;
switch (n->tok) {
case MDOC_Ao:
case MDOC_Aq:
break;
case MDOC_Bro:
case MDOC_Brq:
print_text(h, "\\(rC");
break;
case MDOC_Oo:
case MDOC_Op:
case MDOC_Bo:
case MDOC_Bq:
print_text(h, "\\(rB");
break;
case MDOC_En:
h->flags &= ~HTML_NOSPACE;
else
break;
case MDOC_Qo:
case MDOC_Qq:
case MDOC_Do:
case MDOC_Dq:
print_text(h, "\\(rq");
break;
case MDOC_Po:
case MDOC_Pq:
print_text(h, ")");
break;
case MDOC_Ql:
case MDOC_So:
case MDOC_Sq:
print_text(h, "\\(cq");
break;
default:
abort();
}
}
static int
{
if (n->type != ROFFT_BODY)
return 1;
if (n->end == ENDBODY_NOT &&
print_text(h, "\\&");
h->flags |= HTML_NOSPACE;
return 1;
}
static void
{
if (n->type != ROFFT_BODY)
return;
if (n->end != ENDBODY_NOT) {
h->flags &= ~HTML_NOSPACE;
return;
}
h->flags |= HTML_NOSPACE;
else if ( ! tail)
h->flags &= ~HTML_NOSPACE;
}