=================================================================== RCS file: /cvs/docbook2mdoc/docbook2mdoc.c,v retrieving revision 1.74 retrieving revision 1.78 diff -u -p -r1.74 -r1.78 --- docbook2mdoc/docbook2mdoc.c 2019/03/26 18:32:07 1.74 +++ docbook2mdoc/docbook2mdoc.c 2019/03/28 20:41:33 1.78 @@ -1,4 +1,4 @@ -/* $Id: docbook2mdoc.c,v 1.74 2019/03/26 18:32:07 schwarze Exp $ */ +/* $Id: docbook2mdoc.c,v 1.78 2019/03/28 20:41:33 schwarze Exp $ */ /* * Copyright (c) 2014 Kristaps Dzonsons * Copyright (c) 2019 Ingo Schwarze @@ -21,234 +21,17 @@ #include #include "node.h" +#include "macro.h" #include "format.h" /* * The implementation of the mdoc(7) formatter. */ -enum linestate { - LINE_NEW = 0, - LINE_TEXT, - LINE_MACRO -}; - -struct format { - int level; /* Header level, starting at 1. */ - enum linestate linestate; -}; - static void pnode_print(struct format *, struct pnode *); static void -macro_open(struct format *p, const char *name) -{ - switch (p->linestate) { - case LINE_TEXT: - putchar('\n'); - /* FALLTHROUGH */ - case LINE_NEW: - putchar('.'); - p->linestate = LINE_MACRO; - break; - case LINE_MACRO: - putchar(' '); - break; - } - fputs(name, stdout); -} - -static void -macro_close(struct format *p) -{ - assert(p->linestate == LINE_MACRO); - putchar('\n'); - p->linestate = LINE_NEW; -} - -static void -macro_line(struct format *p, const char *name) -{ - macro_open(p, name); - macro_close(p); -} - -#define ARG_SPACE 1 /* Insert whitespace before this argument. */ -#define ARG_SINGLE 2 /* Quote argument if it contains whitespace. */ -#define ARG_QUOTED 4 /* We are already in a quoted argument. */ -#define ARG_UPPER 8 /* Covert argument to upper case. */ -/* - * Print an argument string on a macro line, collapsing whitespace. - */ -static void -macro_addarg(struct format *p, const char *arg, int flags) -{ - const char *cp; - - assert(p->linestate == LINE_MACRO); - - /* Quote if requested and necessary. */ - - if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) { - for (cp = arg; *cp != '\0'; cp++) - if (isspace((unsigned char)*cp)) - break; - if (*cp != '\0') { - if (flags & ARG_SPACE) { - putchar(' '); - flags &= ~ ARG_SPACE; - } - putchar('"'); - flags = ARG_QUOTED; - } - } - - for (cp = arg; *cp != '\0'; cp++) { - - /* Collapse whitespace. */ - - if (isspace((unsigned char)*cp)) { - flags |= ARG_SPACE; - continue; - } else if (flags & ARG_SPACE) { - putchar(' '); - flags &= ~ ARG_SPACE; - } - - /* Escape us if we look like a macro. */ - - if ((flags & ARG_QUOTED) == 0 && - (cp == arg || isspace((unsigned char)cp[-1])) && - isupper((unsigned char)cp[0]) && - islower((unsigned char)cp[1]) && - (cp[2] == '\0' || cp[2] == ' ' || - (islower((unsigned char)cp[2]) && - (cp[3] == '\0' || cp[3] == ' ')))) - fputs("\\&", stdout); - - if (*cp == '"') - fputs("\\(dq", stdout); - else if (flags & ARG_UPPER) - putchar(toupper((unsigned char)*cp)); - else - putchar(*cp); - if (*cp == '\\') - putchar('e'); - } -} - -static void -macro_argline(struct format *p, const char *name, const char *arg) -{ - macro_open(p, name); - macro_addarg(p, arg, ARG_SPACE); - macro_close(p); -} - -/* - * Recursively append text from the children of a node to a macro line. - */ -static void -macro_addnode(struct format *p, struct pnode *pn, int flags) -{ - int quote_now; - - assert(p->linestate == LINE_MACRO); - - /* - * If the only child is a text node, just add that text, - * letting macro_addarg() decide about quoting. - */ - - pn = TAILQ_FIRST(&pn->childq); - if (pn != NULL && pn->node == NODE_TEXT && - TAILQ_NEXT(pn, child) == NULL) { - macro_addarg(p, pn->b, flags); - return; - } - - /* - * If we want the argument quoted and are not already - * in a quoted context, quote now. - */ - - quote_now = 0; - if (flags & ARG_SINGLE) { - if ((flags & ARG_QUOTED) == 0) { - if (flags & ARG_SPACE) { - putchar(' '); - flags &= ~ARG_SPACE; - } - putchar('"'); - flags |= ARG_QUOTED; - quote_now = 1; - } - flags &= ~ARG_SINGLE; - } - - /* - * Iterate to child and sibling nodes, - * inserting whitespace between nodes. - */ - - while (pn != NULL) { - if (pn->node == NODE_TEXT) - macro_addarg(p, pn->b, flags); - else - macro_addnode(p, pn, flags); - pn = TAILQ_NEXT(pn, child); - flags |= ARG_SPACE; - } - if (quote_now) - putchar('"'); -} - -static void -macro_nodeline(struct format *p, const char *name, struct pnode *pn, int flags) -{ - macro_open(p, name); - macro_addnode(p, pn, ARG_SPACE | flags); - macro_close(p); -} - -/* - * If the next node is a text node starting with closing punctuation, - * emit the closing punctuation as a trailing macro argument. - */ -static void -macro_closepunct(struct format *p, struct pnode *pn) -{ - if ((pn = TAILQ_NEXT(pn, child)) != NULL && - pn->node == NODE_TEXT && pn->bsz > 0 && - (pn->b[0] == ',' || pn->b[0] == '.') && - (pn->bsz == 1 || isspace((unsigned char)pn->b[1]))) { - putchar(' '); - putchar(pn->b[0]); - pn->b++; - pn->bsz--; - } - macro_close(p); -} - -static void -print_text(struct format *p, const char *word) -{ - switch (p->linestate) { - case LINE_NEW: - break; - case LINE_TEXT: - putchar(' '); - break; - case LINE_MACRO: - macro_close(p); - break; - } - fputs(word, stdout); - p->linestate = LINE_TEXT; -} - -static void pnode_printpara(struct format *p, struct pnode *pn) { struct pnode *pp; @@ -607,6 +390,66 @@ pnode_printgroup(struct format *p, struct pnode *pn) } static void +pnode_printauthor(struct format *f, struct pnode *n) +{ + struct pnode *nc, *ncn; + int have_contrib, have_name; + + /* + * Print 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 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; @@ -628,7 +471,7 @@ pnode_printprologue(struct format *p, struct ptree *tr if (tree->flags & TREE_EQN) { macro_line(p, "EQ"); - print_text(p, "delim $$"); + print_text(p, "delim $$", 0); macro_line(p, "EN"); } } @@ -759,14 +602,11 @@ pnode_print(struct format *p, struct pnode *pn) case NODE_APPLICATION: macro_open(p, "Nm"); break; - case NODE_ANCHOR: - /* Don't print anything! */ - return; case NODE_ARG: pnode_printarg(p, pn); break; case NODE_AUTHOR: - macro_open(p, "An"); + pnode_printauthor(p, pn); break; case NODE_AUTHORGROUP: macro_line(p, "An -split"); @@ -790,7 +630,7 @@ pnode_print(struct format *p, struct pnode *pn) macro_open(p, "Dv"); break; case NODE_EDITOR: - print_text(p, "editor:"); + print_text(p, "editor:", ARG_SPACE); macro_open(p, "An"); break; case NODE_EMAIL: @@ -815,8 +655,6 @@ pnode_print(struct format *p, struct pnode *pn) case NODE_FUNCSYNOPSISINFO: macro_open(p, "Fd"); break; - case NODE_INDEXTERM: - return; case NODE_INFORMALEQUATION: macro_line(p, "EQ"); break;