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

Annotation of mandoc/term.c, Revision 1.3

1.3     ! kristaps    1: /* $Id: term.c,v 1.2 2009/02/20 23:35:36 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.2       kristaps   20: #include <ctype.h>
1.1       kristaps   21: #include <curses.h>
                     22: #include <err.h>
                     23: #include <stdlib.h>
                     24: #include <stdio.h>
                     25: #include <string.h>
                     26: #include <term.h>
                     27: #include <unistd.h>
                     28:
1.2       kristaps   29: #include "private.h"
1.1       kristaps   30:
1.2       kristaps   31: enum   termesc {
                     32:        ESC_CLEAR,
                     33:        ESC_BOLD,
                     34:        ESC_UNDERLINE
                     35: };
                     36:
                     37: struct termp {
1.3     ! kristaps   38:        size_t            rmargin;
        !            39:        size_t            maxrmargin;
1.2       kristaps   40:        size_t            maxcols;
1.3     ! kristaps   41:        size_t            offset;
1.2       kristaps   42:        size_t            col;
                     43:        int               flags;
1.3     ! kristaps   44: #define        TERMP_BOLD       (1 << 0)       /* Embolden words. */
        !            45: #define        TERMP_UNDERLINE  (1 << 1)       /* Underline words. */
        !            46: #define        TERMP_NOSPACE    (1 << 2)       /* No space before words. */
        !            47: #define        TERMP_NOLPAD     (1 << 3)       /* No left-padding. */
        !            48: #define        TERMP_NOBREAK    (1 << 4)       /* No break after line */
1.2       kristaps   49:        char             *buf;
                     50: };
                     51:
                     52: struct termact {
                     53:        int             (*pre)(struct termp *,
                     54:                                const struct mdoc_meta *,
1.1       kristaps   55:                                const struct mdoc_node *);
1.2       kristaps   56:        int             (*post)(struct termp *,
                     57:                                const struct mdoc_meta *,
                     58:                                const struct mdoc_node *);
                     59: };
                     60:
                     61: static void              termprint_r(struct termp *,
                     62:                                const struct mdoc_meta *,
                     63:                                const struct mdoc_node *);
                     64: static void              termprint_header(struct termp *,
1.1       kristaps   65:                                const struct mdoc_meta *);
1.2       kristaps   66: static void              termprint_footer(struct termp *,
1.1       kristaps   67:                                const struct mdoc_meta *);
                     68:
1.3     ! kristaps   69: static int               arg_hasattr(int, size_t,
        !            70:                                const struct mdoc_arg *);
        !            71: static int               arg_getattr(int, size_t,
        !            72:                                const struct mdoc_arg *);
        !            73:
1.2       kristaps   74: static void              newln(struct termp *);
                     75: static void              vspace(struct termp *);
                     76: static void              pword(struct termp *, const char *, size_t);
                     77: static void              word(struct termp *, const char *);
                     78:
1.3     ! kristaps   79: #define        decl_prepost(name, suffix) \
        !            80: static int               name##_##suffix(struct termp *, \
        !            81:                                const struct mdoc_meta *, \
        !            82:                                const struct mdoc_node *)
        !            83:
        !            84: #define        decl_pre(name)    decl_prepost(name, pre)
        !            85: #define        decl_post(name)   decl_prepost(name, post)
        !            86:
        !            87: decl_pre(termp_fl);
        !            88: decl_pre(termp_it);
        !            89: decl_pre(termp_nd);
        !            90: decl_pre(termp_ns);
        !            91: decl_pre(termp_op);
        !            92: decl_pre(termp_pp);
        !            93: decl_pre(termp_sh);
        !            94:
        !            95: decl_post(termp_bl);
        !            96: decl_post(termp_it);
        !            97: decl_post(termp_op);
        !            98: decl_post(termp_sh);
        !            99:
        !           100: decl_pre(termp_bold);
        !           101: decl_pre(termp_under);
        !           102:
        !           103: decl_post(termp_bold);
        !           104: decl_post(termp_under);
1.2       kristaps  105:
                    106: const  struct termact termacts[MDOC_MAX] = {
                    107:        { NULL, NULL }, /* \" */
                    108:        { NULL, NULL }, /* Dd */
                    109:        { NULL, NULL }, /* Dt */
                    110:        { NULL, NULL }, /* Os */
                    111:        { termp_sh_pre, termp_sh_post }, /* Sh */
                    112:        { NULL, NULL }, /* Ss */
                    113:        { termp_pp_pre, NULL }, /* Pp */
                    114:        { NULL, NULL }, /* D1 */
                    115:        { NULL, NULL }, /* Dl */
                    116:        { NULL, NULL }, /* Bd */
                    117:        { NULL, NULL }, /* Ed */
                    118:        { NULL, termp_bl_post }, /* Bl */
                    119:        { NULL, NULL }, /* El */
1.3     ! kristaps  120:        { termp_it_pre, termp_it_post }, /* It */
1.2       kristaps  121:        { NULL, NULL }, /* Ad */
                    122:        { NULL, NULL }, /* An */
                    123:        { termp_under_pre, termp_under_post }, /* Ar */
                    124:        { NULL, NULL }, /* Cd */
                    125:        { NULL, NULL }, /* Cm */
                    126:        { NULL, NULL }, /* Dv */
                    127:        { NULL, NULL }, /* Er */
                    128:        { NULL, NULL }, /* Ev */
                    129:        { NULL, NULL }, /* Ex */
                    130:        { NULL, NULL }, /* Fa */
                    131:        { NULL, NULL }, /* Fd */
                    132:        { termp_fl_pre, termp_bold_post }, /* Fl */
                    133:        { NULL, NULL }, /* Fn */
                    134:        { NULL, NULL }, /* Ft */
                    135:        { NULL, NULL }, /* Ic */
                    136:        { NULL, NULL }, /* In */
                    137:        { NULL, NULL }, /* Li */
                    138:        { termp_nd_pre, NULL }, /* Nd */
                    139:        { termp_bold_pre, termp_bold_post }, /* Nm */
                    140:        { termp_op_pre, termp_op_post }, /* Op */
                    141:        { NULL, NULL }, /* Ot */
                    142:        { NULL, NULL }, /* Pa */
                    143:        { NULL, NULL }, /* Rv */
                    144:        { NULL, NULL }, /* St */
                    145:        { NULL, NULL }, /* Va */
                    146:        { NULL, NULL }, /* Vt */
                    147:        { NULL, NULL }, /* Xr */
                    148:        { NULL, NULL }, /* %A */
                    149:        { NULL, NULL }, /* %B */
                    150:        { NULL, NULL }, /* %D */
                    151:        { NULL, NULL }, /* %I */
                    152:        { NULL, NULL }, /* %J */
                    153:        { NULL, NULL }, /* %N */
                    154:        { NULL, NULL }, /* %O */
                    155:        { NULL, NULL }, /* %P */
                    156:        { NULL, NULL }, /* %R */
                    157:        { NULL, NULL }, /* %T */
                    158:        { NULL, NULL }, /* %V */
                    159:        { NULL, NULL }, /* Ac */
                    160:        { NULL, NULL }, /* Ao */
                    161:        { NULL, NULL }, /* Aq */
                    162:        { NULL, NULL }, /* At */
                    163:        { NULL, NULL }, /* Bc */
                    164:        { NULL, NULL }, /* Bf */
                    165:        { NULL, NULL }, /* Bo */
                    166:        { NULL, NULL }, /* Bq */
                    167:        { NULL, NULL }, /* Bsx */
                    168:        { NULL, NULL }, /* Bx */
                    169:        { NULL, NULL }, /* Db */
                    170:        { NULL, NULL }, /* Dc */
                    171:        { NULL, NULL }, /* Do */
                    172:        { NULL, NULL }, /* Dq */
                    173:        { NULL, NULL }, /* Ec */
                    174:        { NULL, NULL }, /* Ef */
                    175:        { NULL, NULL }, /* Em */
                    176:        { NULL, NULL }, /* Eo */
                    177:        { NULL, NULL }, /* Fx */
                    178:        { NULL, NULL }, /* Ms */
                    179:        { NULL, NULL }, /* No */
                    180:        { termp_ns_pre, NULL }, /* Ns */
                    181:        { NULL, NULL }, /* Nx */
                    182:        { NULL, NULL }, /* Ox */
                    183:        { NULL, NULL }, /* Pc */
                    184:        { NULL, NULL }, /* Pf */
                    185:        { NULL, NULL }, /* Po */
                    186:        { NULL, NULL }, /* Pq */
                    187:        { NULL, NULL }, /* Qc */
                    188:        { NULL, NULL }, /* Ql */
                    189:        { NULL, NULL }, /* Qo */
                    190:        { NULL, NULL }, /* Qq */
                    191:        { NULL, NULL }, /* Re */
                    192:        { NULL, NULL }, /* Rs */
                    193:        { NULL, NULL }, /* Sc */
                    194:        { NULL, NULL }, /* So */
                    195:        { NULL, NULL }, /* Sq */
                    196:        { NULL, NULL }, /* Sm */
                    197:        { NULL, NULL }, /* Sx */
                    198:        { NULL, NULL }, /* Sy */
                    199:        { NULL, NULL }, /* Tn */
                    200:        { NULL, NULL }, /* Ux */
                    201:        { NULL, NULL }, /* Xc */
                    202:        { NULL, NULL }, /* Xo */
                    203:        { NULL, NULL }, /* Fo */
                    204:        { NULL, NULL }, /* Fc */
                    205:        { NULL, NULL }, /* Oo */
                    206:        { NULL, NULL }, /* Oc */
                    207:        { NULL, NULL }, /* Bk */
                    208:        { NULL, NULL }, /* Ek */
                    209:        { NULL, NULL }, /* Bt */
                    210:        { NULL, NULL }, /* Hf */
                    211:        { NULL, NULL }, /* Fr */
                    212:        { NULL, NULL }, /* Ud */
                    213: };
                    214:
                    215:
1.3     ! kristaps  216: static int
        !           217: arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv)
        !           218: {
        !           219:
        !           220:        return(-1 != arg_getattr(arg, argc, argv));
        !           221: }
        !           222:
        !           223:
        !           224: static int
        !           225: arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv)
        !           226: {
        !           227:        int              i;
        !           228:
        !           229:        for (i = 0; i < (int)argc; i++)
        !           230:                if (argv[i].arg == arg)
        !           231:                        return(i);
        !           232:        return(-1);
        !           233: }
        !           234:
        !           235:
1.2       kristaps  236: static void
1.3     ! kristaps  237: flushln(struct termp *p)
1.2       kristaps  238: {
                    239:        size_t           i, j, vsz, vis, maxvis;
                    240:
1.3     ! kristaps  241:        /*
        !           242:         * First, establish the maximum columns of "visible" content.
        !           243:         * This is usually the difference between the right-margin and
        !           244:         * an indentation, but can be, for tagged lists or columns, a
        !           245:         * small set of values.
        !           246:         */
        !           247:
        !           248:        assert(p->offset < p->rmargin);
        !           249:        maxvis = p->rmargin - p->offset;
1.2       kristaps  250:        vis = 0;
                    251:
1.3     ! kristaps  252:        /*
        !           253:         * If in the standard case (left-justified), then begin with our
        !           254:         * indentation, otherwise (columns, etc.) just start spitting
        !           255:         * out text.
        !           256:         */
        !           257:
        !           258:        if ( ! (p->flags & TERMP_NOLPAD))
        !           259:                /* LINTED */
        !           260:                for (j = 0; j < p->offset; j++)
        !           261:                        putchar(' ');
1.2       kristaps  262:
                    263:        for (i = 0; i < p->col; i++) {
1.3     ! kristaps  264:                /*
        !           265:                 * Count up visible word characters.  Control sequences
        !           266:                 * (starting with the CSI) aren't counted.
        !           267:                 */
        !           268:                assert( ! isspace(p->buf[i]));
        !           269:
        !           270:                /* LINTED */
1.2       kristaps  271:                for (j = i, vsz = 0; j < p->col; j++) {
                    272:                        if (isspace(p->buf[j]))
                    273:                                break;
                    274:                        else if (27 == p->buf[j]) {
                    275:                                assert(j + 4 <= p->col);
                    276:                                j += 3;
                    277:                        } else
                    278:                                vsz++;
                    279:                }
                    280:                assert(vsz > 0);
                    281:
1.3     ! kristaps  282:                /*
        !           283:                 * If a word is too long and we're within a line, put it
        !           284:                 * on the next line.  Puke if we're being asked to write
        !           285:                 * something that will exceed the right margin (i.e.,
        !           286:                 * from a fresh line or when we're not allowed to break
        !           287:                 * the line with TERMP_NOBREAK).
        !           288:                 */
        !           289:
1.2       kristaps  290:                if (vis && vis + vsz >= maxvis) {
1.3     ! kristaps  291:                        /* FIXME */
        !           292:                        if (p->flags & TERMP_NOBREAK)
        !           293:                                errx(1, "word breaks right margin");
1.2       kristaps  294:                        putchar('\n');
1.3     ! kristaps  295:                        for (j = 0; j < p->offset; j++)
1.2       kristaps  296:                                putchar(' ');
                    297:                        vis = 0;
1.3     ! kristaps  298:                } else if (vis + vsz >= maxvis) {
        !           299:                        /* FIXME */
        !           300:                        errx(1, "word breaks right margin");
        !           301:                }
        !           302:
        !           303:                /*
        !           304:                 * Write out the word and a trailing space.  Omit the
        !           305:                 * space if we're the last word in the line.
        !           306:                 */
1.2       kristaps  307:
                    308:                for ( ; i < p->col; i++) {
                    309:                        if (isspace(p->buf[i]))
                    310:                                break;
                    311:                        putchar(p->buf[i]);
                    312:                }
                    313:                vis += vsz;
                    314:                if (i < p->col) {
                    315:                        putchar(' ');
                    316:                        vis++;
                    317:                }
                    318:        }
                    319:
1.3     ! kristaps  320:        /*
        !           321:         * If we're not to right-marginalise it (newline), then instead
        !           322:         * pad to the right margin and stay off.
        !           323:         */
        !           324:
        !           325:        if (p->flags & TERMP_NOBREAK) {
        !           326:                for ( ; vis <= maxvis; vis++)
        !           327:                        putchar(' ');
        !           328:        } else
        !           329:                putchar('\n');
        !           330:
1.2       kristaps  331:        p->col = 0;
                    332: }
                    333:
                    334:
                    335: static void
                    336: newln(struct termp *p)
                    337: {
                    338:
1.3     ! kristaps  339:        /*
        !           340:         * A newline only breaks an existing line; it won't assert
        !           341:         * vertical space.
        !           342:         */
1.2       kristaps  343:        p->flags |= TERMP_NOSPACE;
                    344:        if (0 == p->col)
                    345:                return;
1.3     ! kristaps  346:        flushln(p);
1.2       kristaps  347: }
                    348:
                    349:
                    350: static void
                    351: vspace(struct termp *p)
                    352: {
                    353:
1.3     ! kristaps  354:        /*
        !           355:         * Asserts a vertical space (a full, empty line-break between
        !           356:         * lines).
        !           357:         */
1.2       kristaps  358:        newln(p);
                    359:        putchar('\n');
                    360: }
                    361:
                    362:
                    363: static void
                    364: chara(struct termp *p, char c)
                    365: {
                    366:
1.3     ! kristaps  367:        /* TODO: dynamically expand the buffer. */
1.2       kristaps  368:        if (p->col + 1 >= p->maxcols)
                    369:                errx(1, "line overrun");
                    370:        p->buf[(p->col)++] = c;
                    371: }
                    372:
                    373:
                    374: static void
                    375: escape(struct termp *p, enum termesc esc)
                    376: {
                    377:
                    378:        if (p->col + 4 >= p->maxcols)
                    379:                errx(1, "line overrun");
                    380:
                    381:        p->buf[(p->col)++] = 27;
                    382:        p->buf[(p->col)++] = '[';
                    383:        switch (esc) {
                    384:        case (ESC_CLEAR):
                    385:                p->buf[(p->col)++] = '0';
                    386:                break;
                    387:        case (ESC_BOLD):
                    388:                p->buf[(p->col)++] = '1';
                    389:                break;
                    390:        case (ESC_UNDERLINE):
                    391:                p->buf[(p->col)++] = '4';
                    392:                break;
                    393:        default:
                    394:                abort();
                    395:                /* NOTREACHED */
                    396:        }
                    397:        p->buf[(p->col)++] = 'm';
                    398: }
                    399:
                    400:
                    401: static void
                    402: pword(struct termp *p, const char *word, size_t len)
                    403: {
                    404:        size_t           i;
                    405:
                    406:        assert(len > 0);
                    407:
                    408:        if ( ! (p->flags & TERMP_NOSPACE))
                    409:                chara(p, ' ');
                    410:
                    411:        p->flags &= ~TERMP_NOSPACE;
                    412:
                    413:        if (p->flags & TERMP_BOLD)
                    414:                escape(p, ESC_BOLD);
                    415:        if (p->flags & TERMP_UNDERLINE)
                    416:                escape(p, ESC_UNDERLINE);
                    417:
1.3     ! kristaps  418:        /* TODO: escape patterns. */
        !           419:
1.2       kristaps  420:        for (i = 0; i < len; i++)
                    421:                chara(p, word[i]);
                    422:
                    423:        if (p->flags & TERMP_BOLD ||
                    424:                        p->flags & TERMP_UNDERLINE)
                    425:                escape(p, ESC_CLEAR);
                    426: }
                    427:
                    428:
                    429: static void
                    430: word(struct termp *p, const char *word)
                    431: {
                    432:        size_t           i, j, len;
                    433:
1.3     ! kristaps  434:        if (mdoc_isdelim(word))
        !           435:                p->flags |= TERMP_NOSPACE;
1.2       kristaps  436:
                    437:        len = strlen(word);
                    438:        assert(len > 0);
                    439:
1.3     ! kristaps  440:        /* LINTED */
1.2       kristaps  441:        for (j = i = 0; i < len; i++) {
                    442:                if ( ! isspace(word[i])) {
                    443:                        j++;
                    444:                        continue;
                    445:                }
                    446:                if (0 == j)
                    447:                        continue;
                    448:                assert(i >= j);
                    449:                pword(p, &word[i - j], j);
                    450:                j = 0;
                    451:        }
                    452:        if (j > 0) {
                    453:                assert(i >= j);
                    454:                pword(p, &word[i - j], j);
                    455:        }
                    456: }
                    457:
                    458:
1.3     ! kristaps  459: /* ARGSUSED */
        !           460: static int
        !           461: termp_it_post(struct termp *p, const struct mdoc_meta *meta,
        !           462:                const struct mdoc_node *node)
        !           463: {
        !           464:        const struct mdoc_node *n, *it;
        !           465:        const struct mdoc_block *bl;
        !           466:        int              i;
        !           467:        size_t           width;
        !           468:
        !           469:        switch (node->type) {
        !           470:        case (MDOC_BODY):
        !           471:                /* FALLTHROUGH */
        !           472:        case (MDOC_HEAD):
        !           473:                break;
        !           474:        default:
        !           475:                return(1);
        !           476:        }
        !           477:
        !           478:        it = node->parent;
        !           479:        assert(MDOC_BLOCK == it->type);
        !           480:        assert(MDOC_It == it->tok);
        !           481:
        !           482:        n = it->parent;
        !           483:        assert(MDOC_BODY == n->type);
        !           484:        assert(MDOC_Bl == n->tok);
        !           485:        n = n->parent;
        !           486:        bl = &n->data.block;
        !           487:
        !           488:        /* If `-tag', adjust our margins accordingly. */
        !           489:
        !           490:        if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
        !           491:                i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
        !           492:                assert(i >= 0);
        !           493:                assert(1 == bl->argv[i].sz);
        !           494:                width = strlen(*bl->argv[i].value); /* XXX */
        !           495:
        !           496:                if (MDOC_HEAD == node->type) {
        !           497:                        flushln(p);
        !           498:                        /* FIXME: nested lists. */
        !           499:                        p->rmargin = p->maxrmargin;
        !           500:                        p->flags &= ~TERMP_NOBREAK;
        !           501:                } else {
        !           502:                        flushln(p);
        !           503:                        p->offset -= width + 1;
        !           504:                        p->flags &= ~TERMP_NOLPAD;
        !           505:                }
        !           506:        }
        !           507:
        !           508:        return(1);
        !           509: }
        !           510:
        !           511:
        !           512: /* ARGSUSED */
1.2       kristaps  513: static int
                    514: termp_it_pre(struct termp *p, const struct mdoc_meta *meta,
                    515:                const struct mdoc_node *node)
                    516: {
1.3     ! kristaps  517:        const struct mdoc_node *n, *it;
        !           518:        const struct mdoc_block *bl;
        !           519:        int              i;
        !           520:        size_t           width;
1.2       kristaps  521:
                    522:        switch (node->type) {
1.3     ! kristaps  523:        case (MDOC_BODY):
        !           524:                /* FALLTHROUGH */
1.2       kristaps  525:        case (MDOC_HEAD):
1.3     ! kristaps  526:                it = node->parent;
        !           527:                break;
        !           528:        case (MDOC_BLOCK):
        !           529:                it = node;
1.2       kristaps  530:                break;
1.1       kristaps  531:        default:
1.3     ! kristaps  532:                return(1);
        !           533:        }
        !           534:
        !           535:        assert(MDOC_BLOCK == it->type);
        !           536:        assert(MDOC_It == it->tok);
        !           537:
        !           538:        n = it->parent;
        !           539:        assert(MDOC_BODY == n->type);
        !           540:        assert(MDOC_Bl == n->tok);
        !           541:        n = n->parent;
        !           542:        bl = &n->data.block;
        !           543:
        !           544:        /* If `-compact', don't assert vertical space. */
        !           545:
        !           546:        if (MDOC_BLOCK == node->type) {
        !           547:                if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
        !           548:                        newln(p);
        !           549:                else
        !           550:                        vspace(p);
        !           551:                return(1);
        !           552:        }
        !           553:
        !           554:        assert(MDOC_HEAD == node->type
        !           555:                        || MDOC_BODY == node->type);
        !           556:
        !           557:        /* If `-tag', adjust our margins accordingly. */
        !           558:
        !           559:        if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
        !           560:                i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
        !           561:                assert(i >= 0); /* XXX */
        !           562:                assert(1 == bl->argv[i].sz);
        !           563:                width = strlen(*bl->argv[i].value); /* XXX */
        !           564:
        !           565:                /* FIXME: nested lists. */
        !           566:
        !           567:                if (MDOC_HEAD == node->type) {
        !           568:                        p->flags |= TERMP_NOBREAK;
        !           569:                        p->flags |= TERMP_NOSPACE;
        !           570:                        p->rmargin = p->offset + width;
        !           571:                } else {
        !           572:                        p->flags |= TERMP_NOSPACE;
        !           573:                        p->flags |= TERMP_NOLPAD;
        !           574:                        p->offset += width + 1;
        !           575:                }
1.1       kristaps  576:        }
1.3     ! kristaps  577:
1.2       kristaps  578:        return(1);
                    579: }
1.1       kristaps  580:
1.2       kristaps  581:
1.3     ! kristaps  582: /* ARGSUSED */
1.2       kristaps  583: static int
                    584: termp_bold_post(struct termp *p, const struct mdoc_meta *meta,
                    585:                const struct mdoc_node *node)
                    586: {
                    587:
                    588:        p->flags &= ~TERMP_BOLD;
                    589:        return(1);
                    590: }
                    591:
                    592:
1.3     ! kristaps  593: /* ARGSUSED */
1.2       kristaps  594: static int
                    595: termp_under_pre(struct termp *p, const struct mdoc_meta *meta,
                    596:                const struct mdoc_node *node)
                    597: {
                    598:
                    599:        p->flags |= TERMP_UNDERLINE;
                    600:        return(1);
1.1       kristaps  601: }
                    602:
                    603:
1.3     ! kristaps  604: /* ARGSUSED */
1.2       kristaps  605: static int
                    606: termp_bold_pre(struct termp *p, const struct mdoc_meta *meta,
                    607:                const struct mdoc_node *node)
1.1       kristaps  608: {
                    609:
1.2       kristaps  610:        p->flags |= TERMP_BOLD;
                    611:        return(1);
                    612: }
                    613:
                    614:
1.3     ! kristaps  615: /* ARGSUSED */
1.2       kristaps  616: static int
                    617: termp_ns_pre(struct termp *p, const struct mdoc_meta *meta,
                    618:                const struct mdoc_node *node)
                    619: {
                    620:
                    621:        p->flags |= TERMP_NOSPACE;
                    622:        return(1);
                    623: }
                    624:
                    625:
1.3     ! kristaps  626: /* ARGSUSED */
1.2       kristaps  627: static int
                    628: termp_pp_pre(struct termp *p, const struct mdoc_meta *meta,
                    629:                const struct mdoc_node *node)
                    630: {
                    631:
                    632:        vspace(p);
                    633:        return(1);
                    634: }
                    635:
                    636:
1.3     ! kristaps  637: /* ARGSUSED */
1.2       kristaps  638: static int
                    639: termp_under_post(struct termp *p, const struct mdoc_meta *meta,
                    640:                const struct mdoc_node *node)
                    641: {
                    642:
                    643:        p->flags &= ~TERMP_UNDERLINE;
                    644:        return(1);
                    645: }
                    646:
                    647:
1.3     ! kristaps  648: /* ARGSUSED */
1.2       kristaps  649: static int
                    650: termp_nd_pre(struct termp *p, const struct mdoc_meta *meta,
                    651:                const struct mdoc_node *node)
                    652: {
                    653:
                    654:        word(p, "-");
                    655:        return(1);
                    656: }
                    657:
                    658:
1.3     ! kristaps  659: /* ARGSUSED */
1.2       kristaps  660: static int
                    661: termp_bl_post(struct termp *p, const struct mdoc_meta *meta,
                    662:                const struct mdoc_node *node)
                    663: {
                    664:
                    665:        switch (node->type) {
                    666:        case (MDOC_BLOCK):
                    667:                newln(p);
                    668:                break;
1.1       kristaps  669:        default:
                    670:                break;
                    671:        }
1.2       kristaps  672:        return(1);
1.1       kristaps  673: }
                    674:
                    675:
1.3     ! kristaps  676: /* ARGSUSED */
1.2       kristaps  677: static int
                    678: termp_op_post(struct termp *p, const struct mdoc_meta *meta,
                    679:                const struct mdoc_node *node)
                    680: {
                    681:
                    682:        switch (node->type) {
                    683:        case (MDOC_BODY):
                    684:                p->flags |= TERMP_NOSPACE;
                    685:                word(p, "\\(rB");
                    686:                break;
                    687:        default:
                    688:                break;
                    689:        }
                    690:        return(1);
                    691: }
                    692:
                    693:
1.3     ! kristaps  694: /* ARGSUSED */
1.2       kristaps  695: static int
                    696: termp_sh_post(struct termp *p, const struct mdoc_meta *meta,
                    697:                const struct mdoc_node *node)
1.1       kristaps  698: {
                    699:
1.2       kristaps  700:        switch (node->type) {
                    701:        case (MDOC_HEAD):
                    702:                p->flags &= ~TERMP_BOLD;
                    703:                newln(p);
                    704:                break;
                    705:        case (MDOC_BODY):
                    706:                newln(p);
1.3     ! kristaps  707:                p->offset -= 4;
1.2       kristaps  708:                break;
                    709:        default:
                    710:                break;
                    711:        }
                    712:        return(1);
                    713: }
                    714:
1.1       kristaps  715:
1.3     ! kristaps  716: /* ARGSUSED */
1.2       kristaps  717: static int
                    718: termp_sh_pre(struct termp *p, const struct mdoc_meta *meta,
                    719:                const struct mdoc_node *node)
                    720: {
1.1       kristaps  721:
1.2       kristaps  722:        switch (node->type) {
                    723:        case (MDOC_HEAD):
                    724:                vspace(p);
                    725:                p->flags |= TERMP_BOLD;
                    726:                break;
                    727:        case (MDOC_BODY):
1.3     ! kristaps  728:                p->offset += 4;
1.2       kristaps  729:                break;
                    730:        default:
                    731:                break;
1.1       kristaps  732:        }
1.2       kristaps  733:        return(1);
                    734: }
                    735:
1.1       kristaps  736:
1.3     ! kristaps  737: /* ARGSUSED */
1.2       kristaps  738: static int
                    739: termp_op_pre(struct termp *p, const struct mdoc_meta *meta,
                    740:                const struct mdoc_node *node)
                    741: {
                    742:
                    743:        switch (node->type) {
                    744:        case (MDOC_BODY):
                    745:                word(p, "\\(lB");
                    746:                p->flags |= TERMP_NOSPACE;
                    747:                break;
                    748:        default:
                    749:                break;
                    750:        }
                    751:        return(1);
1.1       kristaps  752: }
                    753:
                    754:
1.3     ! kristaps  755: /* ARGSUSED */
1.1       kristaps  756: static int
1.2       kristaps  757: termp_fl_pre(struct termp *p, const struct mdoc_meta *meta,
                    758:                const struct mdoc_node *node)
1.1       kristaps  759: {
                    760:
1.2       kristaps  761:        p->flags |= TERMP_BOLD;
                    762:        word(p, "-");
                    763:        p->flags |= TERMP_NOSPACE;
1.1       kristaps  764:        return(1);
                    765: }
                    766:
                    767:
                    768: static void
1.2       kristaps  769: termprint_r(struct termp *p, const struct mdoc_meta *meta,
                    770:                const struct mdoc_node *node)
                    771: {
                    772:
                    773:        /* Pre-processing ----------------- */
                    774:
                    775:        if (MDOC_TEXT != node->type) {
                    776:                if (termacts[node->tok].pre)
                    777:                        if ( ! (*termacts[node->tok].pre)(p, meta, node))
                    778:                                return;
                    779:        } else /* MDOC_TEXT == node->type */
                    780:                word(p, node->data.text.string);
                    781:
                    782:        /* Children ---------------------- */
                    783:
                    784:        if (NULL == node->child) {
                    785:                /* No-child processing. */
                    786:                switch (node->type) {
                    787:                case (MDOC_ELEM):
                    788:                        switch (node->tok) {
                    789:                        case (MDOC_Nm):
                    790:                                word(p, "progname"); /* TODO */
                    791:                                break;
                    792:                        case (MDOC_Ar):
                    793:                                word(p, "...");
                    794:                                break;
                    795:                        default:
                    796:                                break;
                    797:                        }
                    798:                        break;
                    799:                default:
                    800:                        break;
                    801:                }
                    802:        } else
                    803:                termprint_r(p, meta, node->child);
                    804:
                    805:        /* Post-processing --------------- */
                    806:
                    807:        if (MDOC_TEXT != node->type) {
                    808:                if (termacts[node->tok].post)
                    809:                        if ( ! (*termacts[node->tok].post)(p, meta, node))
                    810:                                return;
                    811:        }
                    812:
                    813:        /* Siblings ---------------------- */
                    814:
                    815:        if (node->next)
                    816:                termprint_r(p, meta, node->next);
                    817: }
                    818:
                    819:
                    820: static void
                    821: termprint_footer(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  822: {
                    823:        struct tm       *tm;
                    824:        char            *buf, *os;
                    825:        size_t           sz, osz, ssz, i;
                    826:
1.3     ! kristaps  827:        if (NULL == (buf = malloc(p->rmargin)))
1.1       kristaps  828:                err(1, "malloc");
1.3     ! kristaps  829:        if (NULL == (os = malloc(p->rmargin)))
1.1       kristaps  830:                err(1, "malloc");
                    831:
                    832:        tm = localtime(&meta->date);
1.3     ! kristaps  833:        if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
1.1       kristaps  834:                err(1, "strftime");
                    835:
1.3     ! kristaps  836:        osz = strlcpy(os, meta->os, p->rmargin);
1.1       kristaps  837:
                    838:        sz = strlen(buf);
                    839:        ssz = sz + osz + 1;
                    840:
1.3     ! kristaps  841:        if (ssz > p->rmargin) {
        !           842:                ssz -= p->rmargin;
1.1       kristaps  843:                assert(ssz <= osz);
                    844:                os[osz - ssz] = 0;
                    845:                ssz = 1;
                    846:        } else
1.3     ! kristaps  847:                ssz = p->rmargin - ssz + 1;
1.1       kristaps  848:
1.2       kristaps  849:        printf("\n");
1.1       kristaps  850:        printf("%s", os);
                    851:        for (i = 0; i < ssz; i++)
                    852:                printf(" ");
                    853:
                    854:        printf("%s\n", buf);
1.2       kristaps  855:        fflush(stdout);
1.1       kristaps  856:
                    857:        free(buf);
                    858:        free(os);
                    859: }
                    860:
                    861:
                    862: static void
1.2       kristaps  863: termprint_header(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  864: {
1.2       kristaps  865:        char            *msec, *buf, *title, *pp;
                    866:        size_t           ssz, tsz, ttsz, i;;
1.1       kristaps  867:
1.3     ! kristaps  868:        if (NULL == (buf = malloc(p->rmargin)))
1.1       kristaps  869:                err(1, "malloc");
1.3     ! kristaps  870:        if (NULL == (title = malloc(p->rmargin)))
1.1       kristaps  871:                err(1, "malloc");
                    872:
1.2       kristaps  873:        if (NULL == (pp = mdoc_vol2a(meta->vol)))
                    874:                switch (meta->msec) {
                    875:                case (MSEC_1):
                    876:                        /* FALLTHROUGH */
                    877:                case (MSEC_6):
                    878:                        /* FALLTHROUGH */
                    879:                case (MSEC_7):
                    880:                        pp = mdoc_vol2a(VOL_URM);
                    881:                        break;
                    882:                case (MSEC_8):
                    883:                        pp = mdoc_vol2a(VOL_SMM);
                    884:                        break;
                    885:                case (MSEC_2):
                    886:                        /* FALLTHROUGH */
                    887:                case (MSEC_3):
                    888:                        /* FALLTHROUGH */
                    889:                case (MSEC_4):
                    890:                        /* FALLTHROUGH */
                    891:                case (MSEC_5):
                    892:                        pp = mdoc_vol2a(VOL_PRM);
                    893:                        break;
                    894:                case (MSEC_9):
                    895:                        pp = mdoc_vol2a(VOL_KM);
                    896:                        break;
                    897:                default:
                    898:                        /* FIXME: capitalise. */
                    899:                        if (NULL == (pp = mdoc_msec2a(meta->msec)))
                    900:                                pp = mdoc_msec2a(MSEC_local);
                    901:                        break;
                    902:                }
                    903:        assert(pp);
1.1       kristaps  904:
1.3     ! kristaps  905:        tsz = strlcpy(buf, pp, p->rmargin);
        !           906:        assert(tsz < p->rmargin);
1.1       kristaps  907:
1.2       kristaps  908:        if ((pp = mdoc_arch2a(meta->arch))) {
1.3     ! kristaps  909:                tsz = strlcat(buf, " (", p->rmargin);
        !           910:                assert(tsz < p->rmargin);
        !           911:                tsz = strlcat(buf, pp, p->rmargin);
        !           912:                assert(tsz < p->rmargin);
        !           913:                tsz = strlcat(buf, ")", p->rmargin);
        !           914:                assert(tsz < p->rmargin);
1.2       kristaps  915:        }
                    916:
1.3     ! kristaps  917:        ttsz = strlcpy(title, meta->title, p->rmargin);
1.2       kristaps  918:
                    919:        if (NULL == (msec = mdoc_msec2a(meta->msec)))
1.1       kristaps  920:                msec = "";
                    921:
                    922:        ssz = (2 * (ttsz + 2 + strlen(msec))) + tsz + 2;
                    923:
1.3     ! kristaps  924:        if (ssz > p->rmargin) {
        !           925:                if ((ssz -= p->rmargin) % 2)
1.1       kristaps  926:                        ssz++;
                    927:                ssz /= 2;
                    928:
                    929:                assert(ssz <= ttsz);
                    930:                title[ttsz - ssz] = 0;
                    931:                ssz = 1;
                    932:        } else
1.3     ! kristaps  933:                ssz = ((p->rmargin - ssz) / 2) + 1;
1.1       kristaps  934:
                    935:        printf("%s(%s)", title, msec);
                    936:
                    937:        for (i = 0; i < ssz; i++)
                    938:                printf(" ");
                    939:
                    940:        printf("%s", buf);
                    941:
                    942:        for (i = 0; i < ssz; i++)
                    943:                printf(" ");
                    944:
1.2       kristaps  945:        printf("%s(%s)\n", title, msec);
                    946:        fflush(stdout);
1.1       kristaps  947:
                    948:        free(title);
                    949:        free(buf);
                    950: }
                    951:
                    952:
                    953: int
                    954: termprint(const struct mdoc_node *node,
                    955:                const struct mdoc_meta *meta)
                    956: {
1.2       kristaps  957:        struct termp     p;
1.1       kristaps  958:
                    959:        if (ERR == setupterm(NULL, STDOUT_FILENO, NULL))
                    960:                return(0);
                    961:
1.3     ! kristaps  962:        p.maxrmargin = columns < 60 ? 60 : (size_t)columns;
        !           963:        p.rmargin = p.maxrmargin;
1.2       kristaps  964:        p.maxcols = 1024;
1.3     ! kristaps  965:        p.offset = p.col = 0;
1.2       kristaps  966:        p.flags = TERMP_NOSPACE;
                    967:
                    968:        if (NULL == (p.buf = malloc(p.maxcols)))
                    969:                err(1, "malloc");
                    970:
                    971:        termprint_header(&p, meta);
                    972:        termprint_r(&p, meta, node);
                    973:        termprint_footer(&p, meta);
                    974:
                    975:        free(p.buf);
1.1       kristaps  976:
                    977:        return(1);
                    978: }
                    979:
                    980:

CVSweb