=================================================================== RCS file: /cvs/docbook2mdoc/docbook2mdoc.c,v retrieving revision 1.109 retrieving revision 1.119 diff -u -p -r1.109 -r1.119 --- docbook2mdoc/docbook2mdoc.c 2019/04/12 10:34:48 1.109 +++ docbook2mdoc/docbook2mdoc.c 2019/04/14 12:38:33 1.119 @@ -1,4 +1,4 @@ -/* $Id: docbook2mdoc.c,v 1.109 2019/04/12 10:34:48 schwarze Exp $ */ +/* $Id: docbook2mdoc.c,v 1.119 2019/04/14 12:38:33 schwarze Exp $ */ /* * Copyright (c) 2014 Kristaps Dzonsons * Copyright (c) 2019 Ingo Schwarze @@ -30,6 +30,7 @@ */ static void pnode_print(struct format *, struct pnode *); +static void pnode_printrefentry(struct format *, struct pnode *); static void @@ -70,12 +71,18 @@ pnode_printtext(struct format *f, struct pnode *n) */ if (f->linestate != LINE_MACRO && - (nn = TAILQ_NEXT(n, child)) != NULL && nn->spc == 0 && - (nn->node != NODE_TEXT && nn->node != NODE_ESCAPE)) { - macro_open(f, "Pf"); - accept_arg = 1; - f->flags |= FMT_CHILD; - nn->spc = 1; + (nn = TAILQ_NEXT(n, child)) != NULL && nn->spc == 0) { + switch (pnode_class(nn->node)) { + case CLASS_LINE: + case CLASS_ENCL: + macro_open(f, "Pf"); + accept_arg = 1; + f->flags |= FMT_CHILD; + nn->spc = 1; + break; + default: + break; + } } switch (f->linestate) { @@ -150,6 +157,27 @@ pnode_printpara(struct format *f, struct pnode *n) macro_line(f, "Pp"); } +static void +pnode_printrefnamediv(struct format *f, struct pnode *n) +{ + struct pnode *nc, *nn; + int comma; + + macro_line(f, "Sh NAME"); + comma = 0; + TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) { + if (nc->node != NODE_REFNAME) + continue; + if (comma) + macro_addarg(f, ",", ARG_SPACE); + macro_open(f, "Nm"); + macro_addnode(f, nc, ARG_SPACE); + pnode_unlink(nc); + comma = 1; + } + macro_close(f); +} + /* * If the SYNOPSIS macro has a superfluous title, kill it. */ @@ -169,14 +197,16 @@ pnode_printrefsynopsisdiv(struct format *f, struct pno * Start a hopefully-named `Sh' section. */ static void -pnode_printrefsect(struct format *f, struct pnode *n) +pnode_printsection(struct format *f, struct pnode *n) { - struct pnode *nc; + struct pnode *nc, *ncc; const char *title; int flags, level; - if (n->parent == NULL) + if (n->parent == NULL) { + pnode_printrefentry(f, n); return; + } level = ++f->level; flags = ARG_SPACE; @@ -243,12 +273,24 @@ pnode_printrefsect(struct format *f, struct pnode *n) break; } - if (nc != NULL) { + if (nc != NULL) macro_addnode(f, nc, flags); - pnode_unlink(nc); - } else + else macro_addarg(f, title, flags | ARG_QUOTED); macro_close(f); + + /* + * DocBook has no equivalent for -split mode, + * so just switch the default in the AUTHORS section. + */ + + if (nc != NULL) { + ncc = TAILQ_FIRST(&nc->childq); + if (ncc != NULL && ncc->node == NODE_TEXT && + strcasecmp(ncc->b, "AUTHORS") == 0) + macro_line(f, "An -nosplit"); + pnode_unlink(nc); + } } /* @@ -279,32 +321,6 @@ pnode_printciterefentry(struct format *f, struct pnode } static void -pnode_printrefmeta(struct format *f, struct pnode *n) -{ - struct pnode *nc, *title, *manvol; - - title = manvol = NULL; - TAILQ_FOREACH(nc, &n->childq, child) { - if (nc->node == NODE_MANVOLNUM) - manvol = nc; - else if (nc->node == NODE_REFENTRYTITLE) - title = nc; - } - macro_close(f); - macro_open(f, "Dt"); - if (title == NULL) - macro_addarg(f, "UNKNOWN", ARG_SPACE); - else - macro_addnode(f, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER); - if (manvol == NULL) - macro_addarg(f, "1", ARG_SPACE); - else - macro_addnode(f, manvol, ARG_SPACE | ARG_SINGLE); - macro_close(f); - pnode_unlink(n); -} - -static void pnode_printfuncdef(struct format *f, struct pnode *n) { struct pnode *nc; @@ -429,14 +445,13 @@ pnode_printarg(struct format *f, struct pnode *n) f->flags |= FMT_IMPL; } } - TAILQ_FOREACH(nc, &n->childq, child) { if (nc->node == NODE_TEXT) macro_open(f, "Ar"); pnode_print(f, nc); - if (isrep && nc->node == NODE_TEXT) - macro_addarg(f, "...", ARG_SPACE); } + if (isrep && f->linestate == LINE_MACRO) + macro_addarg(f, "...", ARG_SPACE); if (isop) { if (was_impl) macro_open(f, "Oc"); @@ -449,57 +464,76 @@ pnode_printarg(struct format *f, struct pnode *n) static void pnode_printgroup(struct format *f, struct pnode *n) { - struct pnode *nc, *nn; + struct pnode *nc; struct pattr *a; - int isop, sv; + int bar, isop, isrep, was_impl; isop = 1; - TAILQ_FOREACH(a, &n->attrq, child) + isrep = was_impl = 0; + TAILQ_FOREACH(a, &n->attrq, child) { if (a->key == ATTRKEY_CHOICE && - (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ)) { + (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ)) isop = 0; - break; + else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT) + isrep = 1; + } + if (isop) { + if (f->flags & FMT_IMPL) { + was_impl = 1; + macro_open(f, "Oo"); + } else { + macro_open(f, "Op"); + f->flags |= FMT_IMPL; } - - /* - * Make sure we're on a macro line. - * This will prevent pnode_print() for putting us on a - * subsequent line. - */ - sv = f->linestate == LINE_NEW; - if (isop) - macro_open(f, "Op"); - else if (sv) - macro_open(f, "No"); - f->flags |= FMT_IMPL; - - /* - * 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(). - */ + } else if (isrep) { + if (f->flags & FMT_IMPL) { + was_impl = 1; + macro_open(f, "Bro"); + } else { + macro_open(f, "Brq"); + f->flags |= FMT_IMPL; + } + } + bar = 0; TAILQ_FOREACH(nc, &n->childq, child) { - pnode_print(f, nc); - nn = TAILQ_NEXT(nc, child); - while (nn != NULL) { - if (nc->node != nn->node) - break; + if (bar && f->linestate == LINE_MACRO) macro_addarg(f, "|", ARG_SPACE); - macro_addnode(f, nn, ARG_SPACE); - nc = nn; - nn = TAILQ_NEXT(nn, child); - } + pnode_print(f, nc); + bar = 1; } - if (sv) - macro_close(f); - f->flags &= ~FMT_IMPL; + if (isop) { + if (was_impl) + macro_open(f, "Oc"); + else + f->flags &= ~FMT_IMPL; + } else if (isrep) { + if (was_impl) + macro_open(f, "Brc"); + else + f->flags &= ~FMT_IMPL; + } + if (isrep && f->linestate == LINE_MACRO) + macro_addarg(f, "...", ARG_SPACE); pnode_unlinksub(n); } static void +pnode_printsystemitem(struct format *f, struct pnode *n) +{ + switch (pnode_getattr(n, ATTRKEY_CLASS)) { + case ATTRVAL_IPADDRESS: + break; + case ATTRVAL_SYSTEMNAME: + macro_open(f, "Pa"); + break; + case ATTRVAL_EVENT: + default: + macro_open(f, "Sy"); + break; + } +} + +static void pnode_printauthor(struct format *f, struct pnode *n) { struct pnode *nc, *nn; @@ -554,7 +588,8 @@ pnode_printauthor(struct format *f, struct pnode *n) if ((nc = pnode_findfirst(n, NODE_EMAIL)) != NULL) { f->flags |= FMT_CHILD; - pnode_print(f, nc); + macro_open(f, "Aq Mt"); + macro_addnode(f, nc, ARG_SPACE); pnode_unlink(nc); } @@ -618,32 +653,207 @@ pnode_printlink(struct format *f, struct pnode *n) } static void -pnode_printprologue(struct format *f, struct ptree *tree) +pnode_printprologue(struct format *f, struct pnode *root) { - struct pnode *refmeta; + struct pnode *date, *refmeta, *name, *vol, *descr, *nc, *nn; + const char *sname; - refmeta = tree->root == NULL ? NULL : - pnode_findfirst(tree->root, NODE_REFMETA); + /* Collect information. */ - macro_line(f, "Dd $Mdocdate" "$"); - if (refmeta == NULL) { - macro_open(f, "Dt"); - macro_addarg(f, - pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"), - ARG_SPACE | ARG_SINGLE | ARG_UPPER); - macro_addarg(f, "1", ARG_SPACE); - macro_close(f); + if ((date = pnode_takefirst(root, NODE_PUBDATE)) == NULL) + date = pnode_takefirst(root, NODE_DATE); + + name = vol = NULL; + if ((refmeta = pnode_findfirst(root, NODE_REFMETA)) != NULL) { + TAILQ_FOREACH_SAFE(nc, &refmeta->childq, child, nn) { + switch (nc->node) { + case NODE_REFENTRYTITLE: + name = nc; + break; + case NODE_MANVOLNUM: + vol = nc; + break; + default: + continue; + } + TAILQ_REMOVE(&refmeta->childq, nc, child); + } + } + + if (pnode_findfirst(root, NODE_REFNAMEDIV) == NULL && + ((nc = pnode_findfirst(root, NODE_BOOKINFO)) != NULL || + (nc = pnode_findfirst(root, NODE_REFENTRYINFO)) != NULL)) + descr = pnode_takefirst(nc, NODE_TITLE); + else + descr = NULL; + + /* Print prologue. */ + + if (date == NULL) + macro_line(f, "Dd $Mdocdate" "$"); + else + macro_nodeline(f, "Dd", date, 0); + + macro_open(f, "Dt"); + if (name == NULL) { + sname = pnode_getattr_raw(root, ATTRKEY_ID, "UNKNOWN"); + macro_addarg(f, sname, ARG_SPACE | ARG_SINGLE | ARG_UPPER); } else - pnode_printrefmeta(f, refmeta); + macro_addnode(f, name, ARG_SPACE | ARG_SINGLE | ARG_UPPER); + if (vol == NULL) + macro_addarg(f, "1", ARG_SPACE); + else + macro_addnode(f, vol, ARG_SPACE | ARG_SINGLE); + macro_line(f, "Os"); - if (tree->flags & TREE_EQN) { - macro_line(f, "EQ"); - print_text(f, "delim $$", 0); - macro_line(f, "EN"); + if (descr != NULL) { + macro_line(f, "Sh NAME"); + if (name == NULL) + macro_argline(f, "Nm", sname); + else + macro_nodeline(f, "Nm", name, ARG_SINGLE); + macro_nodeline(f, "Nd", descr, 0); } + + /* Clean up. */ + + pnode_unlink(date); + pnode_unlink(name); + pnode_unlink(vol); + pnode_unlink(descr); } +static void +pnode_printrefentry(struct format *f, struct pnode *n) +{ + struct pnode *info, *meta, *nc, *title; + struct pnode *match, *later; + + /* Collect nodes that remained behind when writing the prologue. */ + + meta = NULL; + info = pnode_takefirst(n, NODE_BOOKINFO); + if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) { + pnode_unlink(info); + info = NULL; + } + if (info == NULL) { + info = pnode_takefirst(n, NODE_REFENTRYINFO); + if (info != NULL && TAILQ_FIRST(&info->childq) == NULL) { + pnode_unlink(info); + info = NULL; + } + meta = pnode_takefirst(n, NODE_REFMETA); + if (meta != NULL && TAILQ_FIRST(&meta->childq) == NULL) { + pnode_unlink(meta); + meta = NULL; + } + } + if (info == NULL && meta == NULL) + return; + + /* + * Find the best place to put this information. + * Use the last existing AUTHORS node, if any. + * Otherwise, put it behind all standard sections that + * conventionally precede AUTHORS, and also behind any + * non-standard sections that follow the last of these, + * but before the next standard section. + */ + + match = later = NULL; + TAILQ_FOREACH(nc, &n->childq, child) { + switch (nc->node) { + case NODE_REFENTRY: + case NODE_REFNAMEDIV: + case NODE_REFSYNOPSISDIV: + case NODE_PREFACE: + later = NULL; + continue; + case NODE_APPENDIX: + case NODE_INDEX: + if (later == NULL) + later = nc; + continue; + default: + break; + } + if ((title = pnode_findfirst(nc, NODE_TITLE)) == NULL || + (title = TAILQ_FIRST(&title->childq)) == NULL || + title->node != NODE_TEXT) + continue; + if (strcasecmp(title->b, "AUTHORS") == 0 || + strcasecmp(title->b, "AUTHOR") == 0) + match = nc; + else if (strcasecmp(title->b, "NAME") == 0 || + strcasecmp(title->b, "SYNOPSIS") == 0 || + strcasecmp(title->b, "DESCRIPTION") == 0 || + strcasecmp(title->b, "RETURN VALUES") == 0 || + strcasecmp(title->b, "ENVIRONMENT") == 0 || + strcasecmp(title->b, "FILES") == 0 || + strcasecmp(title->b, "EXIT STATUS") == 0 || + strcasecmp(title->b, "EXAMPLES") == 0 || + strcasecmp(title->b, "DIAGNOSTICS") == 0 || + strcasecmp(title->b, "ERRORS") == 0 || + strcasecmp(title->b, "SEE ALSO") == 0 || + strcasecmp(title->b, "STANDARDS") == 0 || + strcasecmp(title->b, "HISTORY") == 0) + later = NULL; + else if ((strcasecmp(title->b, "CAVEATS") == 0 || + strcasecmp(title->b, "BUGS") == 0) && + later == NULL) + later = nc; + } + + /* + * If no AUTHORS section was found, create one from scratch, + * and insert that at the place selected earlier. + */ + + if (match == NULL) { + if ((match = calloc(1, sizeof(*match))) == NULL) { + perror(NULL); + exit(1); + } + match->node = NODE_SECTION; + match->spc = 1; + match->parent = n; + TAILQ_INIT(&match->childq); + TAILQ_INIT(&match->attrq); + if ((nc = pnode_alloc(match)) == NULL) { + perror(NULL); + exit(1); + } + nc->node = NODE_TITLE; + nc->spc = 1; + if ((nc = pnode_alloc(nc)) == NULL) { + perror(NULL); + exit(1); + } + nc->node = NODE_TEXT; + if ((nc->b = strdup("AUTHORS")) == NULL) { + perror(NULL); + exit(1); + } + nc->spc = 1; + if (later == NULL) + TAILQ_INSERT_TAIL(&n->childq, match, child); + else + TAILQ_INSERT_BEFORE(later, match, child); + } + + /* + * Dump the stuff excised at the beginning + * into this AUTHORS section. + */ + + if (info != NULL) + TAILQ_INSERT_TAIL(&match->childq, info, child); + if (meta != NULL) + TAILQ_INSERT_TAIL(&match->childq, meta, child); +} + /* * We can have multiple elements within a , which * we should comma-separate as list headers. @@ -815,12 +1025,12 @@ static void pnode_print(struct format *f, struct pnode *n) { struct pnode *nc, *nn; - enum linestate sv; + int was_impl; if (n == NULL) return; - sv = f->linestate; + was_impl = f->flags & FMT_IMPL; if (n->spc) f->flags &= ~FMT_NOSPC; else @@ -842,9 +1052,6 @@ pnode_print(struct format *f, struct pnode *n) case NODE_BLOCKQUOTE: macro_line(f, "Bd -ragged -offset indent"); break; - case NODE_BOOKINFO: - macro_line(f, "Sh NAME"); - break; case NODE_CITEREFENTRY: pnode_printciterefentry(f, n); break; @@ -859,11 +1066,15 @@ pnode_print(struct format *f, struct pnode *n) break; case NODE_EDITOR: print_text(f, "editor:", ARG_SPACE); - sv = LINE_TEXT; macro_open(f, "An"); break; case NODE_EMAIL: - macro_open(f, "Aq Mt"); + if (was_impl) + macro_open(f, "Ao Mt"); + else { + macro_open(f, "Aq Mt"); + f->flags |= FMT_IMPL; + } break; case NODE_EMPHASIS: case NODE_FIRSTTERM: @@ -889,13 +1100,11 @@ pnode_print(struct format *f, struct pnode *n) macro_open(f, "Fd"); break; case NODE_INFORMALEQUATION: + macro_line(f, "Bd -ragged -offset indent"); + /* FALLTHROUGH */ + case NODE_INLINEEQUATION: macro_line(f, "EQ"); break; - case NODE_INLINEEQUATION: - if (f->linestate == LINE_NEW) - f->linestate = LINE_TEXT; - putchar('$'); - break; case NODE_ITEMIZEDLIST: pnode_printlist(f, n); break; @@ -909,7 +1118,12 @@ pnode_print(struct format *f, struct pnode *n) pnode_printlink(f, n); break; case NODE_LITERAL: - macro_open(f, "Ql"); + if (was_impl) + macro_open(f, "So"); + else { + macro_open(f, "Ql"); + f->flags |= FMT_IMPL; + } break; case NODE_LITERALLAYOUT: macro_close(f); @@ -952,25 +1166,30 @@ pnode_print(struct format *f, struct pnode *n) pnode_unlinksub(n); break; case NODE_QUOTE: - macro_open(f, "Qo"); + if (was_impl) + macro_open(f, "Do"); + else { + macro_open(f, "Dq"); + f->flags |= FMT_IMPL; + } break; case NODE_PROGRAMLISTING: case NODE_SCREEN: case NODE_SYNOPSIS: macro_line(f, "Bd -literal"); break; - case NODE_REFENTRYINFO: - /* Suppress. */ - pnode_unlinksub(n); + case NODE_SYSTEMITEM: + pnode_printsystemitem(f, n); break; + case NODE_REFENTRY: + pnode_printrefentry(f, n); + break; case NODE_REFNAME: - /* Suppress non-text children... */ + /* More often, these appear inside NODE_REFNAMEDIV. */ macro_open(f, "Nm"); - macro_addnode(f, n, ARG_SPACE | ARG_SINGLE); - pnode_unlinksub(n); break; case NODE_REFNAMEDIV: - macro_line(f, "Sh NAME"); + pnode_printrefnamediv(f, n); break; case NODE_REFPURPOSE: macro_open(f, "Nd"); @@ -986,7 +1205,7 @@ pnode_print(struct format *f, struct pnode *n) case NODE_TIP: case NODE_CAUTION: case NODE_WARNING: - pnode_printrefsect(f, n); + pnode_printsection(f, n); break; case NODE_REPLACEABLE: macro_open(f, "Ar"); @@ -1002,11 +1221,6 @@ pnode_print(struct format *f, struct pnode *n) pnode_printtgroup(f, n); break; case NODE_TITLE: - if (n->parent != NULL && - n->parent->node == NODE_BOOKINFO) { - macro_open(f, "Nd"); - break; - } pnode_printpara(f, n); macro_nodeline(f, "Sy", n, 0); pnode_unlinksub(n); @@ -1028,6 +1242,13 @@ pnode_print(struct format *f, struct pnode *n) pnode_print(f, nc); switch (n->node) { + case NODE_EMAIL: + if (was_impl) { + f->flags &= ~FMT_NOSPC; + macro_open(f, "Ac"); + } else + f->flags &= ~FMT_IMPL; + break; case NODE_ESCAPE: case NODE_TERM: case NODE_TEXT: @@ -1035,11 +1256,18 @@ pnode_print(struct format *f, struct pnode *n) return; case NODE_INFORMALEQUATION: macro_line(f, "EN"); + macro_line(f, "Ed"); break; case NODE_INLINEEQUATION: - fputs("$ ", stdout); - f->linestate = sv; + macro_line(f, "EN"); break; + case NODE_LITERAL: + if (was_impl) { + f->flags &= ~FMT_NOSPC; + macro_open(f, "Sc"); + } else + f->flags &= ~FMT_IMPL; + break; case NODE_MEMBER: if ((nn = TAILQ_NEXT(n, child)) != NULL && nn->node != NODE_MEMBER) @@ -1067,27 +1295,12 @@ pnode_print(struct format *f, struct pnode *n) fputs(" } ", stdout); break; case NODE_QUOTE: - if (sv == LINE_NEW) - macro_close(f); - sv = f->linestate; - macro_open(f, "Qc"); - if (sv == LINE_NEW) - macro_close(f); + if (was_impl) { + f->flags &= ~FMT_NOSPC; + macro_open(f, "Dc"); + } else + f->flags &= ~FMT_IMPL; break; - case NODE_REFNAME: - /* - * If we're in the NAME macro and we have multiple - * macros in sequence, then print out a - * trailing comma before the newline. - */ - if (n->parent != NULL && - n->parent->node == NODE_REFNAMEDIV && - TAILQ_NEXT(n, child) != NULL && - TAILQ_NEXT(n, child)->node == NODE_REFNAME) - macro_addarg(f, ",", ARG_SPACE); - if (sv == LINE_NEW) - macro_close(f); - break; case NODE_PREFACE: case NODE_SECTION: case NODE_APPENDIX: @@ -1105,11 +1318,6 @@ pnode_print(struct format *f, struct pnode *n) case NODE_SYNOPSIS: macro_line(f, "Ed"); break; - case NODE_TITLE: - if (n->parent != NULL && - n->parent->node == NODE_BOOKINFO) - macro_line(f, "Sh AUTHORS"); - break; default: break; } @@ -1117,13 +1325,13 @@ pnode_print(struct format *f, struct pnode *n) } void -ptree_print(struct ptree *tree) +ptree_print_mdoc(struct ptree *tree) { struct format formatter; formatter.level = 0; formatter.linestate = LINE_NEW; - pnode_printprologue(&formatter, tree); + pnode_printprologue(&formatter, tree->root); pnode_print(&formatter, tree->root); if (formatter.linestate != LINE_NEW) putchar('\n');