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

Annotation of docbook2mdoc/macro.c, Revision 1.21

1.21    ! schwarze    1: /* $Id: macro.c,v 1.20 2019/05/02 12:40:42 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
1.18      schwarze   31: para_check(struct format *f)
                     32: {
                     33:        if (f->parastate != PARA_WANT)
                     34:                return;
                     35:        if (f->linestate != LINE_NEW) {
                     36:                putchar('\n');
                     37:                f->linestate = LINE_NEW;
                     38:        }
                     39:        puts(".Pp");
                     40:        f->parastate = PARA_HAVE;
                     41: }
                     42:
                     43: void
1.1       schwarze   44: macro_open(struct format *f, const char *name)
                     45: {
1.18      schwarze   46:        para_check(f);
1.1       schwarze   47:        switch (f->linestate) {
1.9       schwarze   48:        case LINE_MACRO:
                     49:                if (f->flags & FMT_NOSPC) {
                     50:                        fputs(" Ns ", stdout);
                     51:                        break;
                     52:                }
1.19      schwarze   53:                if (f->nofill || f->flags & (FMT_CHILD | FMT_IMPL)) {
1.9       schwarze   54:                        putchar(' ');
                     55:                        break;
                     56:                }
                     57:                /* FALLTHROUGH */
1.1       schwarze   58:        case LINE_TEXT:
1.18      schwarze   59:                if (f->nofill && f->linestate == LINE_TEXT)
1.17      schwarze   60:                        fputs(" \\c", stdout);
1.1       schwarze   61:                putchar('\n');
                     62:                /* FALLTHROUGH */
                     63:        case LINE_NEW:
                     64:                putchar('.');
                     65:                f->linestate = LINE_MACRO;
1.9       schwarze   66:                f->flags = 0;
1.1       schwarze   67:                break;
                     68:        }
                     69:        fputs(name, stdout);
1.9       schwarze   70:        f->flags &= FMT_IMPL;
                     71:        f->flags |= FMT_ARG;
1.14      schwarze   72:        f->parastate = PARA_MID;
1.1       schwarze   73: }
                     74:
                     75: void
                     76: macro_close(struct format *f)
                     77: {
1.9       schwarze   78:        if (f->linestate != LINE_NEW)
                     79:                putchar('\n');
1.1       schwarze   80:        f->linestate = LINE_NEW;
1.9       schwarze   81:        f->flags = 0;
1.1       schwarze   82: }
                     83:
                     84: void
                     85: macro_line(struct format *f, const char *name)
                     86: {
1.7       schwarze   87:        macro_close(f);
1.1       schwarze   88:        macro_open(f, name);
                     89:        macro_close(f);
                     90: }
                     91:
                     92: /*
                     93:  * Print an argument string on a macro line, collapsing whitespace.
                     94:  */
                     95: void
                     96: macro_addarg(struct format *f, const char *arg, int flags)
                     97: {
                     98:        const char      *cp;
1.13      schwarze   99:        int              quote_now;
1.1       schwarze  100:
                    101:        assert(f->linestate == LINE_MACRO);
                    102:
                    103:        /* Quote if requested and necessary. */
                    104:
1.13      schwarze  105:        quote_now = 0;
1.1       schwarze  106:        if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
                    107:                for (cp = arg; *cp != '\0'; cp++)
                    108:                        if (isspace((unsigned char)*cp))
                    109:                                break;
                    110:                if (*cp != '\0') {
                    111:                        if (flags & ARG_SPACE) {
                    112:                                putchar(' ');
                    113:                                flags &= ~ ARG_SPACE;
                    114:                        }
                    115:                        putchar('"');
                    116:                        flags = ARG_QUOTED;
1.13      schwarze  117:                        quote_now = 1;
1.1       schwarze  118:                }
                    119:        }
                    120:
                    121:        for (cp = arg; *cp != '\0'; cp++) {
                    122:
                    123:                /* Collapse whitespace. */
                    124:
                    125:                if (isspace((unsigned char)*cp)) {
                    126:                        flags |= ARG_SPACE;
                    127:                        continue;
                    128:                } else if (flags & ARG_SPACE) {
                    129:                        putchar(' ');
                    130:                        flags &= ~ ARG_SPACE;
                    131:                }
                    132:
1.21    ! schwarze  133:                /* For XML entities, skip escaping. */
        !           134:
        !           135:                if (flags & ARG_RAW) {
        !           136:                        fputs(arg, stdout);
        !           137:                        break;
        !           138:                }
        !           139:
1.1       schwarze  140:                /* Escape us if we look like a macro. */
                    141:
1.12      schwarze  142:                if ((flags & (ARG_QUOTED | ARG_UPPER)) == 0 &&
1.1       schwarze  143:                    (cp == arg || isspace((unsigned char)cp[-1])) &&
                    144:                    isupper((unsigned char)cp[0]) &&
                    145:                    islower((unsigned char)cp[1]) &&
                    146:                    (cp[2] == '\0' || cp[2] == ' ' ||
1.12      schwarze  147:                     ((cp[3] == '\0' || cp[3] == ' ') &&
                    148:                      (strncmp(cp, "Brq", 3) == 0 ||
                    149:                       strncmp(cp, "Bro", 3) == 0 ||
                    150:                       strncmp(cp, "Brc", 3) == 0 ||
                    151:                       strncmp(cp, "Bsx", 3) == 0))))
1.1       schwarze  152:                        fputs("\\&", stdout);
                    153:
                    154:                if (*cp == '"')
                    155:                        fputs("\\(dq", stdout);
                    156:                else if (flags & ARG_UPPER)
                    157:                        putchar(toupper((unsigned char)*cp));
                    158:                else
                    159:                        putchar(*cp);
                    160:                if (*cp == '\\')
                    161:                        putchar('e');
                    162:        }
1.13      schwarze  163:        if (quote_now)
                    164:                putchar('"');
1.14      schwarze  165:        f->parastate = PARA_MID;
1.1       schwarze  166: }
                    167:
                    168: void
                    169: macro_argline(struct format *f, const char *name, const char *arg)
                    170: {
                    171:        macro_open(f, name);
                    172:        macro_addarg(f, arg, ARG_SPACE);
                    173:        macro_close(f);
                    174: }
                    175:
                    176: /*
                    177:  * Recursively append text from the children of a node to a macro line.
                    178:  */
                    179: void
1.8       schwarze  180: macro_addnode(struct format *f, struct pnode *n, int flags)
1.1       schwarze  181: {
1.5       schwarze  182:        struct pnode    *nc;
1.15      schwarze  183:        int              is_text, quote_now;
1.1       schwarze  184:
                    185:        assert(f->linestate == LINE_MACRO);
                    186:
                    187:        /*
1.2       schwarze  188:         * If this node or its only child is a text node, just add
                    189:         * that text, letting macro_addarg() decide about quoting.
1.1       schwarze  190:         */
                    191:
1.8       schwarze  192:        while ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
1.5       schwarze  193:            TAILQ_NEXT(nc, child) == NULL)
1.8       schwarze  194:                n = nc;
1.5       schwarze  195:
1.21    ! schwarze  196:        switch (n->node) {
        !           197:        case NODE_ESCAPE:
        !           198:                flags |= ARG_RAW;
        !           199:                /* FALLTHROUGH */
        !           200:        case NODE_TEXT:
1.8       schwarze  201:                macro_addarg(f, n->b, flags);
1.14      schwarze  202:                f->parastate = PARA_MID;
1.1       schwarze  203:                return;
1.21    ! schwarze  204:        default:
        !           205:                break;
1.1       schwarze  206:        }
                    207:
                    208:        /*
                    209:         * If we want the argument quoted and are not already
                    210:         * in a quoted context, quote now.
                    211:         */
                    212:
                    213:        quote_now = 0;
                    214:        if (flags & ARG_SINGLE) {
                    215:                if ((flags & ARG_QUOTED) == 0) {
                    216:                        if (flags & ARG_SPACE) {
                    217:                                putchar(' ');
                    218:                                flags &= ~ARG_SPACE;
                    219:                        }
                    220:                        putchar('"');
                    221:                        flags |= ARG_QUOTED;
                    222:                        quote_now = 1;
                    223:                }
                    224:                flags &= ~ARG_SINGLE;
                    225:        }
                    226:
                    227:        /*
                    228:         * Iterate to child and sibling nodes,
                    229:         * inserting whitespace between nodes.
                    230:         */
                    231:
1.5       schwarze  232:        while (nc != NULL) {
                    233:                macro_addnode(f, nc, flags);
1.15      schwarze  234:                is_text = pnode_class(nc->node) == CLASS_TEXT;
1.5       schwarze  235:                nc = TAILQ_NEXT(nc, child);
1.15      schwarze  236:                if (nc == NULL || pnode_class(nc->node) != CLASS_TEXT)
                    237:                        is_text = 0;
1.16      schwarze  238:                if (is_text && (nc->flags & NFLAG_SPC) == 0)
1.15      schwarze  239:                        flags &= ~ARG_SPACE;
                    240:                else
                    241:                        flags |= ARG_SPACE;
1.1       schwarze  242:        }
                    243:        if (quote_now)
                    244:                putchar('"');
1.14      schwarze  245:        f->parastate = PARA_MID;
1.1       schwarze  246: }
                    247:
                    248: void
1.8       schwarze  249: macro_nodeline(struct format *f, const char *name, struct pnode *n, int flags)
1.1       schwarze  250: {
                    251:        macro_open(f, name);
1.8       schwarze  252:        macro_addnode(f, n, ARG_SPACE | flags);
1.1       schwarze  253:        macro_close(f);
1.2       schwarze  254: }
                    255:
                    256:
                    257: /*
                    258:  * Print a word on the current text line if one is open, or on a new text
                    259:  * line otherwise.  The flag ARG_SPACE inserts spaces between words.
                    260:  */
                    261: void
1.10      schwarze  262: print_text(struct format *f, const char *word, int flags)
                    263: {
1.20      schwarze  264:        int      ateos, inword;
                    265:
1.18      schwarze  266:        para_check(f);
1.2       schwarze  267:        switch (f->linestate) {
                    268:        case LINE_NEW:
                    269:                break;
                    270:        case LINE_TEXT:
                    271:                if (flags & ARG_SPACE)
                    272:                        putchar(' ');
                    273:                break;
                    274:        case LINE_MACRO:
                    275:                macro_close(f);
                    276:                break;
                    277:        }
1.11      schwarze  278:        if (f->linestate == LINE_NEW && (*word == '.' || *word == '\''))
                    279:                fputs("\\&", stdout);
1.20      schwarze  280:        ateos = inword = 0;
1.10      schwarze  281:        while (*word != '\0') {
1.20      schwarze  282:                if (f->nofill == 0) {
                    283:                        switch (*word) {
                    284:                        case ' ':
                    285:                                if (ateos == 0) {
                    286:                                        inword = 0;
                    287:                                        break;
                    288:                                }
                    289:                                ateos = inword = 0;
                    290:                                /* Handle the end of a sentence. */
                    291:                                while (*word == ' ')
                    292:                                        word++;
                    293:                                switch (*word) {
                    294:                                case '\0':
                    295:                                        break;
                    296:                                case '\'':
                    297:                                case '.':
                    298:                                        fputs("\n\\&", stdout);
                    299:                                        break;
                    300:                                default:
                    301:                                        putchar('\n');
                    302:                                        break;
                    303:                                }
                    304:                                continue;
                    305:                        /* Detect the end of a sentence. */
                    306:                        case '!':
                    307:                        case '.':
                    308:                        case '?':
                    309:                                if (inword > 1 &&
                    310:                                    (word[-2] != 'n' || word[-1] != 'c') &&
                    311:                                    (word[-2] != 'v' || word[-1] != 's'))
                    312:                                        ateos = 1;
                    313:                                /* FALLTHROUGH */
                    314:                        case '"':
                    315:                        case '\'':
                    316:                        case ')':
                    317:                        case ']':
                    318:                                inword = 0;
                    319:                                break;
                    320:                        default:
                    321:                                if (isalnum((unsigned char)*word))
                    322:                                        inword++;
                    323:                                ateos = 0;
                    324:                                break;
                    325:                        }
                    326:                }
1.10      schwarze  327:                putchar(*word);
                    328:                if (*word++ == '\\')
                    329:                        putchar('e');
                    330:        }
1.2       schwarze  331:        f->linestate = LINE_TEXT;
1.14      schwarze  332:        f->parastate = PARA_MID;
1.9       schwarze  333:        f->flags = 0;
1.2       schwarze  334: }
                    335:
                    336: /*
                    337:  * Recursively print the content of a node on a text line.
                    338:  */
                    339: void
                    340: print_textnode(struct format *f, struct pnode *n)
                    341: {
                    342:        struct pnode    *nc;
                    343:
1.3       schwarze  344:        if (n->node == NODE_TEXT || n->node == NODE_ESCAPE)
1.2       schwarze  345:                print_text(f, n->b, ARG_SPACE);
                    346:        else
                    347:                TAILQ_FOREACH(nc, &n->childq, child)
                    348:                        print_textnode(f, nc);
1.1       schwarze  349: }

CVSweb