=================================================================== RCS file: /cvs/docbook2mdoc/macro.c,v retrieving revision 1.4 retrieving revision 1.20 diff -u -p -r1.4 -r1.20 --- docbook2mdoc/macro.c 2019/04/03 13:42:35 1.4 +++ docbook2mdoc/macro.c 2019/05/02 12:40:42 1.20 @@ -1,4 +1,4 @@ -/* $Id: macro.c,v 1.4 2019/04/03 13:42:35 schwarze Exp $ */ +/* $Id: macro.c,v 1.20 2019/05/02 12:40:42 schwarze Exp $ */ /* * Copyright (c) 2019 Ingo Schwarze * @@ -17,6 +17,7 @@ #include #include #include +#include #include "node.h" #include "macro.h" @@ -27,70 +28,81 @@ */ void +para_check(struct format *f) +{ + if (f->parastate != PARA_WANT) + return; + if (f->linestate != LINE_NEW) { + putchar('\n'); + f->linestate = LINE_NEW; + } + puts(".Pp"); + f->parastate = PARA_HAVE; +} + +void macro_open(struct format *f, const char *name) { + para_check(f); switch (f->linestate) { + case LINE_MACRO: + if (f->flags & FMT_NOSPC) { + fputs(" Ns ", stdout); + break; + } + if (f->nofill || f->flags & (FMT_CHILD | FMT_IMPL)) { + putchar(' '); + break; + } + /* FALLTHROUGH */ case LINE_TEXT: + if (f->nofill && f->linestate == LINE_TEXT) + fputs(" \\c", stdout); putchar('\n'); /* FALLTHROUGH */ case LINE_NEW: putchar('.'); f->linestate = LINE_MACRO; + f->flags = 0; break; - case LINE_MACRO: - putchar(' '); - break; } fputs(name, stdout); + f->flags &= FMT_IMPL; + f->flags |= FMT_ARG; + f->parastate = PARA_MID; } void macro_close(struct format *f) { - if (f->linestate == LINE_NEW) - return; - putchar('\n'); + if (f->linestate != LINE_NEW) + putchar('\n'); f->linestate = LINE_NEW; + f->flags = 0; } void macro_line(struct format *f, const char *name) { + macro_close(f); macro_open(f, name); macro_close(f); } /* - * If the next node is a text node starting with closing punctuation, - * emit the closing punctuation as a trailing macro argument. - */ -void -macro_closepunct(struct format *f, 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(f); -} - -/* * Print an argument string on a macro line, collapsing whitespace. */ void macro_addarg(struct format *f, const char *arg, int flags) { const char *cp; + int quote_now; assert(f->linestate == LINE_MACRO); /* Quote if requested and necessary. */ + quote_now = 0; if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) { for (cp = arg; *cp != '\0'; cp++) if (isspace((unsigned char)*cp)) @@ -102,6 +114,7 @@ macro_addarg(struct format *f, const char *arg, int fl } putchar('"'); flags = ARG_QUOTED; + quote_now = 1; } } @@ -119,13 +132,16 @@ macro_addarg(struct format *f, const char *arg, int fl /* Escape us if we look like a macro. */ - if ((flags & ARG_QUOTED) == 0 && + if ((flags & (ARG_QUOTED | ARG_UPPER)) == 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] == ' ')))) + ((cp[3] == '\0' || cp[3] == ' ') && + (strncmp(cp, "Brq", 3) == 0 || + strncmp(cp, "Bro", 3) == 0 || + strncmp(cp, "Brc", 3) == 0 || + strncmp(cp, "Bsx", 3) == 0)))) fputs("\\&", stdout); if (*cp == '"') @@ -137,6 +153,9 @@ macro_addarg(struct format *f, const char *arg, int fl if (*cp == '\\') putchar('e'); } + if (quote_now) + putchar('"'); + f->parastate = PARA_MID; } void @@ -151,9 +170,10 @@ macro_argline(struct format *f, const char *name, cons * Recursively append text from the children of a node to a macro line. */ void -macro_addnode(struct format *f, struct pnode *pn, int flags) +macro_addnode(struct format *f, struct pnode *n, int flags) { - int quote_now; + struct pnode *nc; + int is_text, quote_now; assert(f->linestate == LINE_MACRO); @@ -162,11 +182,13 @@ macro_addnode(struct format *f, struct pnode *pn, int * that text, letting macro_addarg() decide about quoting. */ - if (pn->node == NODE_TEXT || pn->node == NODE_ESCAPE || - ((pn = TAILQ_FIRST(&pn->childq)) != NULL && - (pn->node == NODE_TEXT || pn->node == NODE_ESCAPE) && - TAILQ_NEXT(pn, child) == NULL)) { - macro_addarg(f, pn->b, flags); + while ((nc = TAILQ_FIRST(&n->childq)) != NULL && + TAILQ_NEXT(nc, child) == NULL) + n = nc; + + if (n->node == NODE_TEXT || n->node == NODE_ESCAPE) { + macro_addarg(f, n->b, flags); + f->parastate = PARA_MID; return; } @@ -194,20 +216,27 @@ macro_addnode(struct format *f, struct pnode *pn, int * inserting whitespace between nodes. */ - while (pn != NULL) { - macro_addnode(f, pn, flags); - pn = TAILQ_NEXT(pn, child); - flags |= ARG_SPACE; + while (nc != NULL) { + macro_addnode(f, nc, flags); + is_text = pnode_class(nc->node) == CLASS_TEXT; + nc = TAILQ_NEXT(nc, child); + if (nc == NULL || pnode_class(nc->node) != CLASS_TEXT) + is_text = 0; + if (is_text && (nc->flags & NFLAG_SPC) == 0) + flags &= ~ARG_SPACE; + else + flags |= ARG_SPACE; } if (quote_now) putchar('"'); + f->parastate = PARA_MID; } void -macro_nodeline(struct format *f, const char *name, struct pnode *pn, int flags) +macro_nodeline(struct format *f, const char *name, struct pnode *n, int flags) { macro_open(f, name); - macro_addnode(f, pn, ARG_SPACE | flags); + macro_addnode(f, n, ARG_SPACE | flags); macro_close(f); } @@ -217,7 +246,11 @@ macro_nodeline(struct format *f, const char *name, str * line otherwise. The flag ARG_SPACE inserts spaces between words. */ void -print_text(struct format *f, const char *word, int flags) { +print_text(struct format *f, const char *word, int flags) +{ + int ateos, inword; + + para_check(f); switch (f->linestate) { case LINE_NEW: break; @@ -229,8 +262,62 @@ print_text(struct format *f, const char *word, int fla macro_close(f); break; } - fputs(word, stdout); + if (f->linestate == LINE_NEW && (*word == '.' || *word == '\'')) + fputs("\\&", stdout); + ateos = inword = 0; + while (*word != '\0') { + if (f->nofill == 0) { + switch (*word) { + case ' ': + if (ateos == 0) { + inword = 0; + break; + } + ateos = inword = 0; + /* Handle the end of a sentence. */ + while (*word == ' ') + word++; + switch (*word) { + case '\0': + break; + case '\'': + case '.': + fputs("\n\\&", stdout); + break; + default: + putchar('\n'); + break; + } + continue; + /* Detect the end of a sentence. */ + case '!': + case '.': + case '?': + if (inword > 1 && + (word[-2] != 'n' || word[-1] != 'c') && + (word[-2] != 'v' || word[-1] != 's')) + ateos = 1; + /* FALLTHROUGH */ + case '"': + case '\'': + case ')': + case ']': + inword = 0; + break; + default: + if (isalnum((unsigned char)*word)) + inword++; + ateos = 0; + break; + } + } + putchar(*word); + if (*word++ == '\\') + putchar('e'); + } f->linestate = LINE_TEXT; + f->parastate = PARA_MID; + f->flags = 0; } /*