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

Annotation of mandoc/man_html.c, Revision 1.6

1.6     ! kristaps    1: /*     $Id: man_html.c,v 1.5 2009/10/04 10:24:31 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008, 2009 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 above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      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.
                     16:  */
                     17: #include <sys/types.h>
                     18: #include <sys/queue.h>
                     19:
1.5       kristaps   20: #include <assert.h>
                     21: #include <ctype.h>
1.4       kristaps   22: #include <err.h>
1.2       kristaps   23: #include <stdio.h>
1.1       kristaps   24: #include <stdlib.h>
1.4       kristaps   25: #include <string.h>
1.1       kristaps   26:
                     27: #include "html.h"
                     28: #include "man.h"
                     29:
1.6     ! kristaps   30: /* TODO: preserve ident widths. */
        !            31:
        !            32: #define        INDENT            5
1.4       kristaps   33: #define        HALFINDENT        3
                     34:
1.3       kristaps   35: #define        MAN_ARGS          const struct man_meta *m, \
                     36:                          const struct man_node *n, \
                     37:                          struct html *h
                     38:
                     39: struct htmlman {
                     40:        int             (*pre)(MAN_ARGS);
                     41:        int             (*post)(MAN_ARGS);
                     42: };
                     43:
                     44: static void              print_man(MAN_ARGS);
                     45: static void              print_man_head(MAN_ARGS);
1.4       kristaps   46: static void              print_man_nodelist(MAN_ARGS);
                     47: static void              print_man_node(MAN_ARGS);
1.3       kristaps   48:
1.5       kristaps   49: static int               a2width(const struct man_node *);
                     50:
1.4       kristaps   51: static int               man_br_pre(MAN_ARGS);
1.6     ! kristaps   52: static int               man_HP_pre(MAN_ARGS);
1.5       kristaps   53: static int               man_IP_pre(MAN_ARGS);
1.4       kristaps   54: static int               man_PP_pre(MAN_ARGS);
                     55: static void              man_root_post(MAN_ARGS);
                     56: static int               man_root_pre(MAN_ARGS);
                     57: static int               man_SH_pre(MAN_ARGS);
                     58: static int               man_SS_pre(MAN_ARGS);
                     59:
                     60: #ifdef __linux__
                     61: extern size_t            strlcpy(char *, const char *, size_t);
                     62: extern size_t            strlcat(char *, const char *, size_t);
                     63: #endif
1.3       kristaps   64:
                     65: static const struct htmlman mans[MAN_MAX] = {
1.4       kristaps   66:        { man_br_pre, NULL }, /* br */
1.3       kristaps   67:        { NULL, NULL }, /* TH */
1.4       kristaps   68:        { man_SH_pre, NULL }, /* SH */
                     69:        { man_SS_pre, NULL }, /* SS */
1.6     ! kristaps   70:        { man_IP_pre, NULL }, /* TP */
1.4       kristaps   71:        { man_PP_pre, NULL }, /* LP */
                     72:        { man_PP_pre, NULL }, /* PP */
                     73:        { man_PP_pre, NULL }, /* P */
1.5       kristaps   74:        { man_IP_pre, NULL }, /* IP */
1.6     ! kristaps   75:        { man_HP_pre, NULL }, /* HP */
1.3       kristaps   76:        { NULL, NULL }, /* SM */
                     77:        { NULL, NULL }, /* SB */
                     78:        { NULL, NULL }, /* BI */
                     79:        { NULL, NULL }, /* IB */
                     80:        { NULL, NULL }, /* BR */
                     81:        { NULL, NULL }, /* RB */
                     82:        { NULL, NULL }, /* R */
                     83:        { NULL, NULL }, /* B */
                     84:        { NULL, NULL }, /* I */
                     85:        { NULL, NULL }, /* IR */
                     86:        { NULL, NULL }, /* RI */
                     87:        { NULL, NULL }, /* na */
                     88:        { NULL, NULL }, /* i */
1.4       kristaps   89:        { man_br_pre, NULL }, /* sp */
1.3       kristaps   90:        { NULL, NULL }, /* nf */
                     91:        { NULL, NULL }, /* fi */
                     92:        { NULL, NULL }, /* r */
                     93:        { NULL, NULL }, /* RE */
                     94:        { NULL, NULL }, /* RS */
                     95:        { NULL, NULL }, /* DT */
                     96:        { NULL, NULL }, /* UC */
                     97: };
                     98:
1.1       kristaps   99:
                    100: void
                    101: html_man(void *arg, const struct man *m)
                    102: {
1.3       kristaps  103:        struct html     *h;
                    104:        struct tag      *t;
                    105:
                    106:        h = (struct html *)arg;
                    107:
                    108:        print_gen_doctype(h);
                    109:
                    110:        t = print_otag(h, TAG_HTML, 0, NULL);
                    111:        print_man(man_meta(m), man_node(m), h);
                    112:        print_tagq(h, t);
                    113:
                    114:        printf("\n");
                    115: }
                    116:
                    117:
                    118: static void
                    119: print_man(MAN_ARGS)
                    120: {
                    121:        struct tag      *t;
                    122:        struct htmlpair  tag;
                    123:
                    124:        t = print_otag(h, TAG_HEAD, 0, NULL);
                    125:
                    126:        print_man_head(m, n, h);
                    127:        print_tagq(h, t);
                    128:        t = print_otag(h, TAG_BODY, 0, NULL);
                    129:
                    130:        tag.key = ATTR_CLASS;
                    131:        tag.val = "body";
                    132:        print_otag(h, TAG_DIV, 1, &tag);
                    133:
1.4       kristaps  134:        print_man_nodelist(m, n, h);
1.3       kristaps  135:
                    136:        print_tagq(h, t);
                    137: }
                    138:
                    139:
                    140: /* ARGSUSED */
                    141: static void
                    142: print_man_head(MAN_ARGS)
                    143: {
                    144:
                    145:        print_gen_head(h);
                    146:        bufinit(h);
                    147:        buffmt(h, "%s(%d)", m->title, m->msec);
                    148:
                    149:        print_otag(h, TAG_TITLE, 0, NULL);
                    150:        print_text(h, h->buf);
1.1       kristaps  151: }
1.4       kristaps  152:
                    153:
                    154: static void
                    155: print_man_nodelist(MAN_ARGS)
                    156: {
                    157:
                    158:        print_man_node(m, n, h);
                    159:        if (n->next)
                    160:                print_man_nodelist(m, n->next, h);
                    161: }
                    162:
                    163:
                    164: static void
                    165: print_man_node(MAN_ARGS)
                    166: {
                    167:        int              child;
                    168:        struct tag      *t;
                    169:
                    170:        child = 1;
                    171:        t = SLIST_FIRST(&h->tags);
                    172:
                    173:        bufinit(h);
                    174:
                    175:        switch (n->type) {
                    176:        case (MAN_ROOT):
                    177:                child = man_root_pre(m, n, h);
                    178:                break;
                    179:        case (MAN_TEXT):
                    180:                print_text(h, n->string);
                    181:                break;
                    182:        default:
                    183:                if (mans[n->tok].pre)
                    184:                        child = (*mans[n->tok].pre)(m, n, h);
                    185:                break;
                    186:        }
                    187:
                    188:        if (child && n->child)
                    189:                print_man_nodelist(m, n->child, h);
                    190:
                    191:        print_stagq(h, t);
                    192:
                    193:        bufinit(h);
                    194:
                    195:        switch (n->type) {
                    196:        case (MAN_ROOT):
                    197:                man_root_post(m, n, h);
                    198:                break;
                    199:        case (MAN_TEXT):
                    200:                break;
                    201:        default:
                    202:                if (mans[n->tok].post)
                    203:                        (*mans[n->tok].post)(m, n, h);
                    204:                break;
                    205:        }
                    206: }
                    207:
                    208:
1.5       kristaps  209: static int
                    210: a2width(const struct man_node *n)
                    211: {
                    212:        int              i, len;
                    213:        const char      *p;
                    214:
1.6     ! kristaps  215:        if (MAN_TEXT != n->type)
        !           216:                return(-1);
1.5       kristaps  217:
                    218:        p = n->string;
                    219:
                    220:        if (0 == (len = (int)strlen(p)))
                    221:                return(-1);
                    222:
                    223:        for (i = 0; i < len; i++)
                    224:                if ( ! isdigit((u_char)p[i]))
                    225:                        break;
                    226:
                    227:        if (i == len - 1)  {
                    228:                if ('n' == p[len - 1] || 'm' == p[len - 1])
                    229:                        return(atoi(p));
                    230:        } else if (i == len)
                    231:                return(atoi(p));
                    232:
                    233:        return(-1);
                    234: }
                    235:
                    236:
1.4       kristaps  237: /* ARGSUSED */
                    238: static int
                    239: man_root_pre(MAN_ARGS)
                    240: {
                    241:        struct htmlpair  tag[2];
                    242:        struct tag      *t, *tt;
                    243:        char             b[BUFSIZ], title[BUFSIZ];
                    244:
                    245:        b[0] = 0;
                    246:        if (m->vol)
                    247:                (void)strlcat(b, m->vol, BUFSIZ);
                    248:
                    249:        (void)snprintf(title, BUFSIZ - 1,
                    250:                        "%s(%d)", m->title, m->msec);
                    251:
                    252:        tag[0].key = ATTR_CLASS;
                    253:        tag[0].val = "header";
                    254:        tag[1].key = ATTR_STYLE;
                    255:        tag[1].val = "width: 100%;";
                    256:        t = print_otag(h, TAG_TABLE, 2, tag);
                    257:        tt = print_otag(h, TAG_TR, 0, NULL);
                    258:
                    259:        tag[0].key = ATTR_STYLE;
                    260:        tag[0].val = "width: 10%;";
                    261:        print_otag(h, TAG_TD, 1, tag);
                    262:        print_text(h, title);
                    263:        print_stagq(h, tt);
                    264:
                    265:        tag[0].key = ATTR_STYLE;
                    266:        tag[0].val = "width: 80%; white-space: nowrap; text-align: center;";
                    267:        print_otag(h, TAG_TD, 1, tag);
                    268:        print_text(h, b);
                    269:        print_stagq(h, tt);
                    270:
                    271:        tag[0].key = ATTR_STYLE;
                    272:        tag[0].val = "width: 10%; text-align: right;";
                    273:        print_otag(h, TAG_TD, 1, tag);
                    274:        print_text(h, title);
                    275:        print_tagq(h, t);
                    276:
                    277:        return(1);
                    278: }
                    279:
                    280:
                    281: /* ARGSUSED */
                    282: static void
                    283: man_root_post(MAN_ARGS)
                    284: {
                    285:        struct tm        tm;
                    286:        struct htmlpair  tag[2];
                    287:        struct tag      *t, *tt;
                    288:        char             b[BUFSIZ];
                    289:
                    290:        (void)localtime_r(&m->date, &tm);
                    291:
                    292:        if (0 == strftime(b, BUFSIZ - 1, "%B %e, %Y", &tm))
                    293:                err(EXIT_FAILURE, "strftime");
                    294:
                    295:        tag[0].key = ATTR_CLASS;
                    296:        tag[0].val = "footer";
                    297:        tag[1].key = ATTR_STYLE;
                    298:        tag[1].val = "width: 100%;";
                    299:        t = print_otag(h, TAG_TABLE, 2, tag);
                    300:        tt = print_otag(h, TAG_TR, 0, NULL);
                    301:
                    302:        tag[0].key = ATTR_STYLE;
                    303:        tag[0].val = "width: 50%;";
                    304:        print_otag(h, TAG_TD, 1, tag);
                    305:        print_text(h, b);
                    306:        print_stagq(h, tt);
                    307:
                    308:        tag[0].key = ATTR_STYLE;
                    309:        tag[0].val = "width: 50%; text-align: right;";
                    310:        print_otag(h, TAG_TD, 1, tag);
                    311:        if (m->source)
                    312:                print_text(h, m->source);
                    313:        print_tagq(h, t);
                    314: }
                    315:
                    316:
                    317:
                    318: /* ARGSUSED */
                    319: static int
                    320: man_br_pre(MAN_ARGS)
                    321: {
                    322:        int             len;
                    323:        struct htmlpair tag;
                    324:
                    325:        switch (n->tok) {
                    326:        case (MAN_sp):
                    327:                len = n->child ? atoi(n->child->string) : 1;
                    328:                break;
                    329:        case (MAN_br):
                    330:                len = 0;
                    331:                break;
                    332:        default:
                    333:                len = 1;
                    334:                break;
                    335:        }
                    336:
                    337:        buffmt(h, "height: %dem;", len);
                    338:        tag.key = ATTR_STYLE;
                    339:        tag.val = h->buf;
                    340:        print_otag(h, TAG_DIV, 1, &tag);
                    341:        return(1);
                    342: }
                    343:
                    344:
                    345: /* ARGSUSED */
                    346: static int
                    347: man_SH_pre(MAN_ARGS)
                    348: {
                    349:        struct htmlpair         tag[2];
                    350:
                    351:        if (MAN_BODY == n->type) {
                    352:                buffmt(h, "margin-left: %dem;", INDENT);
                    353:
                    354:                tag[0].key = ATTR_CLASS;
                    355:                tag[0].val = "sec-body";
                    356:                tag[1].key = ATTR_STYLE;
                    357:                tag[1].val = h->buf;
                    358:
                    359:                print_otag(h, TAG_DIV, 2, tag);
                    360:                return(1);
                    361:        } else if (MAN_BLOCK == n->type) {
                    362:                tag[0].key = ATTR_CLASS;
                    363:                tag[0].val = "sec-block";
                    364:
                    365:                if (n->prev && MAN_SH == n->prev->tok)
                    366:                        if (NULL == n->prev->body->child) {
                    367:                                print_otag(h, TAG_DIV, 1, tag);
                    368:                                return(1);
                    369:                        }
                    370:
                    371:                bufcat(h, "margin-top: 1em;");
                    372:                if (NULL == n->next)
                    373:                        bufcat(h, "margin-bottom: 1em;");
                    374:
                    375:                tag[1].key = ATTR_STYLE;
                    376:                tag[1].val = h->buf;
                    377:
                    378:                print_otag(h, TAG_DIV, 2, tag);
                    379:                return(1);
                    380:        }
                    381:
                    382:        tag[0].key = ATTR_CLASS;
                    383:        tag[0].val = "sec-head";
                    384:
                    385:        print_otag(h, TAG_DIV, 1, tag);
                    386:        return(1);
                    387: }
                    388:
                    389:
                    390: /* ARGSUSED */
                    391: static int
                    392: man_SS_pre(MAN_ARGS)
                    393: {
                    394:        struct htmlpair  tag[3];
                    395:        int              i;
                    396:
                    397:        i = 0;
                    398:
                    399:        if (MAN_BODY == n->type) {
                    400:                tag[i].key = ATTR_CLASS;
                    401:                tag[i++].val = "ssec-body";
                    402:
                    403:                if (n->parent->next && n->child) {
                    404:                        bufcat(h, "margin-bottom: 1em;");
                    405:                        tag[i].key = ATTR_STYLE;
                    406:                        tag[i++].val = h->buf;
                    407:                }
                    408:
                    409:                print_otag(h, TAG_DIV, i, tag);
                    410:                return(1);
                    411:        } else if (MAN_BLOCK == n->type) {
                    412:                tag[i].key = ATTR_CLASS;
                    413:                tag[i++].val = "ssec-block";
                    414:
                    415:                if (n->prev && MAN_SS == n->prev->tok)
                    416:                        if (n->prev->body->child) {
                    417:                                bufcat(h, "margin-top: 1em;");
                    418:                                tag[i].key = ATTR_STYLE;
                    419:                                tag[i++].val = h->buf;
                    420:                        }
                    421:
                    422:                print_otag(h, TAG_DIV, i, tag);
                    423:                return(1);
                    424:        }
                    425:
                    426:        buffmt(h, "margin-left: -%dem;", INDENT - HALFINDENT);
                    427:
                    428:        tag[0].key = ATTR_CLASS;
                    429:        tag[0].val = "ssec-head";
                    430:        tag[1].key = ATTR_STYLE;
                    431:        tag[1].val = h->buf;
                    432:
                    433:        print_otag(h, TAG_DIV, 2, tag);
                    434:        return(1);
                    435: }
                    436:
                    437:
                    438: /* ARGSUSED */
                    439: static int
                    440: man_PP_pre(MAN_ARGS)
                    441: {
1.5       kristaps  442:        struct htmlpair  tag;
                    443:        int              i;
1.4       kristaps  444:
                    445:        if (MAN_BLOCK != n->type)
                    446:                return(1);
                    447:
1.5       kristaps  448:        i = 0;
                    449:
                    450:        if (MAN_ROOT == n->parent->tok) {
                    451:                buffmt(h, "margin-left: %dem;", INDENT);
                    452:                i = 1;
                    453:        }
                    454:        if (n->next && n->next->child) {
                    455:                i = 1;
1.4       kristaps  456:                bufcat(h, "margin-bottom: 1em;");
1.5       kristaps  457:        }
1.4       kristaps  458:
                    459:        tag.key = ATTR_STYLE;
                    460:        tag.val = h->buf;
1.5       kristaps  461:        print_otag(h, TAG_DIV, i, &tag);
                    462:        return(1);
                    463: }
                    464:
                    465:
                    466: /* ARGSUSED */
                    467: static int
                    468: man_IP_pre(MAN_ARGS)
                    469: {
                    470:        struct htmlpair          tag;
                    471:        int                      len, ival;
                    472:        const struct man_node   *nn;
                    473:
1.6     ! kristaps  474:        if (MAN_BODY == n->type) {
        !           475:                print_otag(h, TAG_DIV, 0, NULL);
        !           476:                return(1);
        !           477:        }
        !           478:
        !           479:        nn = MAN_BLOCK == n->type ?
        !           480:                n->head->child : n->parent->head->child;
        !           481:        len = INDENT;
        !           482:        ival = -1;
        !           483:
        !           484:        /* Calculate the indentation length. */
        !           485:
        !           486:        if (NULL != nn)
1.5       kristaps  487:                if (NULL != (nn = nn->next)) {
                    488:                        for ( ; nn->next; nn = nn->next)
                    489:                                /* Do nothing. */ ;
                    490:                        if ((ival = a2width(nn)) >= 0)
                    491:                                len = ival;
                    492:                }
                    493:
                    494:        if (MAN_BLOCK == n->type) {
                    495:                buffmt(h, "clear: both; margin-left: %dem;", len);
                    496:                tag.key = ATTR_STYLE;
                    497:                tag.val = h->buf;
                    498:                print_otag(h, TAG_DIV, 1, &tag);
                    499:                return(1);
1.6     ! kristaps  500:        }
        !           501:
        !           502:        /* If there's an indent string, print it out. */
        !           503:
        !           504:        buffmt(h, "margin-left: -%dem; min-width: %dem;",
        !           505:                        len, len - 1);
        !           506:        bufcat(h, "clear: left; padding-right: 1em;");
        !           507:
        !           508:        if (n->next && n->next->child)
        !           509:                bufcat(h, "float: left;");
        !           510:
        !           511:        tag.key = ATTR_STYLE;
        !           512:        tag.val = h->buf;
        !           513:        print_otag(h, TAG_DIV, 1, &tag);
        !           514:
        !           515:        if (ival < 0)
        !           516:                return(1);
        !           517:
        !           518:        /* With a length string, omit the last child. */
        !           519:
        !           520:        for (nn = n->child; nn->next; nn = nn->next)
        !           521:                print_man_node(m, nn, h);
        !           522:
        !           523:        return(0);
        !           524: }
        !           525:
        !           526:
        !           527: /* ARGSUSED */
        !           528: static int
        !           529: man_HP_pre(MAN_ARGS)
        !           530: {
        !           531:        int                      ival, len;
        !           532:        const struct man_node   *nn;
        !           533:        struct htmlpair          tag;
        !           534:
        !           535:        if (MAN_HEAD == n->type)
        !           536:                return(0);
        !           537:
        !           538:        nn = MAN_BLOCK == n->type ?
        !           539:                n->head->child : n->parent->head->child;
        !           540:
        !           541:        len = INDENT;
        !           542:
        !           543:        if (NULL != nn)
        !           544:                if ((ival = a2width(nn)) >= 0)
        !           545:                        len = ival;
        !           546:
        !           547:        if (MAN_BLOCK == n->type) {
        !           548:                buffmt(h, "clear: both; margin-left: %dem;", len);
1.5       kristaps  549:                tag.key = ATTR_STYLE;
                    550:                tag.val = h->buf;
                    551:                print_otag(h, TAG_DIV, 1, &tag);
1.6     ! kristaps  552:                return(1);
        !           553:        }
1.5       kristaps  554:
1.6     ! kristaps  555:        buffmt(h, "text-indent: -%dem;", len);
1.5       kristaps  556:
1.6     ! kristaps  557:        tag.key = ATTR_STYLE;
        !           558:        tag.val = h->buf;
        !           559:        print_otag(h, TAG_DIV, 1, &tag);
1.5       kristaps  560:
1.4       kristaps  561:        return(1);
                    562: }
1.6     ! kristaps  563:

CVSweb