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

Annotation of docbook2mdoc/macro.c, Revision 1.6

1.6     ! schwarze    1: /* $Id: macro.c,v 1.5 2019/04/03 15:04:03 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: {
                     62:        macro_open(f, name);
                     63:        macro_close(f);
                     64: }
                     65:
                     66: /*
1.6     ! schwarze   67:  * At the end of a macro, decide whether the line needs to remain open
        !            68:  * because the next node follows without intervening whitespace;
        !            69:  * otherwise, close the line.
1.1       schwarze   70:  */
                     71: void
                     72: macro_closepunct(struct format *f, struct pnode *pn)
                     73: {
1.6     ! schwarze   74:        char            *cp;
        !            75:
        !            76:        if ((pn = TAILQ_NEXT(pn, child)) != NULL && pn->spc == 0) {
        !            77:
        !            78:                /*
        !            79:                 * If a non-text node follows without intervening
        !            80:                 * whitespace, the handler of that node will decide
        !            81:                 * whether and how to suppress whitespace.  To allow
        !            82:                 * that, the macro line needs to remain open.
        !            83:                 */
        !            84:
        !            85:                if (pn->node != NODE_TEXT && pn->node != NODE_ESCAPE)
        !            86:                        return;
        !            87:
        !            88:                /*
        !            89:                 * Give closing punctuation
        !            90:                 * in the form of trailing macro arguments.
        !            91:                 */
        !            92:
        !            93:                while (*pn->b != '\0' &&
        !            94:                    strchr("!),.:;?]", *pn->b) != NULL) {
        !            95:                        putchar(' ');
        !            96:                        putchar(*pn->b);
        !            97:                        pn->b++;
        !            98:                        pn->bsz--;
        !            99:                }
        !           100:
        !           101:                /*
        !           102:                 * Text follows without intervening whitespace.
        !           103:                 * Append the first word with .Ns.
        !           104:                 */
        !           105:
        !           106:                if (*pn->b != '\0' && isspace((unsigned char)*pn->b) == 0) {
        !           107:                        fputs(" Ns", stdout);
        !           108:                        for (cp = pn->b; *cp != '\0'; cp++)
        !           109:                                if (isspace((unsigned char)*cp))
        !           110:                                        break;
        !           111:                        *cp = '\0';
        !           112:                        macro_addarg(f, pn->b, ARG_SPACE);
        !           113:                        pn->bsz -= cp - pn->b;
        !           114:                        pn->b = cp;
        !           115:                        if (pn->bsz > 0) {
        !           116:                                pn->b++;
        !           117:                                pn->bsz--;
        !           118:                                pn->spc = 1;
        !           119:                        }
        !           120:                }
        !           121:
        !           122:                /* Skip whitespace after the first word. */
        !           123:
        !           124:                while (isspace((unsigned char)*pn->b)) {
        !           125:                        pn->b++;
        !           126:                        pn->bsz--;
        !           127:                        pn->spc = 1;
        !           128:                }
1.1       schwarze  129:        }
                    130:        macro_close(f);
                    131: }
                    132:
                    133: /*
                    134:  * Print an argument string on a macro line, collapsing whitespace.
                    135:  */
                    136: void
                    137: macro_addarg(struct format *f, const char *arg, int flags)
                    138: {
                    139:        const char      *cp;
                    140:
                    141:        assert(f->linestate == LINE_MACRO);
                    142:
                    143:        /* Quote if requested and necessary. */
                    144:
                    145:        if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
                    146:                for (cp = arg; *cp != '\0'; cp++)
                    147:                        if (isspace((unsigned char)*cp))
                    148:                                break;
                    149:                if (*cp != '\0') {
                    150:                        if (flags & ARG_SPACE) {
                    151:                                putchar(' ');
                    152:                                flags &= ~ ARG_SPACE;
                    153:                        }
                    154:                        putchar('"');
                    155:                        flags = ARG_QUOTED;
                    156:                }
                    157:        }
                    158:
                    159:        for (cp = arg; *cp != '\0'; cp++) {
                    160:
                    161:                /* Collapse whitespace. */
                    162:
                    163:                if (isspace((unsigned char)*cp)) {
                    164:                        flags |= ARG_SPACE;
                    165:                        continue;
                    166:                } else if (flags & ARG_SPACE) {
                    167:                        putchar(' ');
                    168:                        flags &= ~ ARG_SPACE;
                    169:                }
                    170:
                    171:                /* Escape us if we look like a macro. */
                    172:
                    173:                if ((flags & ARG_QUOTED) == 0 &&
                    174:                    (cp == arg || isspace((unsigned char)cp[-1])) &&
                    175:                    isupper((unsigned char)cp[0]) &&
                    176:                    islower((unsigned char)cp[1]) &&
                    177:                    (cp[2] == '\0' || cp[2] == ' ' ||
                    178:                     (islower((unsigned char)cp[2]) &&
                    179:                      (cp[3] == '\0' || cp[3] == ' '))))
                    180:                        fputs("\\&", stdout);
                    181:
                    182:                if (*cp == '"')
                    183:                        fputs("\\(dq", stdout);
                    184:                else if (flags & ARG_UPPER)
                    185:                        putchar(toupper((unsigned char)*cp));
                    186:                else
                    187:                        putchar(*cp);
                    188:                if (*cp == '\\')
                    189:                        putchar('e');
                    190:        }
                    191: }
                    192:
                    193: void
                    194: macro_argline(struct format *f, const char *name, const char *arg)
                    195: {
                    196:        macro_open(f, name);
                    197:        macro_addarg(f, arg, ARG_SPACE);
                    198:        macro_close(f);
                    199: }
                    200:
                    201: /*
                    202:  * Recursively append text from the children of a node to a macro line.
                    203:  */
                    204: void
                    205: macro_addnode(struct format *f, struct pnode *pn, int flags)
                    206: {
1.5       schwarze  207:        struct pnode    *nc;
1.1       schwarze  208:        int              quote_now;
                    209:
                    210:        assert(f->linestate == LINE_MACRO);
                    211:
                    212:        /*
1.2       schwarze  213:         * If this node or its only child is a text node, just add
                    214:         * that text, letting macro_addarg() decide about quoting.
1.1       schwarze  215:         */
                    216:
1.5       schwarze  217:        while ((nc = TAILQ_FIRST(&pn->childq)) != NULL &&
                    218:            TAILQ_NEXT(nc, child) == NULL)
                    219:                pn = nc;
                    220:
                    221:        if (pn->node == NODE_TEXT || pn->node == NODE_ESCAPE) {
1.1       schwarze  222:                macro_addarg(f, pn->b, flags);
                    223:                return;
                    224:        }
                    225:
                    226:        /*
                    227:         * If we want the argument quoted and are not already
                    228:         * in a quoted context, quote now.
                    229:         */
                    230:
                    231:        quote_now = 0;
                    232:        if (flags & ARG_SINGLE) {
                    233:                if ((flags & ARG_QUOTED) == 0) {
                    234:                        if (flags & ARG_SPACE) {
                    235:                                putchar(' ');
                    236:                                flags &= ~ARG_SPACE;
                    237:                        }
                    238:                        putchar('"');
                    239:                        flags |= ARG_QUOTED;
                    240:                        quote_now = 1;
                    241:                }
                    242:                flags &= ~ARG_SINGLE;
                    243:        }
                    244:
                    245:        /*
                    246:         * Iterate to child and sibling nodes,
                    247:         * inserting whitespace between nodes.
                    248:         */
                    249:
1.5       schwarze  250:        while (nc != NULL) {
                    251:                macro_addnode(f, nc, flags);
                    252:                nc = TAILQ_NEXT(nc, child);
1.1       schwarze  253:                flags |= ARG_SPACE;
                    254:        }
                    255:        if (quote_now)
                    256:                putchar('"');
                    257: }
                    258:
                    259: void
                    260: macro_nodeline(struct format *f, const char *name, struct pnode *pn, int flags)
                    261: {
                    262:        macro_open(f, name);
                    263:        macro_addnode(f, pn, ARG_SPACE | flags);
                    264:        macro_close(f);
1.2       schwarze  265: }
                    266:
                    267:
                    268: /*
                    269:  * Print a word on the current text line if one is open, or on a new text
                    270:  * line otherwise.  The flag ARG_SPACE inserts spaces between words.
                    271:  */
                    272: void
                    273: print_text(struct format *f, const char *word, int flags) {
                    274:        switch (f->linestate) {
                    275:        case LINE_NEW:
                    276:                break;
                    277:        case LINE_TEXT:
                    278:                if (flags & ARG_SPACE)
                    279:                        putchar(' ');
                    280:                break;
                    281:        case LINE_MACRO:
                    282:                macro_close(f);
                    283:                break;
                    284:        }
                    285:        fputs(word, stdout);
                    286:        f->linestate = LINE_TEXT;
                    287: }
                    288:
                    289: /*
                    290:  * Recursively print the content of a node on a text line.
                    291:  */
                    292: void
                    293: print_textnode(struct format *f, struct pnode *n)
                    294: {
                    295:        struct pnode    *nc;
                    296:
1.3       schwarze  297:        if (n->node == NODE_TEXT || n->node == NODE_ESCAPE)
1.2       schwarze  298:                print_text(f, n->b, ARG_SPACE);
                    299:        else
                    300:                TAILQ_FOREACH(nc, &n->childq, child)
                    301:                        print_textnode(f, nc);
1.1       schwarze  302: }

CVSweb