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

Annotation of mandoc/mlg.c, Revision 1.8

1.8     ! kristaps    1: /* $Id: mlg.c,v 1.7 2008/12/04 19:31:57 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>
                     20: #include <ctype.h>
                     21: #include <err.h>
                     22: #include <stdlib.h>
                     23: #include <stdio.h>
                     24: #include <string.h>
                     25:
                     26: #include "libmdocml.h"
                     27: #include "private.h"
                     28: #include "ml.h"
                     29:
                     30: /* TODO: literal tokens. */
                     31:
                     32: #define        COLUMNS           72
                     33: #define        INDENT            4
1.2       kristaps   34: #define        MAXINDENT         10
1.1       kristaps   35:
                     36: enum   md_tok {
                     37:        MD_TEXT,
                     38:        MD_INLINE_IN,
                     39:        MD_INLINE_OUT,
                     40:        MD_BLK_IN,
                     41:        MD_BLK_OUT,
                     42: };
                     43:
                     44: struct md_mlg {
                     45:        const struct md_args    *args;
                     46:        const struct md_rbuf    *rbuf;
                     47:
                     48:        struct md_mbuf   *mbuf;
                     49:        struct rofftree  *tree;
                     50:        size_t            indent;
                     51:        size_t            pos;
                     52:        enum md_tok       last;
                     53:        void             *arg;
                     54:        ml_begintag       begintag;
                     55:        ml_endtag         endtag;
1.2       kristaps   56:        ml_begin          begin;
                     57:        ml_end            end;
1.1       kristaps   58:        int               flags;
                     59: #define        ML_OVERRIDE_ONE  (1 << 0)
                     60: #define        ML_OVERRIDE_ALL  (1 << 1)
                     61: };
                     62:
                     63:
                     64: static void             mlg_roffmsg(void *arg, enum roffmsg,
                     65:                                const char *, const char *, char *);
1.4       kristaps   66: static int              mlg_roffhead(void *, const struct tm *,
                     67:                                const char *, const char *,
                     68:                                const char *, const char *);
1.1       kristaps   69: static int              mlg_rofftail(void *);
                     70: static int              mlg_roffin(void *, int, int *, char **);
1.5       kristaps   71: static int              mlg_roffdata(void *, int,
                     72:                                const char *, char *);
1.1       kristaps   73: static int              mlg_roffout(void *, int);
                     74: static int              mlg_roffblkin(void *, int, int *, char **);
                     75: static int              mlg_roffblkout(void *, int);
1.8     ! kristaps   76: static int              mlg_roffspecial(void *, int,
        !            77:                                const char *, char **);
1.5       kristaps   78: static int              mlg_roffblkheadin(void *, int,
                     79:                                int *, char **);
1.2       kristaps   80: static int              mlg_roffblkheadout(void *, int);
1.5       kristaps   81: static int              mlg_roffblkbodyin(void *, int,
                     82:                                int *, char **);
1.2       kristaps   83: static int              mlg_roffblkbodyout(void *, int);
                     84:
                     85: static int              mlg_endblk(struct md_mlg *, enum md_ns, int);
1.1       kristaps   86: static int              mlg_begintag(struct md_mlg *, enum md_ns,
                     87:                                int, int *, char **);
                     88: static int              mlg_endtag(struct md_mlg *, enum md_ns, int);
                     89: static int              mlg_indent(struct md_mlg *);
                     90: static int              mlg_newline(struct md_mlg *);
                     91: static void             mlg_mode(struct md_mlg *, enum md_tok);
1.5       kristaps   92: static int              mlg_data(struct md_mlg *, int,
                     93:                                const char *, char *);
                     94: static void             mlg_err(struct md_mlg *, const char *,
                     95:                                const char *, char *);
                     96: static void             mlg_warn(struct md_mlg *, const char *,
                     97:                                const char *, char *);
                     98: static void             mlg_msg(struct md_mlg *, enum roffmsg,
                     99:                                const char *, const char *, char *);
1.1       kristaps  100:
                    101: #ifdef __linux__
                    102: extern size_t           strlcat(char *, const char *, size_t);
                    103: extern size_t           strlcpy(char *, const char *, size_t);
                    104: #endif
                    105:
                    106:
                    107: static int
1.2       kristaps  108: mlg_endblk(struct md_mlg *p, enum md_ns ns, int tok)
                    109: {
                    110:
                    111:        p->indent--;
                    112:
                    113:        if (0 != p->pos) {
                    114:                if ( ! mlg_newline(p))
                    115:                        return(0);
                    116:                if ( ! mlg_indent(p))
                    117:                        return(0);
                    118:        } else if ( ! mlg_indent(p))
                    119:                return(0);
                    120:
                    121:        mlg_mode(p, MD_BLK_OUT);
                    122:        if ( ! mlg_endtag(p, ns, tok))
                    123:                return(0);
                    124:        return(mlg_newline(p));
                    125: }
                    126:
                    127:
                    128: static int
1.1       kristaps  129: mlg_begintag(struct md_mlg *p, enum md_ns ns, int tok,
                    130:                int *argc, char **argv)
                    131: {
                    132:        ssize_t          res;
                    133:
1.8     ! kristaps  134:        assert(MD_NS_DEFAULT != ns);
        !           135:
        !           136:        switch (ns) {
        !           137:        case (MD_NS_INLINE):
        !           138:                if ( ! (ML_OVERRIDE_ONE & p->flags) &&
        !           139:                                ! (ML_OVERRIDE_ALL & p->flags) &&
        !           140:                                p->pos + 11 > COLUMNS)
        !           141:                        if ( ! mlg_newline(p))
        !           142:                                return(0);
        !           143:                if (0 != p->pos && (MD_TEXT == p->last ||
        !           144:                                        MD_INLINE_OUT == p->last)
        !           145:                                && ! (ML_OVERRIDE_ONE & p->flags)
        !           146:                                && ! (ML_OVERRIDE_ALL & p->flags))
        !           147:                        if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
        !           148:                                return(0);
        !           149:                if (0 == p->pos && ! mlg_indent(p))
        !           150:                        return(0);
        !           151:                mlg_mode(p, MD_INLINE_IN);
        !           152:                break;
        !           153:        default:
        !           154:                if (0 != p->pos) {
        !           155:                        if ( ! mlg_newline(p))
        !           156:                                return(0);
        !           157:                        if ( ! mlg_indent(p))
        !           158:                                return(0);
        !           159:                } else if ( ! mlg_indent(p))
        !           160:                        return(0);
        !           161:                p->indent++;
        !           162:                mlg_mode(p, MD_BLK_IN);
        !           163:                break;
        !           164:        }
1.1       kristaps  165:
                    166:        if ( ! ml_nputs(p->mbuf, "<", 1, &p->pos))
                    167:                return(0);
                    168:
                    169:        res = (*p->begintag)(p->mbuf, p->args, ns, tok,
                    170:                        argc, (const char **)argv);
                    171:        if (-1 == res)
                    172:                return(0);
                    173:
                    174:        assert(res >= 0);
                    175:        p->pos += (size_t)res;
                    176:
1.8     ! kristaps  177:        if ( ! ml_nputs(p->mbuf, ">", 1, &p->pos))
        !           178:                return(0);
        !           179:
        !           180:        switch (ns) {
        !           181:        case (MD_NS_INLINE):
        !           182:                break;
        !           183:        default:
        !           184:                if ( ! mlg_newline(p))
        !           185:                        return(0);
        !           186:                break;
        !           187:        }
1.1       kristaps  188:
1.8     ! kristaps  189:        return(1);
1.1       kristaps  190: }
                    191:
                    192:
                    193: static int
                    194: mlg_endtag(struct md_mlg *p, enum md_ns ns, int tok)
                    195: {
                    196:        ssize_t          res;
                    197:
                    198:        /* TODO: extra rules for block/inline. */
                    199:
                    200:        if ( ! ml_nputs(p->mbuf, "</", 2, &p->pos))
                    201:                return(0);
                    202:
                    203:        res = (*p->endtag)(p->mbuf, p->args, ns, tok);
                    204:        if (-1 == res)
                    205:                return(0);
                    206:
                    207:        assert(res >= 0);
                    208:        p->pos += (size_t)res;
                    209:
                    210:        /* TODO: extra rules for block/inline. */
                    211:
                    212:        return(ml_nputs(p->mbuf, ">", 1, &p->pos));
                    213: }
                    214:
                    215:
                    216: static int
                    217: mlg_indent(struct md_mlg *p)
                    218: {
                    219:        size_t           count;
                    220:
1.3       kristaps  221:        count = p->indent > MAXINDENT ? (size_t)MAXINDENT : p->indent;
1.1       kristaps  222:        count *= INDENT;
                    223:
                    224:        assert(0 == p->pos);
                    225:        return(ml_putchars(p->mbuf, ' ', count, &p->pos));
                    226: }
                    227:
                    228:
                    229: static int
                    230: mlg_newline(struct md_mlg *p)
                    231: {
                    232:        size_t           dummy;
                    233:
                    234:        if ( ! ml_nputs(p->mbuf, "\n", 1, &dummy))
                    235:                return(0);
                    236:        p->pos = 0;
                    237:        return(1);
                    238: }
                    239:
                    240:
                    241: static void
                    242: mlg_mode(struct md_mlg *p, enum md_tok ns)
                    243: {
1.3       kristaps  244:
1.1       kristaps  245:        p->flags &= ~ML_OVERRIDE_ONE;
                    246:        p->last = ns;
                    247: }
                    248:
                    249:
                    250: static int
1.5       kristaps  251: mlg_data(struct md_mlg *p, int space, const char *start, char *buf)
1.1       kristaps  252: {
                    253:        size_t           sz;
1.5       kristaps  254:        int              c;
1.1       kristaps  255:
                    256:        assert(p->mbuf);
                    257:        assert(0 != p->indent);
                    258:
                    259:        if (ML_OVERRIDE_ONE & p->flags ||
                    260:                        ML_OVERRIDE_ALL & p->flags)
                    261:                space = 0;
                    262:
1.8     ! kristaps  263:        sz = strlen(buf);
1.1       kristaps  264:
1.8     ! kristaps  265:        if (0 == p->pos) {
        !           266:                if ( ! mlg_indent(p))
        !           267:                        return(0);
1.1       kristaps  268:
1.8     ! kristaps  269:                c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
1.1       kristaps  270:
1.8     ! kristaps  271:                if (0 == c) {
        !           272:                        mlg_err(p, start, buf, "bad char sequence");
        !           273:                        return(0);
        !           274:                } else if (c > 1) {
        !           275:                        mlg_warn(p, start, buf, "bogus char sequence");
        !           276:                        return(0);
        !           277:                } else if (-1 == c)
        !           278:                        return(0);
1.1       kristaps  279:
1.8     ! kristaps  280:                if (p->indent * INDENT + sz >= COLUMNS)
        !           281:                        if ( ! mlg_newline(p))
1.1       kristaps  282:                                return(0);
1.5       kristaps  283:
1.8     ! kristaps  284:                return(1);
        !           285:        }
1.5       kristaps  286:
1.8     ! kristaps  287:        if (space && sz + p->pos >= COLUMNS) {
        !           288:                if ( ! mlg_newline(p))
1.5       kristaps  289:                        return(0);
1.8     ! kristaps  290:                if ( ! mlg_indent(p))
1.5       kristaps  291:                        return(0);
1.8     ! kristaps  292:        } else if (space) {
        !           293:                if ( ! ml_nputs(p->mbuf, " ", 1, &p->pos))
1.1       kristaps  294:                        return(0);
1.8     ! kristaps  295:        }
1.1       kristaps  296:
1.8     ! kristaps  297:        c = ml_nputstring(p->mbuf, buf, sz, &p->pos);
        !           298:
        !           299:        if (0 == c) {
        !           300:                mlg_err(p, start, buf, "bad char sequence");
        !           301:                return(0);
        !           302:        } else if (c > 1) {
        !           303:                mlg_warn(p, start, buf, "bogus char sequence");
        !           304:                return(0);
        !           305:        } else if (-1 == c)
        !           306:                return(0);
1.1       kristaps  307:
                    308:        return(1);
                    309: }
                    310:
                    311:
                    312: int
                    313: mlg_line(struct md_mlg *p, char *buf)
                    314: {
                    315:
                    316:        return(roff_engine(p->tree, buf));
                    317: }
                    318:
                    319:
                    320: int
                    321: mlg_exit(struct md_mlg *p, int flush)
                    322: {
                    323:        int              c;
                    324:
                    325:        c = roff_free(p->tree, flush);
                    326:        free(p);
                    327:        return(c);
                    328: }
                    329:
                    330:
                    331: struct md_mlg *
                    332: mlg_alloc(const struct md_args *args,
                    333:                const struct md_rbuf *rbuf,
                    334:                struct md_mbuf *mbuf,
1.2       kristaps  335:                ml_begintag begintag, ml_endtag endtag,
                    336:                ml_begin begin, ml_end end)
1.1       kristaps  337: {
                    338:        struct roffcb    cb;
                    339:        struct md_mlg   *p;
                    340:
                    341:        cb.roffhead = mlg_roffhead;
                    342:        cb.rofftail = mlg_rofftail;
                    343:        cb.roffin = mlg_roffin;
                    344:        cb.roffout = mlg_roffout;
                    345:        cb.roffblkin = mlg_roffblkin;
1.2       kristaps  346:        cb.roffblkheadin = mlg_roffblkheadin;
                    347:        cb.roffblkheadout = mlg_roffblkheadout;
                    348:        cb.roffblkbodyin = mlg_roffblkbodyin;
                    349:        cb.roffblkbodyout = mlg_roffblkbodyout;
1.1       kristaps  350:        cb.roffblkout = mlg_roffblkout;
                    351:        cb.roffspecial = mlg_roffspecial;
                    352:        cb.roffmsg = mlg_roffmsg;
                    353:        cb.roffdata = mlg_roffdata;
                    354:
                    355:        if (NULL == (p = calloc(1, sizeof(struct md_mlg))))
                    356:                err(1, "calloc");
                    357:
                    358:        p->args = args;
                    359:        p->mbuf = mbuf;
                    360:        p->rbuf = rbuf;
                    361:        p->begintag = begintag;
                    362:        p->endtag = endtag;
1.2       kristaps  363:        p->begin = begin;
                    364:        p->end = end;
1.1       kristaps  365:
                    366:        if (NULL == (p->tree = roff_alloc(&cb, p))) {
                    367:                free(p);
                    368:                return(NULL);
                    369:        }
                    370:
                    371:        return(p);
                    372: }
                    373:
                    374:
                    375: static int
1.4       kristaps  376: mlg_roffhead(void *arg, const struct tm *tm, const char *os,
                    377:                const char *title, const char *sec, const char *vol)
1.1       kristaps  378: {
                    379:        struct md_mlg   *p;
                    380:
                    381:        assert(arg);
                    382:        p = (struct md_mlg *)arg;
                    383:
                    384:        mlg_mode(p, MD_BLK_IN);
1.4       kristaps  385:        if ( ! (*p->begin)(p->mbuf, p->args, tm, os, title, sec, vol))
1.1       kristaps  386:                return(0);
                    387:
                    388:        p->indent++;
                    389:        return(mlg_newline(p));
                    390: }
                    391:
                    392:
                    393: static int
                    394: mlg_rofftail(void *arg)
                    395: {
                    396:        struct md_mlg   *p;
                    397:
                    398:        assert(arg);
                    399:        p = (struct md_mlg *)arg;
                    400:
                    401:        if (0 != p->pos && ! mlg_newline(p))
                    402:                return(0);
                    403:
                    404:        mlg_mode(p, MD_BLK_OUT);
1.2       kristaps  405:        if ( ! (*p->end)(p->mbuf, p->args))
1.1       kristaps  406:                return(0);
                    407:
                    408:        return(mlg_newline(p));
                    409: }
                    410:
                    411:
                    412: /* ARGSUSED */
                    413: static int
1.8     ! kristaps  414: mlg_roffspecial(void *arg, int tok, const char *start, char **more)
1.1       kristaps  415: {
                    416:        struct md_mlg   *p;
                    417:
                    418:        assert(arg);
                    419:        p = (struct md_mlg *)arg;
                    420:
                    421:        switch (tok) {
1.8     ! kristaps  422:        case (ROFF_Xr):
        !           423:                if ( ! *more) {
        !           424:                        mlg_err(p, start, start,
        !           425:                                        "missing required argument");
        !           426:                        return(0);
        !           427:                }
        !           428:                if ( ! mlg_begintag(p, MD_NS_INLINE, tok, NULL, NULL))
        !           429:                        return(0);
        !           430:                if ( ! ml_puts(p->mbuf, *more++, &p->pos))
        !           431:                        return(0);
        !           432:                if (*more) {
        !           433:                        if ( ! ml_nputs(p->mbuf, "(", 1, &p->pos))
        !           434:                                return(0);
        !           435:                        if ( ! mlg_data(p, 0, start, *more++))
        !           436:                                return(0);
        !           437:                        if ( ! ml_nputs(p->mbuf, ")", 1, &p->pos))
        !           438:                                return(0);
        !           439:                }
        !           440:                if (*more) {
        !           441:                        mlg_err(p, start, start, "too many arguments");
        !           442:                        return(0);
        !           443:                }
        !           444:                if ( ! mlg_endtag(p, MD_NS_INLINE, tok))
        !           445:                        return(0);
        !           446:                mlg_mode(p, MD_INLINE_OUT);
        !           447:                break;
        !           448:        case (ROFF_Fn):
        !           449:                break;
1.1       kristaps  450:        case (ROFF_Ns):
                    451:                p->flags |= ML_OVERRIDE_ONE;
                    452:                break;
                    453:        case (ROFF_Sm):
                    454:                assert(*more);
                    455:                if (0 == strcmp(*more, "on"))
                    456:                        p->flags |= ML_OVERRIDE_ALL;
                    457:                else
                    458:                        p->flags &= ~ML_OVERRIDE_ALL;
                    459:                break;
                    460:        default:
                    461:                break;
                    462:        }
                    463:
                    464:        return(1);
                    465: }
                    466:
                    467:
                    468: static int
                    469: mlg_roffblkin(void *arg, int tok, int *argc, char **argv)
                    470: {
                    471:
1.8     ! kristaps  472:        return(mlg_begintag((struct md_mlg *)arg,
1.2       kristaps  473:                                MD_NS_BLOCK, tok, argc, argv));
                    474: }
                    475:
                    476:
                    477: static int
                    478: mlg_roffblkout(void *arg, int tok)
                    479: {
                    480:
                    481:        return(mlg_endblk((struct md_mlg *)arg, MD_NS_BLOCK, tok));
                    482: }
                    483:
                    484:
                    485: static int
                    486: mlg_roffblkbodyin(void *arg, int tok, int *argc, char **argv)
                    487: {
                    488:
1.8     ! kristaps  489:        return(mlg_begintag((struct md_mlg *)arg,
1.2       kristaps  490:                                MD_NS_BODY, tok, argc, argv));
                    491: }
1.1       kristaps  492:
                    493:
1.2       kristaps  494: static int
                    495: mlg_roffblkbodyout(void *arg, int tok)
                    496: {
1.1       kristaps  497:
1.2       kristaps  498:        return(mlg_endblk((struct md_mlg *)arg, MD_NS_BODY, tok));
1.1       kristaps  499: }
                    500:
                    501:
                    502: static int
1.2       kristaps  503: mlg_roffblkheadin(void *arg, int tok, int *argc, char **argv)
1.1       kristaps  504: {
                    505:
1.8     ! kristaps  506:        return(mlg_begintag((struct md_mlg *)arg,
1.2       kristaps  507:                                MD_NS_HEAD, tok, argc, argv));
                    508: }
1.1       kristaps  509:
                    510:
1.2       kristaps  511: static int
                    512: mlg_roffblkheadout(void *arg, int tok)
                    513: {
1.1       kristaps  514:
1.2       kristaps  515:        return(mlg_endblk((struct md_mlg *)arg, MD_NS_HEAD, tok));
1.1       kristaps  516: }
                    517:
                    518:
                    519: static int
                    520: mlg_roffin(void *arg, int tok, int *argc, char **argv)
                    521: {
                    522:
1.8     ! kristaps  523:        return(mlg_begintag((struct md_mlg *)arg,
        !           524:                                MD_NS_INLINE, tok, argc, argv));
1.1       kristaps  525: }
                    526:
                    527:
                    528: static int
                    529: mlg_roffout(void *arg, int tok)
                    530: {
                    531:        struct md_mlg   *p;
                    532:
                    533:        assert(arg);
                    534:        p = (struct md_mlg *)arg;
                    535:
                    536:        if (0 == p->pos && ! mlg_indent(p))
                    537:                return(0);
                    538:
                    539:        mlg_mode(p, MD_INLINE_OUT);
                    540:        return(mlg_endtag(p, MD_NS_INLINE, tok));
                    541: }
                    542:
                    543:
                    544: static void
                    545: mlg_roffmsg(void *arg, enum roffmsg lvl,
                    546:                const char *buf, const char *pos, char *msg)
                    547: {
1.5       kristaps  548:
                    549:        mlg_msg((struct md_mlg *)arg, lvl, buf, pos, msg);
                    550: }
                    551:
                    552:
                    553: static int
                    554: mlg_roffdata(void *arg, int space, const char *start, char *buf)
                    555: {
1.1       kristaps  556:        struct md_mlg   *p;
                    557:
                    558:        assert(arg);
                    559:        p = (struct md_mlg *)arg;
                    560:
1.5       kristaps  561:        if ( ! mlg_data(p, space, start, buf))
                    562:                return(0);
                    563:
                    564:        mlg_mode(p, MD_TEXT);
                    565:        return(1);
                    566: }
                    567:
                    568:
                    569: static void
                    570: mlg_err(struct md_mlg *p, const char *buf, const char *pos, char *msg)
                    571: {
                    572:
                    573:        mlg_msg(p, ROFF_ERROR, buf, pos, msg);
                    574: }
                    575:
                    576:
                    577: static void
                    578: mlg_warn(struct md_mlg *p, const char *buf, const char *pos, char *msg)
                    579: {
                    580:
                    581:        mlg_msg(p, ROFF_WARN, buf, pos, msg);
                    582: }
                    583:
                    584:
                    585: static void
                    586: mlg_msg(struct md_mlg *p, enum roffmsg lvl,
                    587:                const char *buf, const char *pos, char *msg)
                    588: {
                    589:        char            *level;
                    590:
1.1       kristaps  591:        switch (lvl) {
                    592:        case (ROFF_WARN):
                    593:                if ( ! (MD_WARN_ALL & p->args->warnings))
                    594:                        return;
                    595:                level = "warning";
                    596:                break;
                    597:        case (ROFF_ERROR):
                    598:                level = "error";
                    599:                break;
                    600:        default:
                    601:                abort();
                    602:        }
                    603:
                    604:        if (pos)
                    605:                (void)fprintf(stderr, "%s:%zu: %s: %s (column %zu)\n",
                    606:                                p->rbuf->name, p->rbuf->line, level,
                    607:                                msg, pos - buf);
                    608:        else
                    609:                (void)fprintf(stderr, "%s: %s: %s\n",
                    610:                                p->rbuf->name, level, msg);
                    611:
                    612: }

CVSweb