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

Annotation of mandoc/man_term.c, Revision 1.18

1.18    ! kristaps    1: /*     $Id: man_term.c,v 1.17 2009/07/24 20:22:24 kristaps Exp $ */
1.1       kristaps    2: /*
1.9       kristaps    3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1       kristaps    4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
1.8       kristaps    6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    8:  *
1.8       kristaps    9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
1.1       kristaps   16:  */
                     17: #include <assert.h>
1.18    ! kristaps   18: #include <ctype.h>
1.1       kristaps   19: #include <err.h>
                     20: #include <stdio.h>
                     21: #include <stdlib.h>
                     22: #include <string.h>
                     23:
                     24: #include "term.h"
                     25: #include "man.h"
                     26:
1.18    ! kristaps   27: #define        INDENT            7
        !            28: #define        HALFINDENT        3
        !            29:
1.2       kristaps   30: #ifdef __linux__
                     31: extern size_t            strlcpy(char *, const char *, size_t);
                     32: extern size_t            strlcat(char *, const char *, size_t);
                     33: #endif
                     34:
1.1       kristaps   35: #define        DECL_ARGS         struct termp *p, \
                     36:                          const struct man_node *n, \
                     37:                          const struct man_meta *m
                     38:
                     39: struct termact {
                     40:        int             (*pre)(DECL_ARGS);
                     41:        void            (*post)(DECL_ARGS);
                     42: };
                     43:
                     44: static int               pre_B(DECL_ARGS);
1.3       kristaps   45: static int               pre_BI(DECL_ARGS);
                     46: static int               pre_BR(DECL_ARGS);
1.15      kristaps   47: static int               pre_br(DECL_ARGS);
1.1       kristaps   48: static int               pre_I(DECL_ARGS);
1.3       kristaps   49: static int               pre_IB(DECL_ARGS);
1.4       kristaps   50: static int               pre_IP(DECL_ARGS);
1.3       kristaps   51: static int               pre_IR(DECL_ARGS);
1.1       kristaps   52: static int               pre_PP(DECL_ARGS);
1.3       kristaps   53: static int               pre_RB(DECL_ARGS);
                     54: static int               pre_RI(DECL_ARGS);
1.1       kristaps   55: static int               pre_SH(DECL_ARGS);
                     56: static int               pre_SS(DECL_ARGS);
                     57: static int               pre_TP(DECL_ARGS);
                     58:
                     59: static void              post_B(DECL_ARGS);
                     60: static void              post_I(DECL_ARGS);
                     61: static void              post_SH(DECL_ARGS);
                     62: static void              post_SS(DECL_ARGS);
                     63:
                     64: static const struct termact termacts[MAN_MAX] = {
1.15      kristaps   65:        { pre_br, NULL }, /* br */
1.1       kristaps   66:        { NULL, NULL }, /* TH */
                     67:        { pre_SH, post_SH }, /* SH */
                     68:        { pre_SS, post_SS }, /* SS */
                     69:        { pre_TP, NULL }, /* TP */
                     70:        { pre_PP, NULL }, /* LP */
                     71:        { pre_PP, NULL }, /* PP */
                     72:        { pre_PP, NULL }, /* P */
1.4       kristaps   73:        { pre_IP, NULL }, /* IP */
                     74:        { pre_PP, NULL }, /* HP */ /* FIXME */
1.1       kristaps   75:        { NULL, NULL }, /* SM */
                     76:        { pre_B, post_B }, /* SB */
1.3       kristaps   77:        { pre_BI, NULL }, /* BI */
                     78:        { pre_IB, NULL }, /* IB */
                     79:        { pre_BR, NULL }, /* BR */
                     80:        { pre_RB, NULL }, /* RB */
1.1       kristaps   81:        { NULL, NULL }, /* R */
                     82:        { pre_B, post_B }, /* B */
                     83:        { pre_I, post_I }, /* I */
1.3       kristaps   84:        { pre_IR, NULL }, /* IR */
                     85:        { pre_RI, NULL }, /* RI */
1.6       kristaps   86:        { NULL, NULL }, /* na */
1.7       kristaps   87:        { pre_I, post_I }, /* i */
1.17      kristaps   88:        { NULL, NULL }, /* sp */
1.1       kristaps   89: };
                     90:
                     91: static void              print_head(struct termp *,
                     92:                                const struct man_meta *);
                     93: static void              print_body(DECL_ARGS);
                     94: static void              print_node(DECL_ARGS);
                     95: static void              print_foot(struct termp *,
                     96:                                const struct man_meta *);
1.18    ! kristaps   97: static void              fmt_block_vspace(struct termp *,
        !            98:                                const struct man_node *);
        !            99: static int               arg_width(const struct man_node *);
1.1       kristaps  100:
                    101:
                    102: int
                    103: man_run(struct termp *p, const struct man *m)
                    104: {
                    105:
                    106:        print_head(p, man_meta(m));
                    107:        p->flags |= TERMP_NOSPACE;
1.14      kristaps  108:        assert(man_node(m));
                    109:        assert(MAN_ROOT == man_node(m)->type);
                    110:        if (man_node(m)->child)
                    111:                print_body(p, man_node(m)->child, man_meta(m));
1.1       kristaps  112:        print_foot(p, man_meta(m));
                    113:
                    114:        return(1);
                    115: }
                    116:
                    117:
1.18    ! kristaps  118: static void
        !           119: fmt_block_vspace(struct termp *p, const struct man_node *n)
        !           120: {
        !           121:        term_newln(p);
        !           122:
        !           123:        if (NULL == n->prev)
        !           124:                return;
        !           125:
        !           126:        if (MAN_SS == n->prev->tok)
        !           127:                return;
        !           128:        if (MAN_SH == n->prev->tok)
        !           129:                return;
        !           130:
        !           131:        term_vspace(p);
        !           132: }
        !           133:
        !           134:
        !           135: static int
        !           136: arg_width(const struct man_node *n)
        !           137: {
        !           138:        int              i, len;
        !           139:        const char      *p;
        !           140:
        !           141:        assert(MAN_TEXT == n->type);
        !           142:        assert(n->string);
        !           143:
        !           144:        p = n->string;
        !           145:
        !           146:        if (0 == (len = (int)strlen(p)))
        !           147:                return(-1);
        !           148:
        !           149:        for (i = 0; i < len; i++)
        !           150:                if ( ! isdigit((u_char)p[i]))
        !           151:                        break;
        !           152:
        !           153:        if (i == len - 1)  {
        !           154:                if ('n' == p[len - 1] || 'm' == p[len - 1])
        !           155:                        return(atoi(p));
        !           156:        } else if (i == len)
        !           157:                return(atoi(p));
        !           158:
        !           159:        return(-1);
        !           160: }
        !           161:
        !           162:
1.3       kristaps  163: /* ARGSUSED */
1.1       kristaps  164: static int
                    165: pre_I(DECL_ARGS)
                    166: {
                    167:
                    168:        p->flags |= TERMP_UNDER;
                    169:        return(1);
                    170: }
                    171:
                    172:
1.3       kristaps  173: /* ARGSUSED */
1.1       kristaps  174: static void
                    175: post_I(DECL_ARGS)
                    176: {
                    177:
                    178:        p->flags &= ~TERMP_UNDER;
                    179: }
                    180:
                    181:
1.3       kristaps  182: /* ARGSUSED */
                    183: static int
                    184: pre_IR(DECL_ARGS)
                    185: {
                    186:        const struct man_node *nn;
                    187:        int              i;
                    188:
                    189:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    190:                if ( ! (i % 2))
                    191:                        p->flags |= TERMP_UNDER;
1.4       kristaps  192:                if (i > 0)
                    193:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  194:                print_node(p, nn, m);
                    195:                if ( ! (i % 2))
                    196:                        p->flags &= ~TERMP_UNDER;
                    197:        }
                    198:        return(0);
                    199: }
                    200:
                    201:
                    202: /* ARGSUSED */
                    203: static int
                    204: pre_IB(DECL_ARGS)
                    205: {
                    206:        const struct man_node *nn;
                    207:        int              i;
                    208:
                    209:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    210:                p->flags |= i % 2 ? TERMP_BOLD : TERMP_UNDER;
1.4       kristaps  211:                if (i > 0)
                    212:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  213:                print_node(p, nn, m);
                    214:                p->flags &= i % 2 ? ~TERMP_BOLD : ~TERMP_UNDER;
                    215:        }
                    216:        return(0);
                    217: }
                    218:
                    219:
                    220: /* ARGSUSED */
                    221: static int
                    222: pre_RB(DECL_ARGS)
                    223: {
                    224:        const struct man_node *nn;
                    225:        int              i;
                    226:
                    227:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    228:                if (i % 2)
                    229:                        p->flags |= TERMP_BOLD;
1.4       kristaps  230:                if (i > 0)
                    231:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  232:                print_node(p, nn, m);
                    233:                if (i % 2)
                    234:                        p->flags &= ~TERMP_BOLD;
                    235:        }
                    236:        return(0);
                    237: }
                    238:
                    239:
                    240: /* ARGSUSED */
                    241: static int
                    242: pre_RI(DECL_ARGS)
                    243: {
                    244:        const struct man_node *nn;
                    245:        int              i;
                    246:
                    247:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    248:                if ( ! (i % 2))
                    249:                        p->flags |= TERMP_UNDER;
1.4       kristaps  250:                if (i > 0)
                    251:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  252:                print_node(p, nn, m);
                    253:                if ( ! (i % 2))
                    254:                        p->flags &= ~TERMP_UNDER;
                    255:        }
                    256:        return(0);
                    257: }
                    258:
                    259:
                    260: /* ARGSUSED */
                    261: static int
                    262: pre_BR(DECL_ARGS)
                    263: {
                    264:        const struct man_node *nn;
                    265:        int              i;
                    266:
                    267:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    268:                if ( ! (i % 2))
                    269:                        p->flags |= TERMP_BOLD;
1.4       kristaps  270:                if (i > 0)
                    271:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  272:                print_node(p, nn, m);
                    273:                if ( ! (i % 2))
                    274:                        p->flags &= ~TERMP_BOLD;
                    275:        }
                    276:        return(0);
                    277: }
                    278:
                    279:
                    280: /* ARGSUSED */
                    281: static int
                    282: pre_BI(DECL_ARGS)
                    283: {
                    284:        const struct man_node *nn;
                    285:        int              i;
                    286:
                    287:        for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
                    288:                p->flags |= i % 2 ? TERMP_UNDER : TERMP_BOLD;
1.4       kristaps  289:                if (i > 0)
                    290:                        p->flags |= TERMP_NOSPACE;
1.3       kristaps  291:                print_node(p, nn, m);
                    292:                p->flags &= i % 2 ? ~TERMP_UNDER : ~TERMP_BOLD;
                    293:        }
                    294:        return(0);
                    295: }
                    296:
                    297:
                    298: /* ARGSUSED */
1.1       kristaps  299: static int
                    300: pre_B(DECL_ARGS)
                    301: {
                    302:
                    303:        p->flags |= TERMP_BOLD;
                    304:        return(1);
                    305: }
                    306:
                    307:
1.3       kristaps  308: /* ARGSUSED */
1.1       kristaps  309: static void
                    310: post_B(DECL_ARGS)
                    311: {
                    312:
                    313:        p->flags &= ~TERMP_BOLD;
                    314: }
                    315:
                    316:
1.3       kristaps  317: /* ARGSUSED */
1.1       kristaps  318: static int
1.15      kristaps  319: pre_br(DECL_ARGS)
                    320: {
                    321:
                    322:        term_newln(p);
                    323:        return(0);
                    324: }
                    325:
                    326:
                    327: /* ARGSUSED */
                    328: static int
1.1       kristaps  329: pre_PP(DECL_ARGS)
                    330: {
                    331:
                    332:        term_vspace(p);
1.18    ! kristaps  333:        term_vspace(p);
1.1       kristaps  334:        p->offset = INDENT;
1.18    ! kristaps  335:        p->flags |= TERMP_NOSPACE;
1.1       kristaps  336:        return(0);
                    337: }
                    338:
                    339:
1.3       kristaps  340: /* ARGSUSED */
1.1       kristaps  341: static int
1.4       kristaps  342: pre_IP(DECL_ARGS)
                    343: {
                    344:        const struct man_node *nn;
1.18    ! kristaps  345:        size_t           offs, sv;
        !           346:        int              ival;
        !           347:
        !           348:        fmt_block_vspace(p, n);
        !           349:
        !           350:        p->flags |= TERMP_NOSPACE;
1.4       kristaps  351:
1.18    ! kristaps  352:        sv = p->offset;
1.4       kristaps  353:        p->offset = INDENT;
                    354:
1.18    ! kristaps  355:        if (NULL == n->child)
1.4       kristaps  356:                return(1);
                    357:
1.18    ! kristaps  358:        p->flags |= TERMP_NOBREAK;
        !           359:
        !           360:        offs = sv;
        !           361:
        !           362:        /*
        !           363:         * If the last token is number-looking (3m, 3n, 3) then
        !           364:         * interpret it as the width specifier, else we stick with the
        !           365:         * prior saved offset.  XXX - obviously not documented.
        !           366:         */
        !           367:        for (nn = n->child; nn; nn = nn->next) {
        !           368:                if (NULL == nn->next) {
        !           369:                        ival = arg_width(nn);
        !           370:                        if (ival >= 0) {
        !           371:                                offs = (size_t)ival;
        !           372:                                break;
        !           373:                        }
        !           374:                }
        !           375:                print_node(p, nn, m);
        !           376:        }
        !           377:
        !           378:        p->rmargin = p->offset + offs;
        !           379:
        !           380:        term_flushln(p);
        !           381:
        !           382:        p->offset = offs;
        !           383:        p->rmargin = p->maxrmargin;
1.4       kristaps  384:
1.18    ! kristaps  385:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.12      kristaps  386:
1.4       kristaps  387:        return(0);
                    388: }
                    389:
                    390:
                    391: /* ARGSUSED */
                    392: static int
1.1       kristaps  393: pre_TP(DECL_ARGS)
                    394: {
                    395:        const struct man_node *nn;
                    396:        size_t           offs;
                    397:
                    398:        term_vspace(p);
1.16      kristaps  399:
1.1       kristaps  400:        p->offset = INDENT;
                    401:
                    402:        if (NULL == (nn = n->child))
                    403:                return(1);
                    404:
                    405:        if (nn->line == n->line) {
                    406:                if (MAN_TEXT != nn->type)
                    407:                        errx(1, "expected text line argument");
1.3       kristaps  408:                offs = (size_t)atoi(nn->string);
1.1       kristaps  409:                nn = nn->next;
                    410:        } else
                    411:                offs = INDENT;
                    412:
                    413:        for ( ; nn; nn = nn->next)
                    414:                print_node(p, nn, m);
                    415:
                    416:        term_flushln(p);
                    417:        p->flags |= TERMP_NOSPACE;
                    418:        p->offset += offs;
                    419:        return(0);
                    420: }
                    421:
                    422:
1.3       kristaps  423: /* ARGSUSED */
1.1       kristaps  424: static int
                    425: pre_SS(DECL_ARGS)
                    426: {
                    427:
                    428:        term_vspace(p);
                    429:        p->flags |= TERMP_BOLD;
                    430:        return(1);
                    431: }
                    432:
                    433:
1.3       kristaps  434: /* ARGSUSED */
1.1       kristaps  435: static void
                    436: post_SS(DECL_ARGS)
                    437: {
                    438:
                    439:        term_flushln(p);
                    440:        p->flags &= ~TERMP_BOLD;
                    441:        p->flags |= TERMP_NOSPACE;
                    442: }
                    443:
                    444:
1.3       kristaps  445: /* ARGSUSED */
1.1       kristaps  446: static int
                    447: pre_SH(DECL_ARGS)
                    448: {
                    449:
                    450:        term_vspace(p);
                    451:        p->offset = 0;
                    452:        p->flags |= TERMP_BOLD;
                    453:        return(1);
                    454: }
                    455:
                    456:
1.3       kristaps  457: /* ARGSUSED */
1.1       kristaps  458: static void
                    459: post_SH(DECL_ARGS)
                    460: {
                    461:
                    462:        term_flushln(p);
                    463:        p->offset = INDENT;
                    464:        p->flags &= ~TERMP_BOLD;
                    465:        p->flags |= TERMP_NOSPACE;
                    466: }
                    467:
                    468:
                    469: static void
                    470: print_node(DECL_ARGS)
                    471: {
1.4       kristaps  472:        int              c, sz;
1.1       kristaps  473:
                    474:        c = 1;
                    475:
                    476:        switch (n->type) {
                    477:        case(MAN_ELEM):
                    478:                if (termacts[n->tok].pre)
                    479:                        c = (*termacts[n->tok].pre)(p, n, m);
                    480:                break;
                    481:        case(MAN_TEXT):
1.4       kristaps  482:                if (0 == *n->string) {
                    483:                        term_vspace(p);
1.1       kristaps  484:                        break;
                    485:                }
1.4       kristaps  486:                /*
                    487:                 * Note!  This is hacky.  Here, we recognise the `\c'
                    488:                 * escape embedded in so many -man pages.  It's supposed
                    489:                 * to remove the subsequent space, so we mark NOSPACE if
                    490:                 * it's encountered in the string.
                    491:                 */
                    492:                sz = (int)strlen(n->string);
                    493:                term_word(p, n->string);
                    494:                if (sz >= 2 && n->string[sz - 1] == 'c' &&
                    495:                                n->string[sz - 2] == '\\')
                    496:                        p->flags |= TERMP_NOSPACE;
1.1       kristaps  497:                break;
                    498:        default:
                    499:                break;
                    500:        }
                    501:
                    502:        if (c && n->child)
                    503:                print_body(p, n->child, m);
                    504:
                    505:        switch (n->type) {
                    506:        case (MAN_ELEM):
                    507:                if (termacts[n->tok].post)
                    508:                        (*termacts[n->tok].post)(p, n, m);
                    509:                break;
                    510:        default:
                    511:                break;
                    512:        }
                    513: }
                    514:
                    515:
                    516: static void
                    517: print_body(DECL_ARGS)
                    518: {
                    519:        print_node(p, n, m);
                    520:        if ( ! n->next)
                    521:                return;
                    522:        print_body(p, n->next, m);
                    523: }
                    524:
                    525:
                    526: static void
                    527: print_foot(struct termp *p, const struct man_meta *meta)
                    528: {
                    529:        struct tm       *tm;
                    530:        char            *buf;
                    531:
                    532:        if (NULL == (buf = malloc(p->rmargin)))
                    533:                err(1, "malloc");
                    534:
                    535:        tm = localtime(&meta->date);
                    536:
                    537:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    538:                err(1, "strftime");
                    539:
                    540:        term_vspace(p);
                    541:
                    542:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    543:        p->rmargin = p->maxrmargin - strlen(buf);
                    544:        p->offset = 0;
                    545:
                    546:        if (meta->source)
                    547:                term_word(p, meta->source);
                    548:        if (meta->source)
                    549:                term_word(p, "");
                    550:        term_flushln(p);
                    551:
                    552:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    553:        p->offset = p->rmargin;
                    554:        p->rmargin = p->maxrmargin;
                    555:        p->flags &= ~TERMP_NOBREAK;
                    556:
                    557:        term_word(p, buf);
                    558:        term_flushln(p);
                    559:
                    560:        free(buf);
                    561: }
                    562:
                    563:
                    564: static void
                    565: print_head(struct termp *p, const struct man_meta *meta)
                    566: {
                    567:        char            *buf, *title;
                    568:
                    569:        p->rmargin = p->maxrmargin;
                    570:        p->offset = 0;
                    571:
                    572:        if (NULL == (buf = malloc(p->rmargin)))
                    573:                err(1, "malloc");
                    574:        if (NULL == (title = malloc(p->rmargin)))
                    575:                err(1, "malloc");
                    576:
                    577:        if (meta->vol)
                    578:                (void)strlcpy(buf, meta->vol, p->rmargin);
                    579:        else
                    580:                *buf = 0;
                    581:
                    582:        (void)snprintf(title, p->rmargin, "%s(%d)",
                    583:                        meta->title, meta->msec);
                    584:
                    585:        p->offset = 0;
1.10      kristaps  586:        p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
1.1       kristaps  587:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                    588:
                    589:        term_word(p, title);
                    590:        term_flushln(p);
                    591:
                    592:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    593:        p->offset = p->rmargin;
                    594:        p->rmargin = p->maxrmargin - strlen(title);
                    595:
                    596:        term_word(p, buf);
                    597:        term_flushln(p);
                    598:
                    599:        p->offset = p->rmargin;
                    600:        p->rmargin = p->maxrmargin;
                    601:        p->flags &= ~TERMP_NOBREAK;
                    602:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    603:
                    604:        term_word(p, title);
                    605:        term_flushln(p);
                    606:
                    607:        p->rmargin = p->maxrmargin;
                    608:        p->offset = 0;
                    609:        p->flags &= ~TERMP_NOSPACE;
                    610:
                    611:        free(title);
                    612:        free(buf);
                    613: }
                    614:

CVSweb