[BACK]Return to macro.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / docbook2mdoc

Annotation of docbook2mdoc/macro.c, Revision 1.7

1.7     ! schwarze    1: /* $Id: macro.c,v 1.6 2019/04/06 22:37:57 schwarze Exp $ */
1.1       schwarze    2: /*
                      3:  * Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <assert.h>
                     18: #include <ctype.h>
                     19: #include <stdio.h>
1.6       schwarze   20: #include <string.h>
1.1       schwarze   21:
                     22: #include "node.h"
                     23: #include "macro.h"
                     24:
                     25: /*
                     26:  * The implementation of the macro line formatter,
                     27:  * a part of the mdoc(7) formatter.
                     28:  */
                     29:
                     30: void
                     31: macro_open(struct format *f, const char *name)
                     32: {
                     33:        switch (f->linestate) {
                     34:        case LINE_TEXT:
                     35:                putchar('\n');
                     36:                /* FALLTHROUGH */
                     37:        case LINE_NEW:
                     38:                putchar('.');
                     39:                f->linestate = LINE_MACRO;
                     40:                break;
                     41:        case LINE_MACRO:
                     42:                putchar(' ');
1.6       schwarze   43:                if (f->spc == 0)
                     44:                        fputs("Ns ", stdout);
1.1       schwarze   45:                break;
                     46:        }
                     47:        fputs(name, stdout);
                     48: }
                     49:
                     50: void
                     51: macro_close(struct format *f)
                     52: {
1.4       schwarze   53:        if (f->linestate == LINE_NEW)
                     54:                return;
1.1       schwarze   55:        putchar('\n');
                     56:        f->linestate = LINE_NEW;
                     57: }
                     58:
                     59: void
                     60: macro_line(struct format *f, const char *name)
                     61: {
1.7     ! schwarze   62:        macro_close(f);
1.1       schwarze   63:        macro_open(f, name);
                     64:        macro_close(f);
                     65: }
                     66:
                     67: /*
1.6       schwarze   68:  * At the end of a macro, decide whether the line needs to remain open
                     69:  * because the next node follows without intervening whitespace;
                     70:  * otherwise, close the line.
1.1       schwarze   71:  */
                     72: void
                     73: macro_closepunct(struct format *f, struct pnode *pn)
                     74: {
1.6       schwarze   75:        char            *cp;
                     76:
                     77:        if ((pn = TAILQ_NEXT(pn, child)) != NULL && pn->spc == 0) {
                     78:
                     79:                /*
                     80:                 * If a non-text node follows without intervening
                     81:                 * whitespace, the handler of that node will decide
                     82:                 * whether and how to suppress whitespace.  To allow
                     83:                 * that, the macro line needs to remain open.
                     84:                 */
                     85:
                     86:                if (pn->node != NODE_TEXT && pn->node != NODE_ESCAPE)
                     87:                        return;
                     88:
                     89:                /*
                     90:                 * Give closing punctuation
                     91:                 * in the form of trailing macro arguments.
                     92:                 */
                     93:
                     94:                while (*pn->b != '\0' &&
                     95:                    strchr("!),.:;?]", *pn->b) != NULL) {
                     96:                        putchar(' ');
                     97:                        putchar(*pn->b);
                     98:                        pn->b++;
                     99:                        pn->bsz--;
                    100:                }
                    101:
                    102:                /*
                    103:                 * Text follows without intervening whitespace.
                    104:                 * Append the first word with .Ns.
                    105:                 */
                    106:
                    107:                if (*pn->b != '\0' && isspace((unsigned char)*pn->b) == 0) {
                    108:                        fputs(" Ns", stdout);
                    109:                        for (cp = pn->b; *cp != '\0'; cp++)
                    110:                                if (isspace((unsigned char)*cp))
                    111:                                        break;
                    112:                        *cp = '\0';
                    113:                        macro_addarg(f, pn->b, ARG_SPACE);
                    114:                        pn->bsz -= cp - pn->b;
                    115:                        pn->b = cp;
                    116:                        if (pn->bsz > 0) {
                    117:                                pn->b++;
                    118:                                pn->bsz--;
                    119:                                pn->spc = 1;
                    120:                        }
                    121:                }
                    122:
                    123:                /* Skip whitespace after the first word. */
                    124:
                    125:                while (isspace((unsigned char)*pn->b)) {
                    126:                        pn->b++;
                    127:                        pn->bsz--;
                    128:                        pn->spc = 1;
                    129:                }
1.1       schwarze  130:        }
                    131:        macro_close(f);
                    132: }
                    133:
                    134: /*
                    135:  * Print an argument string on a macro line, collapsing whitespace.
                    136:  */
                    137: void
                    138: macro_addarg(struct format *f, const char *arg, int flags)
                    139: {
                    140:        const char      *cp;
                    141:
                    142:        assert(f->linestate == LINE_MACRO);
                    143:
                    144:        /* Quote if requested and necessary. */
                    145:
                    146:        if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
                    147:                for (cp = arg; *cp != '\0'; cp++)
                    148:                        if (isspace((unsigned char)*cp))
                    149:                                break;
                    150:                if (*cp != '\0') {
                    151:                        if (flags & ARG_SPACE) {
                    152:                                putchar(' ');
                    153:                                flags &= ~ ARG_SPACE;
                    154:                        }
                    155:                        putchar('"');
                    156:                        flags = ARG_QUOTED;
                    157:                }
                    158:        }
                    159:
                    160:        for (cp = arg; *cp != '\0'; cp++) {
                    161:
                    162:                /* Collapse whitespace. */
                    163:
                    164:                if (isspace((unsigned char)*cp)) {
                    165:                        flags |= ARG_SPACE;
                    166:                        continue;
                    167:                } else if (flags & ARG_SPACE) {
                    168:                        putchar(' ');
                    169:                        flags &= ~ ARG_SPACE;
                    170:                }
                    171:
                    172:                /* Escape us if we look like a macro. */
                    173:
                    174:                if ((flags & ARG_QUOTED) == 0 &&
                    175:                    (cp == arg || isspace((unsigned char)cp[-1])) &&
                    176:                    isupper((unsigned char)cp[0]) &&
                    177:                    islower((unsigned char)cp[1]) &&
                    178:                    (cp[2] == '\0' || cp[2] == ' ' ||
                    179:                     (islower((unsigned char)cp[2]) &&
                    180:                      (cp[3] == '\0' || cp[3] == ' '))))
                    181:                        fputs("\\&", stdout);
                    182:
                    183:                if (*cp == '"')
                    184:                        fputs("\\(dq", stdout);
                    185:                else if (flags & ARG_UPPER)
                    186:                        putchar(toupper((unsigned char)*cp));
                    187:                else
                    188:                        putchar(*cp);
                    189:                if (*cp == '\\')
                    190:                        putchar('e');
                    191:        }
                    192: }
                    193:
                    194: void
                    195: macro_argline(struct format *f, const char *name, const char *arg)
                    196: {
                    197:        macro_open(f, name);
                    198:        macro_addarg(f, arg, ARG_SPACE);
                    199:        macro_close(f);
                    200: }
                    201:
                    202: /*
                    203:  * Recursively append text from the children of a node to a macro line.
                    204:  */
                    205: void
                    206: macro_addnode(struct format *f, struct pnode *pn, int flags)
                    207: {
1.5       schwarze  208:        struct pnode    *nc;
1.1       schwarze  209:        int              quote_now;
                    210:
                    211:        assert(f->linestate == LINE_MACRO);
                    212:
                    213:        /*
1.2       schwarze  214:         * If this node or its only child is a text node, just add
                    215:         * that text, letting macro_addarg() decide about quoting.
1.1       schwarze  216:         */
                    217:
1.5       schwarze  218:        while ((nc = TAILQ_FIRST(&pn->childq)) != NULL &&
                    219:            TAILQ_NEXT(nc, child) == NULL)
                    220:                pn = nc;
                    221:
                    222:        if (pn->node == NODE_TEXT || pn->node == NODE_ESCAPE) {
1.1       schwarze  223:                macro_addarg(f, pn->b, flags);
                    224:                return;
                    225:        }
                    226:
                    227:        /*
                    228:         * If we want the argument quoted and are not already
                    229:         * in a quoted context, quote now.
                    230:         */
                    231:
                    232:        quote_now = 0;
                    233:        if (flags & ARG_SINGLE) {
                    234:                if ((flags & ARG_QUOTED) == 0) {
                    235:                        if (flags & ARG_SPACE) {
                    236:                                putchar(' ');
                    237:                                flags &= ~ARG_SPACE;
                    238:                        }
                    239:                        putchar('"');
                    240:                        flags |= ARG_QUOTED;
                    241:                        quote_now = 1;
                    242:                }
                    243:                flags &= ~ARG_SINGLE;
                    244:        }
                    245:
                    246:        /*
                    247:         * Iterate to child and sibling nodes,
                    248:         * inserting whitespace between nodes.
                    249:         */
                    250:
1.5       schwarze  251:        while (nc != NULL) {
                    252:                macro_addnode(f, nc, flags);
                    253:                nc = TAILQ_NEXT(nc, child);
1.1       schwarze  254:                flags |= ARG_SPACE;
                    255:        }
                    256:        if (quote_now)
                    257:                putchar('"');
                    258: }
                    259:
                    260: void
                    261: macro_nodeline(struct format *f, const char *name, struct pnode *pn, int flags)
                    262: {
                    263:        macro_open(f, name);
                    264:        macro_addnode(f, pn, ARG_SPACE | flags);
                    265:        macro_close(f);
1.2       schwarze  266: }
                    267:
                    268:
                    269: /*
                    270:  * Print a word on the current text line if one is open, or on a new text
                    271:  * line otherwise.  The flag ARG_SPACE inserts spaces between words.
                    272:  */
                    273: void
                    274: print_text(struct format *f, const char *word, int flags) {
                    275:        switch (f->linestate) {
                    276:        case LINE_NEW:
                    277:                break;
                    278:        case LINE_TEXT:
                    279:                if (flags & ARG_SPACE)
                    280:                        putchar(' ');
                    281:                break;
                    282:        case LINE_MACRO:
                    283:                macro_close(f);
                    284:                break;
                    285:        }
                    286:        fputs(word, stdout);
                    287:        f->linestate = LINE_TEXT;
                    288: }
                    289:
                    290: /*
                    291:  * Recursively print the content of a node on a text line.
                    292:  */
                    293: void
                    294: print_textnode(struct format *f, struct pnode *n)
                    295: {
                    296:        struct pnode    *nc;
                    297:
1.3       schwarze  298:        if (n->node == NODE_TEXT || n->node == NODE_ESCAPE)
1.2       schwarze  299:                print_text(f, n->b, ARG_SPACE);
                    300:        else
                    301:                TAILQ_FOREACH(nc, &n->childq, child)
                    302:                        print_textnode(f, nc);
1.1       schwarze  303: }

CVSweb