File: [cvsweb.bsd.lv] / docbook2mdoc / docbook2mdoc.c (download)
Revision 1.82, Wed Apr 3 11:46:09 2019 UTC (5 years, 2 months ago) by schwarze
Branch: MAIN
Changes since 1.81: +38 -12 lines
Prints tables containing only one column as .Bl -bullet -compact.
The number of columns is taken from the "cols" attribute.
No neat to treat <informaltable> separately from <table>;
the only difference is whether or not it has a title.
Treat <table> as transparent and handle <tgroup> instead.
The advantages are that <title> gets a generic handler
which also works in other contexts
and that other children of <table> are now covered as well.
|
/* $Id: docbook2mdoc.c,v 1.82 2019/04/03 11:46:09 schwarze Exp $ */
/*
* Copyright (c) 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2019 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 <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "node.h"
#include "macro.h"
#include "format.h"
/*
* The implementation of the mdoc(7) formatter.
*/
static void pnode_print(struct format *, struct pnode *);
static void
pnode_printpara(struct format *p, struct pnode *pn)
{
struct pnode *pp;
if ((pp = TAILQ_PREV(pn, pnodeq, child)) == NULL &&
(pp = pn->parent) == NULL)
return;
switch (pp->node) {
case NODE_ENTRY:
case NODE_LISTITEM:
return;
case NODE_PREFACE:
case NODE_SECTION:
if (p->level < 3)
return;
break;
default:
break;
}
macro_line(p, "Pp");
}
/*
* If the SYNOPSIS macro has a superfluous title, kill it.
*/
static void
pnode_printrefsynopsisdiv(struct format *p, struct pnode *pn)
{
struct pnode *pp, *pq;
TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq)
if (pp->node == NODE_TITLE)
pnode_unlink(pp);
macro_line(p, "Sh SYNOPSIS");
}
/*
* Start a hopefully-named `Sh' section.
*/
static void
pnode_printrefsect(struct format *p, struct pnode *pn)
{
struct pnode *pp;
const char *title;
int flags, level;
if (pn->parent == NULL)
return;
level = ++p->level;
flags = ARG_SPACE;
if (level == 1)
flags |= ARG_UPPER;
if (level < 3) {
switch (pn->node) {
case NODE_CAUTION:
case NODE_NOTE:
case NODE_TIP:
case NODE_WARNING:
level = 3;
break;
default:
break;
}
}
TAILQ_FOREACH(pp, &pn->childq, child)
if (pp->node == NODE_TITLE)
break;
if (pp == NULL) {
switch (pn->node) {
case NODE_PREFACE:
title = "Preface";
break;
case NODE_CAUTION:
title = "Caution";
break;
case NODE_NOTE:
title = "Note";
break;
case NODE_TIP:
title = "Tip";
break;
case NODE_WARNING:
title = "Warning";
break;
default:
title = "Unknown";
break;
}
}
switch (level) {
case 1:
macro_open(p, "Sh");
break;
case 2:
macro_open(p, "Ss");
break;
default:
pnode_printpara(p, pn);
macro_open(p, "Sy");
break;
}
if (pp != NULL) {
macro_addnode(p, pp, flags);
pnode_unlink(pp);
} else
macro_addarg(p, title, ARG_SPACE | ARG_QUOTED);
macro_close(p);
}
/*
* Start a reference, extracting the title and volume.
*/
static void
pnode_printciterefentry(struct format *p, struct pnode *pn)
{
struct pnode *pp, *title, *manvol;
title = manvol = NULL;
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node == NODE_MANVOLNUM)
manvol = pp;
else if (pp->node == NODE_REFENTRYTITLE)
title = pp;
}
macro_open(p, "Xr");
if (title == NULL)
macro_addarg(p, "unknown", ARG_SPACE);
else
macro_addnode(p, title, ARG_SPACE | ARG_SINGLE);
if (manvol == NULL)
macro_addarg(p, "1", ARG_SPACE);
else
macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
pnode_unlinksub(pn);
}
static void
pnode_printrefmeta(struct format *p, struct pnode *pn)
{
struct pnode *pp, *title, *manvol;
title = manvol = NULL;
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node == NODE_MANVOLNUM)
manvol = pp;
else if (pp->node == NODE_REFENTRYTITLE)
title = pp;
}
macro_open(p, "Dt");
if (title == NULL)
macro_addarg(p, "UNKNOWN", ARG_SPACE);
else
macro_addnode(p, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
if (manvol == NULL)
macro_addarg(p, "1", ARG_SPACE);
else
macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
macro_close(p);
pnode_unlink(pn);
}
static void
pnode_printfuncdef(struct format *p, struct pnode *pn)
{
struct pnode *pp, *ftype, *func;
ftype = func = NULL;
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node == NODE_TEXT)
ftype = pp;
else if (pp->node == NODE_FUNCTION)
func = pp;
}
if (ftype != NULL)
macro_argline(p, "Ft", ftype->b);
macro_open(p, "Fo");
if (func == NULL)
macro_addarg(p, "UNKNOWN", ARG_SPACE);
else
macro_addnode(p, func, ARG_SPACE | ARG_SINGLE);
macro_close(p);
}
/*
* The <mml:mfenced> node is a little peculiar.
* First, it can have arbitrary open and closing tokens, which default
* to parentheses.
* Second, >1 arguments are separated by commas.
*/
static void
pnode_printmathfenced(struct format *p, struct pnode *pn)
{
struct pnode *pp;
printf("left %s ", pnode_getattr_raw(pn, ATTRKEY_OPEN, "("));
pp = TAILQ_FIRST(&pn->childq);
pnode_print(p, pp);
while ((pp = TAILQ_NEXT(pp, child)) != NULL) {
putchar(',');
pnode_print(p, pp);
}
printf("right %s ", pnode_getattr_raw(pn, ATTRKEY_CLOSE, ")"));
pnode_unlinksub(pn);
}
/*
* These math nodes require special handling because they have infix
* syntax, instead of the usual prefix or prefix.
* So we need to break up the first and second child node with a
* particular eqn(7) word.
*/
static void
pnode_printmath(struct format *p, struct pnode *pn)
{
struct pnode *pp;
pp = TAILQ_FIRST(&pn->childq);
pnode_print(p, pp);
switch (pn->node) {
case NODE_MML_MSUP:
fputs(" sup ", stdout);
break;
case NODE_MML_MFRAC:
fputs(" over ", stdout);
break;
case NODE_MML_MSUB:
fputs(" sub ", stdout);
break;
default:
break;
}
pp = TAILQ_NEXT(pp, child);
pnode_print(p, pp);
pnode_unlinksub(pn);
}
static void
pnode_printfuncprototype(struct format *p, struct pnode *pn)
{
struct pnode *pp, *fdef;
TAILQ_FOREACH(fdef, &pn->childq, child)
if (fdef->node == NODE_FUNCDEF)
break;
if (fdef != NULL)
pnode_printfuncdef(p, fdef);
else
macro_line(p, "Fo UNKNOWN");
TAILQ_FOREACH(pp, &pn->childq, child)
if (pp->node == NODE_PARAMDEF)
macro_nodeline(p, "Fa", pp, ARG_SINGLE);
macro_line(p, "Fc");
pnode_unlinksub(pn);
}
/*
* The <arg> element is more complicated than it should be because text
* nodes are treated like ".Ar foo", but non-text nodes need to be
* re-sent into the printer (i.e., without the preceding ".Ar").
* This also handles the case of "repetition" (or in other words, the
* ellipsis following an argument) and optionality.
*/
static void
pnode_printarg(struct format *p, struct pnode *pn)
{
struct pnode *pp;
struct pattr *ap;
int isop, isrep;
isop = 1;
isrep = 0;
TAILQ_FOREACH(ap, &pn->attrq, child) {
if (ap->key == ATTRKEY_CHOICE &&
(ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ))
isop = 0;
else if (ap->key == ATTRKEY_REP && ap->val == ATTRVAL_REPEAT)
isrep = 1;
}
if (isop)
macro_open(p, "Op");
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node == NODE_TEXT)
macro_open(p, "Ar");
pnode_print(p, pp);
if (isrep && pp->node == NODE_TEXT)
macro_addarg(p, "...", ARG_SPACE);
}
pnode_unlinksub(pn);
}
static void
pnode_printgroup(struct format *p, struct pnode *pn)
{
struct pnode *pp, *np;
struct pattr *ap;
int isop, sv;
isop = 1;
TAILQ_FOREACH(ap, &pn->attrq, child)
if (ap->key == ATTRKEY_CHOICE &&
(ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ)) {
isop = 0;
break;
}
/*
* Make sure we're on a macro line.
* This will prevent pnode_print() for putting us on a
* subsequent line.
*/
sv = p->linestate == LINE_NEW;
if (isop)
macro_open(p, "Op");
else if (sv)
macro_open(p, "No");
/*
* Keep on printing text separated by the vertical bar as long
* as we're within the same origin node as the group.
* This is kind of a nightmare.
* Eh, DocBook...
* FIXME: if there's a "Fl", we don't cut off the leading "-"
* like we do in pnode_print().
*/
TAILQ_FOREACH(pp, &pn->childq, child) {
pnode_print(p, pp);
np = TAILQ_NEXT(pp, child);
while (np != NULL) {
if (pp->node != np->node)
break;
macro_addarg(p, "|", ARG_SPACE);
macro_addnode(p, np, ARG_SPACE);
pp = np;
np = TAILQ_NEXT(np, child);
}
}
if (sv)
macro_close(p);
pnode_unlinksub(pn);
}
static void
pnode_printauthor(struct format *f, struct pnode *n)
{
struct pnode *nc, *ncn;
int have_contrib, have_name;
/*
* Print <contrib> children up front, before the .An scope,
* and figure out whether we a name of a person.
*/
have_contrib = have_name = 0;
TAILQ_FOREACH_SAFE(nc, &n->childq, child, ncn) {
switch (nc->node) {
case NODE_CONTRIB:
if (have_contrib)
print_text(f, ",", 0);
print_textnode(f, nc);
pnode_unlink(nc);
have_contrib = 1;
break;
case NODE_PERSONNAME:
have_name = 1;
break;
default:
break;
}
}
if (TAILQ_FIRST(&n->childq) == NULL)
return;
if (have_contrib)
print_text(f, ":", 0);
/*
* If we have a name, print it in the .An scope and leave
* all other content for child handlers, to print after the
* scope. Otherwise, print everything in the scope.
*/
macro_open(f, "An");
TAILQ_FOREACH_SAFE(nc, &n->childq, child, ncn) {
if (nc->node == NODE_PERSONNAME || have_name == 0) {
macro_addnode(f, nc, ARG_SPACE);
pnode_unlink(nc);
}
}
/*
* If there is an email address,
* print it on the same macro line.
*/
if ((nc = pnode_findfirst(n, NODE_EMAIL)) != NULL) {
pnode_print(f, nc);
pnode_unlink(nc);
}
/*
* If there are still unprinted children, end the scope
* with a comma. Otherwise, leave the scope open in case
* a text node follows that starts with closing punctuation.
*/
if (TAILQ_FIRST(&n->childq) != NULL) {
macro_addarg(f, ",", ARG_SPACE);
macro_close(f);
}
}
static void
pnode_printprologue(struct format *p, struct ptree *tree)
{
struct pnode *refmeta;
refmeta = tree->root == NULL ? NULL :
pnode_findfirst(tree->root, NODE_REFMETA);
macro_line(p, "Dd $Mdocdate" "$");
if (refmeta == NULL) {
macro_open(p, "Dt");
macro_addarg(p,
pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"),
ARG_SPACE | ARG_SINGLE | ARG_UPPER);
macro_addarg(p, "1", ARG_SPACE);
macro_close(p);
} else
pnode_printrefmeta(p, refmeta);
macro_line(p, "Os");
if (tree->flags & TREE_EQN) {
macro_line(p, "EQ");
print_text(p, "delim $$", 0);
macro_line(p, "EN");
}
}
/*
* We can have multiple <term> elements within a <varlistentry>, which
* we should comma-separate as list headers.
*/
static void
pnode_printvarlistentry(struct format *p, struct pnode *pn)
{
struct pnode *pp;
int first = 1;
macro_open(p, "It");
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node != NODE_TERM)
continue;
if ( ! first)
macro_addarg(p, ",", 0);
pnode_print(p, pp);
first = 0;
}
macro_close(p);
TAILQ_FOREACH(pp, &pn->childq, child)
if (pp->node != NODE_TERM)
pnode_print(p, pp);
pnode_unlinksub(pn);
}
static void
pnode_printtitle(struct format *p, struct pnode *pn)
{
struct pnode *pp, *pq;
TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq) {
if (pp->node == NODE_TITLE) {
pnode_printpara(p, pp);
pnode_print(p, pp);
pnode_unlink(pp);
}
}
}
static void
pnode_printrow(struct format *p, struct pnode *pn)
{
struct pnode *pp;
macro_line(p, "Bl -dash -compact");
TAILQ_FOREACH(pp, &pn->childq, child) {
macro_line(p, "It");
pnode_print(p, pp);
}
macro_line(p, "El");
pnode_unlink(pn);
}
static void
pnode_printtgroup1(struct format *p, struct pnode *n)
{
struct pnode *nc;
macro_line(p, "Bl -bullet -compact");
while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
macro_line(p, "It");
pnode_print(p, nc);
pnode_unlink(nc);
}
macro_line(p, "El");
pnode_unlinksub(n);
}
static void
pnode_printtgroup(struct format *p, struct pnode *n)
{
struct pnode *nc;
switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
case 1:
pnode_printtgroup1(p, n);
return;
default:
break;
}
macro_line(p, "Bl -ohang");
while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
macro_line(p, "It Table Row");
pnode_printrow(p, nc);
}
macro_line(p, "El");
pnode_unlinksub(n);
}
static void
pnode_printlist(struct format *p, struct pnode *pn)
{
struct pnode *pp;
pnode_printtitle(p, pn);
macro_argline(p, "Bl",
pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
TAILQ_FOREACH(pp, &pn->childq, child) {
macro_line(p, "It");
pnode_print(p, pp);
}
macro_line(p, "El");
pnode_unlinksub(pn);
}
static void
pnode_printvariablelist(struct format *p, struct pnode *pn)
{
struct pnode *pp;
pnode_printtitle(p, pn);
macro_line(p, "Bl -tag -width Ds");
TAILQ_FOREACH(pp, &pn->childq, child) {
if (pp->node == NODE_VARLISTENTRY)
pnode_print(p, pp);
else
macro_nodeline(p, "It", pp, 0);
}
macro_line(p, "El");
pnode_unlinksub(pn);
}
/*
* Print a parsed node (or ignore it--whatever).
* This is a recursive function.
* FIXME: if we're in a literal context (<screen> or <programlisting> or
* whatever), don't print inline macros.
*/
static void
pnode_print(struct format *p, struct pnode *pn)
{
struct pnode *pp;
const char *ccp;
char *cp;
int last;
enum linestate sv;
if (pn == NULL)
return;
sv = p->linestate;
switch (pn->node) {
case NODE_APPLICATION:
macro_open(p, "Nm");
break;
case NODE_ARG:
pnode_printarg(p, pn);
break;
case NODE_AUTHOR:
pnode_printauthor(p, pn);
break;
case NODE_AUTHORGROUP:
macro_line(p, "An -split");
break;
case NODE_BOOKINFO:
macro_line(p, "Sh NAME");
break;
case NODE_CITEREFENTRY:
pnode_printciterefentry(p, pn);
break;
case NODE_CITETITLE:
macro_open(p, "%T");
break;
case NODE_CODE:
macro_open(p, "Li");
break;
case NODE_COMMAND:
macro_open(p, "Nm");
break;
case NODE_CONSTANT:
macro_open(p, "Dv");
break;
case NODE_EDITOR:
print_text(p, "editor:", ARG_SPACE);
macro_open(p, "An");
break;
case NODE_EMAIL:
macro_open(p, "Aq Mt");
break;
case NODE_EMPHASIS:
case NODE_FIRSTTERM:
macro_open(p, "Em");
break;
case NODE_ENVAR:
macro_open(p, "Ev");
break;
case NODE_ESCAPE:
if (p->linestate == LINE_NEW)
p->linestate = LINE_TEXT;
else
putchar(' ');
fputs(pn->b, stdout);
break;
case NODE_FILENAME:
macro_open(p, "Pa");
break;
case NODE_FUNCTION:
macro_open(p, "Fn");
break;
case NODE_FUNCPROTOTYPE:
pnode_printfuncprototype(p, pn);
break;
case NODE_FUNCSYNOPSISINFO:
macro_open(p, "Fd");
break;
case NODE_INFORMALEQUATION:
macro_line(p, "EQ");
break;
case NODE_INLINEEQUATION:
if (p->linestate == LINE_NEW)
p->linestate = LINE_TEXT;
putchar('$');
break;
case NODE_ITEMIZEDLIST:
pnode_printlist(p, pn);
break;
case NODE_GROUP:
pnode_printgroup(p, pn);
break;
case NODE_KEYSYM:
macro_open(p, "Sy");
break;
case NODE_LEGALNOTICE:
macro_line(p, "Sh LEGAL NOTICE");
break;
case NODE_LINK:
ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
if (ccp == NULL)
break;
macro_argline(p, "Sx", ccp);
return;
case NODE_LITERAL:
macro_open(p, "Li");
break;
case NODE_LITERALLAYOUT:
macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
break;
case NODE_MML_MFENCED:
pnode_printmathfenced(p, pn);
break;
case NODE_MML_MROW:
case NODE_MML_MI:
case NODE_MML_MN:
case NODE_MML_MO:
if (TAILQ_EMPTY(&pn->childq))
break;
fputs(" { ", stdout);
break;
case NODE_MML_MFRAC:
case NODE_MML_MSUB:
case NODE_MML_MSUP:
pnode_printmath(p, pn);
break;
case NODE_OPTION:
macro_open(p, "Fl");
break;
case NODE_ORDEREDLIST:
pnode_printlist(p, pn);
break;
case NODE_PARA:
pnode_printpara(p, pn);
break;
case NODE_PARAMETER:
macro_nodeline(p, "Fa", pn, ARG_SINGLE);
pnode_unlinksub(pn);
break;
case NODE_QUOTE:
macro_open(p, "Qo");
break;
case NODE_PROGRAMLISTING:
case NODE_SCREEN:
macro_line(p, "Bd -literal");
break;
case NODE_REFENTRYINFO:
/* Suppress. */
pnode_unlinksub(pn);
break;
case NODE_REFMETA:
abort();
break;
case NODE_REFNAME:
/* Suppress non-text children... */
macro_open(p, "Nm");
macro_addnode(p, pn, ARG_SPACE | ARG_SINGLE);
pnode_unlinksub(pn);
break;
case NODE_REFNAMEDIV:
macro_line(p, "Sh NAME");
break;
case NODE_REFPURPOSE:
macro_open(p, "Nd");
break;
case NODE_REFSYNOPSISDIV:
pnode_printrefsynopsisdiv(p, pn);
break;
case NODE_PREFACE:
case NODE_SECTION:
case NODE_NOTE:
case NODE_TIP:
case NODE_CAUTION:
case NODE_WARNING:
pnode_printrefsect(p, pn);
break;
case NODE_REPLACEABLE:
macro_open(p, "Ar");
break;
case NODE_SBR:
macro_line(p, "br");
break;
case NODE_SGMLTAG:
macro_open(p, "Li");
break;
case NODE_STRUCTNAME:
macro_open(p, "Vt");
break;
case NODE_TEXT:
if (pn->bsz == 0) {
assert(pn->real != pn->b);
break;
}
if (p->linestate == LINE_NEW)
p->linestate = LINE_TEXT;
else
putchar(' ');
/*
* Output all characters, squeezing out whitespace
* between newlines.
* XXX: all whitespace, including tabs (?).
* Remember to escape control characters and escapes.
*/
cp = pn->b;
/*
* There's often a superfluous "-" in its <option> tags
* before the actual flags themselves.
* "Fl" does this for us, so remove it.
*/
if (pn->parent != NULL &&
pn->parent->node == NODE_OPTION &&
*cp == '-')
cp++;
for (last = '\n'; *cp != '\0'; ) {
if (last == '\n') {
/* Consume all whitespace. */
if (isspace((unsigned char)*cp)) {
while (isspace((unsigned char)*cp))
cp++;
continue;
} else if (*cp == '\'' || *cp == '.')
fputs("\\&", stdout);
}
putchar(last = *cp++);
/* If we're a character escape, escape us. */
if (last == '\\')
putchar('e');
}
break;
case NODE_TGROUP:
pnode_printtgroup(p, pn);
break;
case NODE_TITLE:
if (pn->parent->node == NODE_BOOKINFO) {
macro_open(p, "Nd");
break;
}
pnode_printpara(p, pn);
macro_nodeline(p, "Sy", pn, 0);
pnode_unlinksub(pn);
break;
case NODE_TYPE:
macro_open(p, "Vt");
break;
case NODE_USERINPUT:
macro_open(p, "Li");
break;
case NODE_VARIABLELIST:
pnode_printvariablelist(p, pn);
break;
case NODE_VARLISTENTRY:
pnode_printvarlistentry(p, pn);
break;
case NODE_VARNAME:
macro_open(p, "Va");
break;
default:
break;
}
TAILQ_FOREACH(pp, &pn->childq, child)
pnode_print(p, pp);
switch (pn->node) {
case NODE_INFORMALEQUATION:
macro_line(p, "EN");
break;
case NODE_INLINEEQUATION:
fputs("$ ", stdout);
p->linestate = sv;
break;
case NODE_MML_MROW:
case NODE_MML_MI:
case NODE_MML_MN:
case NODE_MML_MO:
if (TAILQ_EMPTY(&pn->childq))
break;
fputs(" } ", stdout);
break;
case NODE_APPLICATION:
case NODE_ARG:
case NODE_AUTHOR:
case NODE_CITEREFENTRY:
case NODE_CITETITLE:
case NODE_CODE:
case NODE_COMMAND:
case NODE_CONSTANT:
case NODE_EDITOR:
case NODE_EMAIL:
case NODE_EMPHASIS:
case NODE_ENVAR:
case NODE_FILENAME:
case NODE_FIRSTTERM:
case NODE_FUNCTION:
case NODE_FUNCSYNOPSISINFO:
case NODE_KEYSYM:
case NODE_LITERAL:
case NODE_OPTION:
case NODE_PARAMETER:
case NODE_REPLACEABLE:
case NODE_REFPURPOSE:
case NODE_SGMLTAG:
case NODE_STRUCTNAME:
case NODE_TYPE:
case NODE_USERINPUT:
case NODE_VARNAME:
if (sv != LINE_MACRO && p->linestate == LINE_MACRO)
macro_closepunct(p, pn);
break;
case NODE_QUOTE:
if (sv == LINE_NEW)
macro_close(p);
sv = p->linestate;
macro_open(p, "Qc");
if (sv == LINE_NEW)
macro_close(p);
break;
case NODE_REFNAME:
/*
* If we're in the NAME macro and we have multiple
* <refname> macros in sequence, then print out a
* trailing comma before the newline.
*/
if (pn->parent != NULL &&
pn->parent->node == NODE_REFNAMEDIV &&
TAILQ_NEXT(pn, child) != NULL &&
TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
macro_addarg(p, ",", ARG_SPACE);
if (sv == LINE_NEW)
macro_close(p);
break;
case NODE_PREFACE:
case NODE_SECTION:
case NODE_NOTE:
case NODE_TIP:
case NODE_CAUTION:
case NODE_WARNING:
p->level--;
break;
case NODE_LITERALLAYOUT:
case NODE_PROGRAMLISTING:
case NODE_SCREEN:
macro_line(p, "Ed");
break;
case NODE_TITLE:
if (pn->parent->node == NODE_BOOKINFO)
macro_line(p, "Sh AUTHORS");
break;
default:
break;
}
}
void
ptree_print(struct ptree *tree)
{
struct format formatter;
formatter.level = 0;
formatter.linestate = LINE_NEW;
pnode_printprologue(&formatter, tree);
pnode_print(&formatter, tree->root);
if (formatter.linestate != LINE_NEW)
putchar('\n');
}