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

Annotation of mandoc/mdocterm.c, Revision 1.3

1.3     ! kristaps    1:        /* $Id: mdocterm.c,v 1.2 2009/02/22 22:58:39 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
                      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
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19: #include <assert.h>
1.3     ! kristaps   20: #include <ctype.h>
1.1       kristaps   21: #include <err.h>
                     22: #include <getopt.h>
1.3     ! kristaps   23: #include <stdio.h>
1.1       kristaps   24: #include <stdlib.h>
1.3     ! kristaps   25: #include <string.h>
1.1       kristaps   26:
1.2       kristaps   27: #include "mmain.h"
1.1       kristaps   28: #include "term.h"
                     29:
1.3     ! kristaps   30: enum   termstyle {
        !            31:        STYLE_CLEAR,
        !            32:        STYLE_BOLD,
        !            33:        STYLE_UNDERLINE
        !            34: };
        !            35:
        !            36: static void              body(struct termp *,
        !            37:                                const struct mdoc_meta *,
        !            38:                                const struct mdoc_node *);
        !            39: static void              header(struct termp *,
        !            40:                                const struct mdoc_meta *);
        !            41: static void              footer(struct termp *,
        !            42:                                const struct mdoc_meta *);
        !            43:
        !            44: static void              pword(struct termp *, const char *, size_t);
        !            45: static void              pescape(struct termp *,
        !            46:                                const char *, size_t *, size_t);
        !            47: static void              chara(struct termp *, char);
        !            48: static void              style(struct termp *, enum termstyle);
        !            49:
        !            50: #ifdef __linux__
        !            51: extern size_t            strlcat(char *, const char *, size_t);
        !            52: extern size_t            strlcpy(char *, const char *, size_t);
        !            53: #endif
        !            54:
        !            55:
1.1       kristaps   56: int
                     57: main(int argc, char *argv[])
                     58: {
1.2       kristaps   59:        struct mmain    *p;
                     60:        const struct mdoc *mdoc;
1.3     ! kristaps   61:        struct termp     termp;
1.2       kristaps   62:
                     63:        extern int       optreset;
1.1       kristaps   64:        extern int       optind;
                     65:
1.2       kristaps   66:        p = mmain_alloc();
1.1       kristaps   67:
1.3     ! kristaps   68:        if ( ! mmain_getopt(p, argc, argv, NULL, NULL, NULL, NULL))
1.2       kristaps   69:                mmain_exit(p, 1);
1.1       kristaps   70:
1.3     ! kristaps   71:        if (NULL == (mdoc = mmain_mdoc(p)))
        !            72:                mmain_exit(p, 1);
        !            73:
        !            74:        termp.maxrmargin = 80; /* XXX */
        !            75:        termp.rmargin = termp.maxrmargin;
        !            76:        termp.maxcols = 1024;
        !            77:        termp.offset = termp.col = 0;
        !            78:        termp.flags = TERMP_NOSPACE;
        !            79:
        !            80:        if (NULL == (termp.buf = malloc(termp.maxcols)))
        !            81:                err(1, "malloc");
        !            82:
        !            83:        header(&termp, mdoc_meta(mdoc));
        !            84:        body(&termp, mdoc_meta(mdoc), mdoc_node(mdoc));
        !            85:        footer(&termp, mdoc_meta(mdoc));
        !            86:
        !            87:        free(termp.buf);
        !            88:
        !            89:        mmain_exit(p, 0);
        !            90:        /* NOTREACHED */
        !            91: }
        !            92:
        !            93:
        !            94: void
        !            95: flushln(struct termp *p)
        !            96: {
        !            97:        size_t           i, j, vsz, vis, maxvis;
        !            98:
        !            99:        /*
        !           100:         * First, establish the maximum columns of "visible" content.
        !           101:         * This is usually the difference between the right-margin and
        !           102:         * an indentation, but can be, for tagged lists or columns, a
        !           103:         * small set of values.
        !           104:         */
        !           105:
        !           106:        assert(p->offset < p->rmargin);
        !           107:        maxvis = p->rmargin - p->offset;
        !           108:        vis = 0;
        !           109:
        !           110:        /*
        !           111:         * If in the standard case (left-justified), then begin with our
        !           112:         * indentation, otherwise (columns, etc.) just start spitting
        !           113:         * out text.
        !           114:         */
        !           115:
        !           116:        if ( ! (p->flags & TERMP_NOLPAD))
        !           117:                /* LINTED */
        !           118:                for (j = 0; j < p->offset; j++)
        !           119:                        putchar(' ');
        !           120:
        !           121:        /*
        !           122:         * If we're literal, print out verbatim.
        !           123:         */
        !           124:        if (p->flags & TERMP_LITERAL) {
        !           125:                /* FIXME: count non-printing chars. */
        !           126:                for (i = 0; i < p->col; i++)
        !           127:                        putchar(p->buf[i]);
        !           128:                putchar('\n');
        !           129:                p->col = 0;
        !           130:                return;
        !           131:        }
        !           132:
        !           133:        for (i = 0; i < p->col; i++) {
        !           134:                /*
        !           135:                 * Count up visible word characters.  Control sequences
        !           136:                 * (starting with the CSI) aren't counted.
        !           137:                 */
        !           138:                assert( ! isspace(p->buf[i]));
        !           139:
        !           140:                /* LINTED */
        !           141:                for (j = i, vsz = 0; j < p->col; j++) {
        !           142:                        if (isspace(p->buf[j]))
        !           143:                                break;
        !           144:                        else if (27 == p->buf[j]) {
        !           145:                                assert(j + 4 <= p->col);
        !           146:                                j += 3;
        !           147:                        } else
        !           148:                                vsz++;
        !           149:                }
        !           150:                assert(vsz > 0);
        !           151:
        !           152:                /*
        !           153:                 * If a word is too long and we're within a line, put it
        !           154:                 * on the next line.  Puke if we're being asked to write
        !           155:                 * something that will exceed the right margin (i.e.,
        !           156:                 * from a fresh line or when we're not allowed to break
        !           157:                 * the line with TERMP_NOBREAK).
        !           158:                 */
        !           159:
        !           160:                if (vis && vis + vsz >= maxvis) {
        !           161:                        /* FIXME */
        !           162:                        if (p->flags & TERMP_NOBREAK)
        !           163:                                errx(1, "word breaks right margin");
        !           164:                        putchar('\n');
        !           165:                        for (j = 0; j < p->offset; j++)
        !           166:                                putchar(' ');
        !           167:                        vis = 0;
        !           168:                } else if (vis + vsz >= maxvis) {
        !           169:                        /* FIXME */
        !           170:                        errx(1, "word breaks right margin");
        !           171:                }
        !           172:
        !           173:                /*
        !           174:                 * Write out the word and a trailing space.  Omit the
        !           175:                 * space if we're the last word in the line.
        !           176:                 */
        !           177:
        !           178:                for ( ; i < p->col; i++) {
        !           179:                        if (isspace(p->buf[i]))
        !           180:                                break;
        !           181:                        putchar(p->buf[i]);
        !           182:                }
        !           183:                vis += vsz;
        !           184:                if (i < p->col) {
        !           185:                        putchar(' ');
        !           186:                        vis++;
        !           187:                }
        !           188:        }
        !           189:
        !           190:        /*
        !           191:         * If we're not to right-marginalise it (newline), then instead
        !           192:         * pad to the right margin and stay off.
        !           193:         */
        !           194:
        !           195:        if (p->flags & TERMP_NOBREAK) {
        !           196:                for ( ; vis <= maxvis; vis++)
        !           197:                        putchar(' ');
        !           198:        } else
        !           199:                putchar('\n');
        !           200:
        !           201:        p->col = 0;
        !           202: }
        !           203:
        !           204:
        !           205: void
        !           206: newln(struct termp *p)
        !           207: {
1.1       kristaps  208:
1.3     ! kristaps  209:        /*
        !           210:         * A newline only breaks an existing line; it won't assert
        !           211:         * vertical space.
        !           212:         */
        !           213:        p->flags |= TERMP_NOSPACE;
        !           214:        if (0 == p->col)
        !           215:                return;
        !           216:        flushln(p);
        !           217: }
        !           218:
        !           219:
        !           220: void
        !           221: vspace(struct termp *p)
        !           222: {
        !           223:
        !           224:        /*
        !           225:         * Asserts a vertical space (a full, empty line-break between
        !           226:         * lines).
        !           227:         */
        !           228:        newln(p);
        !           229:        putchar('\n');
        !           230: }
        !           231:
        !           232:
        !           233: static void
        !           234: chara(struct termp *p, char c)
        !           235: {
        !           236:
        !           237:        /* TODO: dynamically expand the buffer. */
        !           238:        if (p->col + 1 >= p->maxcols)
        !           239:                errx(1, "line overrun");
        !           240:        p->buf[(p->col)++] = c;
        !           241: }
        !           242:
        !           243:
        !           244: static void
        !           245: style(struct termp *p, enum termstyle esc)
        !           246: {
        !           247:
        !           248:        if (p->col + 4 >= p->maxcols)
        !           249:                errx(1, "line overrun");
        !           250:
        !           251:        p->buf[(p->col)++] = 27;
        !           252:        p->buf[(p->col)++] = '[';
        !           253:        switch (esc) {
        !           254:        case (STYLE_CLEAR):
        !           255:                p->buf[(p->col)++] = '0';
        !           256:                break;
        !           257:        case (STYLE_BOLD):
        !           258:                p->buf[(p->col)++] = '1';
        !           259:                break;
        !           260:        case (STYLE_UNDERLINE):
        !           261:                p->buf[(p->col)++] = '4';
        !           262:                break;
        !           263:        default:
        !           264:                abort();
        !           265:                /* NOTREACHED */
        !           266:        }
        !           267:        p->buf[(p->col)++] = 'm';
        !           268: }
        !           269:
        !           270:
        !           271: static void
        !           272: pescape(struct termp *p, const char *word, size_t *i, size_t len)
        !           273: {
        !           274:
        !           275:        (*i)++;
        !           276:        assert(*i < len);
        !           277:
        !           278:        if ('(' == word[*i]) {
        !           279:                /* Two-character escapes. */
        !           280:                (*i)++;
        !           281:                assert(*i + 1 < len);
        !           282:
        !           283:                if ('r' == word[*i] && 'B' == word[*i + 1])
        !           284:                        chara(p, ']');
        !           285:                else if ('l' == word[*i] && 'B' == word[*i + 1])
        !           286:                        chara(p, '[');
        !           287:
        !           288:                (*i)++;
        !           289:                return;
        !           290:
        !           291:        } else if ('[' != word[*i]) {
        !           292:                /* One-character escapes. */
        !           293:                switch (word[*i]) {
        !           294:                case ('\\'):
        !           295:                        /* FALLTHROUGH */
        !           296:                case ('\''):
        !           297:                        /* FALLTHROUGH */
        !           298:                case ('`'):
        !           299:                        /* FALLTHROUGH */
        !           300:                case ('-'):
        !           301:                        /* FALLTHROUGH */
        !           302:                case ('.'):
        !           303:                        chara(p, word[*i]);
1.1       kristaps  304:                default:
1.3     ! kristaps  305:                        break;
        !           306:                }
        !           307:                return;
        !           308:        }
        !           309:        /* n-character escapes. */
        !           310: }
        !           311:
        !           312:
        !           313: static void
        !           314: pword(struct termp *p, const char *word, size_t len)
        !           315: {
        !           316:        size_t           i;
        !           317:
        !           318:        /*assert(len > 0);*/ /* Can be, if literal. */
        !           319:
        !           320:        if ( ! (p->flags & TERMP_NOSPACE) &&
        !           321:                        ! (p->flags & TERMP_LITERAL))
        !           322:                chara(p, ' ');
        !           323:
        !           324:        p->flags &= ~TERMP_NOSPACE;
        !           325:
        !           326:        if (p->flags & TERMP_BOLD)
        !           327:                style(p, STYLE_BOLD);
        !           328:        if (p->flags & TERMP_UNDERLINE)
        !           329:                style(p, STYLE_UNDERLINE);
        !           330:
        !           331:        for (i = 0; i < len; i++) {
        !           332:                if ('\\' == word[i]) {
        !           333:                        pescape(p, word, &i, len);
        !           334:                        continue;
        !           335:                }
        !           336:                chara(p, word[i]);
        !           337:        }
        !           338:
        !           339:        if (p->flags & TERMP_BOLD ||
        !           340:                        p->flags & TERMP_UNDERLINE)
        !           341:                style(p, STYLE_CLEAR);
        !           342: }
        !           343:
        !           344:
        !           345: void
        !           346: word(struct termp *p, const char *word)
        !           347: {
        !           348:        size_t           i, j, len;
        !           349:
        !           350:        if (p->flags & TERMP_LITERAL) {
        !           351:                pword(p, word, strlen(word));
        !           352:                return;
        !           353:        }
        !           354:
        !           355:        len = strlen(word);
        !           356:        assert(len > 0);
        !           357:
        !           358:        if (mdoc_isdelim(word)) {
        !           359:                if ( ! (p->flags & TERMP_IGNDELIM))
        !           360:                        p->flags |= TERMP_NOSPACE;
        !           361:                p->flags &= ~TERMP_IGNDELIM;
        !           362:        }
        !           363:
        !           364:        /* LINTED */
        !           365:        for (j = i = 0; i < len; i++) {
        !           366:                if ( ! isspace(word[i])) {
        !           367:                        j++;
        !           368:                        continue;
1.1       kristaps  369:                }
1.3     ! kristaps  370:                if (0 == j)
        !           371:                        continue;
        !           372:                assert(i >= j);
        !           373:                pword(p, &word[i - j], j);
        !           374:                j = 0;
        !           375:        }
        !           376:        if (j > 0) {
        !           377:                assert(i >= j);
        !           378:                pword(p, &word[i - j], j);
        !           379:        }
        !           380: }
        !           381:
        !           382:
        !           383: static void
        !           384: body(struct termp *p, const struct mdoc_meta *meta,
        !           385:                const struct mdoc_node *node)
        !           386: {
        !           387:        int              dochild;
        !           388:
        !           389:        /* Pre-processing. */
        !           390:
        !           391:        dochild = 1;
        !           392:
        !           393:        if (MDOC_TEXT != node->type) {
        !           394:                if (termacts[node->tok].pre)
        !           395:                        if ( ! (*termacts[node->tok].pre)(p, meta, node))
        !           396:                                dochild = 0;
        !           397:        } else /* MDOC_TEXT == node->type */
        !           398:                word(p, node->data.text.string);
        !           399:
        !           400:        /* Children. */
        !           401:
        !           402:        if (dochild && node->child)
        !           403:                body(p, meta, node->child);
        !           404:
        !           405:        /* Post-processing. */
        !           406:
        !           407:        if (MDOC_TEXT != node->type)
        !           408:                if (termacts[node->tok].post)
        !           409:                        (*termacts[node->tok].post)(p, meta, node);
        !           410:
        !           411:        /* Siblings. */
1.1       kristaps  412:
1.3     ! kristaps  413:        if (node->next)
        !           414:                body(p, meta, node->next);
        !           415: }
        !           416:
        !           417:
        !           418: static void
        !           419: footer(struct termp *p, const struct mdoc_meta *meta)
        !           420: {
        !           421:        struct tm       *tm;
        !           422:        char            *buf, *os;
        !           423:        size_t           sz, osz, ssz, i;
        !           424:
        !           425:        if (NULL == (buf = malloc(p->rmargin)))
        !           426:                err(1, "malloc");
        !           427:        if (NULL == (os = malloc(p->rmargin)))
        !           428:                err(1, "malloc");
        !           429:
        !           430:        tm = localtime(&meta->date);
        !           431:
        !           432: #ifdef __linux__
        !           433:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
        !           434: #else
        !           435:        if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
        !           436: #endif
        !           437:                err(1, "strftime");
        !           438:
        !           439:        osz = strlcpy(os, meta->os, p->rmargin);
        !           440:
        !           441:        sz = strlen(buf);
        !           442:        ssz = sz + osz + 1;
        !           443:
        !           444:        if (ssz > p->rmargin) {
        !           445:                ssz -= p->rmargin;
        !           446:                assert(ssz <= osz);
        !           447:                os[osz - ssz] = 0;
        !           448:                ssz = 1;
        !           449:        } else
        !           450:                ssz = p->rmargin - ssz + 1;
        !           451:
        !           452:        printf("\n");
        !           453:        printf("%s", os);
        !           454:        for (i = 0; i < ssz; i++)
        !           455:                printf(" ");
        !           456:
        !           457:        printf("%s\n", buf);
        !           458:        fflush(stdout);
1.1       kristaps  459:
1.3     ! kristaps  460:        free(buf);
        !           461:        free(os);
1.1       kristaps  462: }
                    463:
                    464:
1.3     ! kristaps  465: static void
        !           466: header(struct termp *p, const struct mdoc_meta *meta)
        !           467: {
        !           468:        char            *buf, *title;
        !           469:        const char      *pp, *msec;
        !           470:        size_t           ssz, tsz, ttsz, i;;
        !           471:
        !           472:        if (NULL == (buf = malloc(p->rmargin)))
        !           473:                err(1, "malloc");
        !           474:        if (NULL == (title = malloc(p->rmargin)))
        !           475:                err(1, "malloc");
        !           476:
        !           477:        if (NULL == (pp = mdoc_vol2a(meta->vol)))
        !           478:                switch (meta->msec) {
        !           479:                case (MSEC_1):
        !           480:                        /* FALLTHROUGH */
        !           481:                case (MSEC_6):
        !           482:                        /* FALLTHROUGH */
        !           483:                case (MSEC_7):
        !           484:                        pp = mdoc_vol2a(VOL_URM);
        !           485:                        break;
        !           486:                case (MSEC_8):
        !           487:                        pp = mdoc_vol2a(VOL_SMM);
        !           488:                        break;
        !           489:                case (MSEC_2):
        !           490:                        /* FALLTHROUGH */
        !           491:                case (MSEC_3):
        !           492:                        /* FALLTHROUGH */
        !           493:                case (MSEC_4):
        !           494:                        /* FALLTHROUGH */
        !           495:                case (MSEC_5):
        !           496:                        pp = mdoc_vol2a(VOL_PRM);
        !           497:                        break;
        !           498:                case (MSEC_9):
        !           499:                        pp = mdoc_vol2a(VOL_KM);
        !           500:                        break;
        !           501:                default:
        !           502:                        /* FIXME: capitalise. */
        !           503:                        if (NULL == (pp = mdoc_msec2a(meta->msec)))
        !           504:                                pp = mdoc_msec2a(MSEC_local);
        !           505:                        break;
        !           506:                }
        !           507:        assert(pp);
        !           508:
        !           509:        tsz = strlcpy(buf, pp, p->rmargin);
        !           510:        assert(tsz < p->rmargin);
        !           511:
        !           512:        if ((pp = mdoc_arch2a(meta->arch))) {
        !           513:                tsz = strlcat(buf, " (", p->rmargin);
        !           514:                assert(tsz < p->rmargin);
        !           515:                tsz = strlcat(buf, pp, p->rmargin);
        !           516:                assert(tsz < p->rmargin);
        !           517:                tsz = strlcat(buf, ")", p->rmargin);
        !           518:                assert(tsz < p->rmargin);
        !           519:        }
        !           520:
        !           521:        ttsz = strlcpy(title, meta->title, p->rmargin);
        !           522:
        !           523:        if (NULL == (msec = mdoc_msec2a(meta->msec)))
        !           524:                msec = "";
        !           525:
        !           526:        ssz = (2 * (ttsz + 2 + strlen(msec))) + tsz + 2;
        !           527:
        !           528:        if (ssz > p->rmargin) {
        !           529:                if ((ssz -= p->rmargin) % 2)
        !           530:                        ssz++;
        !           531:                ssz /= 2;
        !           532:
        !           533:                assert(ssz <= ttsz);
        !           534:                title[ttsz - ssz] = 0;
        !           535:                ssz = 1;
        !           536:        } else
        !           537:                ssz = ((p->rmargin - ssz) / 2) + 1;
        !           538:
        !           539:        printf("%s(%s)", title, msec);
        !           540:
        !           541:        for (i = 0; i < ssz; i++)
        !           542:                printf(" ");
        !           543:
        !           544:        printf("%s", buf);
        !           545:
        !           546:        for (i = 0; i < ssz; i++)
        !           547:                printf(" ");
        !           548:
        !           549:        printf("%s(%s)\n", title, msec);
        !           550:        fflush(stdout);
        !           551:
        !           552:        free(title);
        !           553:        free(buf);
        !           554: }

CVSweb