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

Annotation of mandoc/mdocterm.c, Revision 1.47

1.47    ! kristaps    1: /* $Id: mdocterm.c,v 1.46 2009/03/16 22:19:19 kristaps Exp $ */
1.1       kristaps    2: /*
1.47    ! kristaps    3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
1.1       kristaps    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:  */
1.37      kristaps   19: #include <sys/types.h>
                     20:
1.1       kristaps   21: #include <assert.h>
1.3       kristaps   22: #include <ctype.h>
1.1       kristaps   23: #include <err.h>
                     24: #include <getopt.h>
1.3       kristaps   25: #include <stdio.h>
1.1       kristaps   26: #include <stdlib.h>
1.3       kristaps   27: #include <string.h>
1.43      kristaps   28: #include <unistd.h>
1.5       kristaps   29:
1.2       kristaps   30: #include "mmain.h"
1.1       kristaps   31: #include "term.h"
                     32:
1.43      kristaps   33: struct nroffopt {
                     34:        int               fl_h;
                     35:        int               fl_i;
                     36:        char             *arg_m;
                     37:        char             *arg_n;
                     38:        char             *arg_o;
                     39:        char             *arg_r;
                     40:        char             *arg_T;
                     41:        struct termp     *termp; /* Ephemeral. */
                     42: };
                     43:
1.46      kristaps   44: __dead void              punt(struct nroffopt *, char *);
1.43      kristaps   45: static int               option(void *, int, char *);
                     46: static int               optsopt(struct termp *, char *);
1.3       kristaps   47: static void              body(struct termp *,
1.12      kristaps   48:                                struct termpair *,
1.3       kristaps   49:                                const struct mdoc_meta *,
                     50:                                const struct mdoc_node *);
                     51: static void              header(struct termp *,
                     52:                                const struct mdoc_meta *);
                     53: static void              footer(struct termp *,
                     54:                                const struct mdoc_meta *);
                     55:
                     56: static void              pword(struct termp *, const char *, size_t);
1.25      kristaps   57: static void              pescape(struct termp *, const char *,
                     58:                                size_t *, size_t);
                     59: static void              nescape(struct termp *,
1.12      kristaps   60:                                const char *, size_t);
1.3       kristaps   61: static void              chara(struct termp *, char);
1.25      kristaps   62: static void              stringa(struct termp *,
                     63:                                const char *, size_t);
1.38      kristaps   64: static void              sanity(const struct mdoc_node *);
1.3       kristaps   65:
1.1       kristaps   66: int
                     67: main(int argc, char *argv[])
                     68: {
1.34      kristaps   69:        struct mmain      *p;
1.2       kristaps   70:        const struct mdoc *mdoc;
1.43      kristaps   71:        struct nroffopt    nroff;
1.34      kristaps   72:        struct termp       termp;
1.43      kristaps   73:        int                c;
                     74:        char              *in;
1.2       kristaps   75:
1.42      kristaps   76:        (void)memset(&termp, 0, sizeof(struct termp));
1.43      kristaps   77:        (void)memset(&nroff, 0, sizeof(struct nroffopt));
1.42      kristaps   78:
1.43      kristaps   79:        termp.maxrmargin = termp.rmargin = 78; /* FIXME */
                     80:        termp.maxcols = 1024; /* FIXME */
1.42      kristaps   81:        termp.flags = TERMP_NOSPACE;
1.46      kristaps   82:        termp.symtab = ascii2htab();
1.43      kristaps   83:
                     84:        nroff.termp = &termp;
1.42      kristaps   85:
1.2       kristaps   86:        p = mmain_alloc();
1.43      kristaps   87:
1.42      kristaps   88:        c = mmain_getopt(p, argc, argv, "[-Ooption...]",
1.43      kristaps   89:                        "[infile]", "him:n:o:r:T:O:", &nroff, option);
1.1       kristaps   90:
1.43      kristaps   91:        /* FIXME: this needs to accept multiple outputs. */
                     92:        argv += c;
                     93:        if ((argc -= c) > 0)
                     94:                in = *argv++;
                     95:        else
                     96:                in = "-";
                     97:
                     98:        mmain_prepare(p, in);
                     99:
                    100:        if (NULL == (mdoc = mmain_process(p))) {
                    101:                if (TERMP_NOPUNT & termp.iflags)
                    102:                        mmain_exit(p, 1);
                    103:                mmain_free(p);
                    104:                punt(&nroff, in);
                    105:                /* NOTREACHED */
                    106:        }
1.3       kristaps  107:
                    108:        if (NULL == (termp.buf = malloc(termp.maxcols)))
                    109:                err(1, "malloc");
                    110:
1.15      kristaps  111:        header(&termp, mdoc_meta(mdoc));
1.12      kristaps  112:        body(&termp, NULL, mdoc_meta(mdoc), mdoc_node(mdoc));
1.3       kristaps  113:        footer(&termp, mdoc_meta(mdoc));
                    114:
                    115:        free(termp.buf);
                    116:
                    117:        mmain_exit(p, 0);
                    118:        /* NOTREACHED */
                    119: }
                    120:
                    121:
1.43      kristaps  122: static int
                    123: optsopt(struct termp *p, char *arg)
                    124: {
                    125:        char            *v;
1.45      kristaps  126:        char            *toks[] = { "nopunt", NULL };
1.43      kristaps  127:
                    128:        while (*arg)
                    129:                switch (getsubopt(&arg, toks, &v)) {
                    130:                case (0):
                    131:                        p->iflags |= TERMP_NOPUNT;
                    132:                        break;
                    133:                default:
                    134:                        warnx("unknown -O argument");
                    135:                        return(0);
                    136:                }
                    137:
                    138:        return(1);
                    139: }
                    140:
                    141:
                    142: static int
                    143: option(void *ptr, int c, char *arg)
1.42      kristaps  144: {
1.43      kristaps  145:        struct termp    *termp;
                    146:        struct nroffopt *nroff;
1.42      kristaps  147:
1.43      kristaps  148:        nroff = (struct nroffopt *)ptr;
                    149:        termp = nroff->termp;
1.42      kristaps  150:
1.43      kristaps  151:        switch (c) {
                    152:        case ('h'):
                    153:                nroff->fl_h = 1;
                    154:                break;
                    155:        case ('i'):
                    156:                nroff->fl_i = 1;
                    157:                break;
                    158:        case ('m'):
                    159:                nroff->arg_m = arg;
                    160:                break;
                    161:        case ('n'):
                    162:                nroff->arg_n = arg;
                    163:                break;
                    164:        case ('o'):
                    165:                nroff->arg_o = arg;
                    166:                break;
                    167:        case ('r'):
                    168:                nroff->arg_r = arg;
                    169:                break;
                    170:        case ('T'):
                    171:                nroff->arg_T = arg;
                    172:                break;
                    173:        case ('O'):
                    174:                return(optsopt(termp, arg));
                    175:        default:
                    176:                break;
1.42      kristaps  177:        }
                    178:
1.43      kristaps  179:        return(1);
1.42      kristaps  180: }
                    181:
                    182:
1.25      kristaps  183: /*
                    184:  * Flush a line of text.  A "line" is loosely defined as being something
                    185:  * that should be followed by a newline, regardless of whether it's
                    186:  * broken apart by newlines getting there.  A line can also be a
                    187:  * fragment of a columnar list.
                    188:  *
                    189:  * Specifically, a line is whatever's in p->buf of length p->col, which
                    190:  * is zeroed after this function returns.
                    191:  *
                    192:  * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of
                    193:  * critical importance here.  Their behaviour follows:
                    194:  *
                    195:  *  - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
                    196:  *    offset value.  This is useful when doing columnar lists where the
                    197:  *    prior column has right-padded.
                    198:  *
                    199:  *  - TERMP_NOBREAK: this is the most important and is used when making
                    200:  *    columns.  In short: don't print a newline and instead pad to the
                    201:  *    right margin.  Used in conjunction with TERMP_NOLPAD.
                    202:  *
                    203:  *  In-line line breaking:
                    204:  *
                    205:  *  If TERMP_NOBREAK is specified and the line overruns the right
                    206:  *  margin, it will break and pad-right to the right margin after
                    207:  *  writing.  If maxrmargin is violated, it will break and continue
                    208:  *  writing from the right-margin, which will lead to the above
                    209:  *  scenario upon exit.
                    210:  *
                    211:  *  Otherwise, the line will break at the right margin.  Extremely long
                    212:  *  lines will cause the system to emit a warning (TODO: hyphenate, if
                    213:  *  possible).
                    214:  */
1.3       kristaps  215: void
                    216: flushln(struct termp *p)
                    217: {
1.25      kristaps  218:        size_t           i, j, vsz, vis, maxvis, mmax, bp;
1.3       kristaps  219:
                    220:        /*
                    221:         * First, establish the maximum columns of "visible" content.
                    222:         * This is usually the difference between the right-margin and
                    223:         * an indentation, but can be, for tagged lists or columns, a
                    224:         * small set of values.
                    225:         */
                    226:
                    227:        assert(p->offset < p->rmargin);
                    228:        maxvis = p->rmargin - p->offset;
1.25      kristaps  229:        mmax = p->maxrmargin - p->offset;
                    230:        bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.3       kristaps  231:        vis = 0;
                    232:
                    233:        /*
                    234:         * If in the standard case (left-justified), then begin with our
                    235:         * indentation, otherwise (columns, etc.) just start spitting
                    236:         * out text.
                    237:         */
                    238:
                    239:        if ( ! (p->flags & TERMP_NOLPAD))
                    240:                /* LINTED */
                    241:                for (j = 0; j < p->offset; j++)
                    242:                        putchar(' ');
                    243:
                    244:        for (i = 0; i < p->col; i++) {
                    245:                /*
                    246:                 * Count up visible word characters.  Control sequences
1.23      kristaps  247:                 * (starting with the CSI) aren't counted.  A space
                    248:                 * generates a non-printing word, which is valid (the
                    249:                 * space is printed according to regular spacing rules).
1.3       kristaps  250:                 */
                    251:
                    252:                /* LINTED */
                    253:                for (j = i, vsz = 0; j < p->col; j++) {
1.46      kristaps  254:                        if (' ' == p->buf[j])
1.3       kristaps  255:                                break;
1.45      kristaps  256:                        else if (8 == p->buf[j])
1.42      kristaps  257:                                j += 1;
1.45      kristaps  258:                        else
1.3       kristaps  259:                                vsz++;
                    260:                }
                    261:
                    262:                /*
1.25      kristaps  263:                 * Do line-breaking.  If we're greater than our
                    264:                 * break-point and already in-line, break to the next
                    265:                 * line and start writing.  If we're at the line start,
                    266:                 * then write out the word (TODO: hyphenate) and break
                    267:                 * in a subsequent loop invocation.
1.3       kristaps  268:                 */
                    269:
1.22      kristaps  270:                if ( ! (TERMP_NOBREAK & p->flags)) {
1.25      kristaps  271:                        if (vis && vis + vsz > bp) {
1.22      kristaps  272:                                putchar('\n');
                    273:                                for (j = 0; j < p->offset; j++)
                    274:                                        putchar(' ');
                    275:                                vis = 0;
1.25      kristaps  276:                        } else if (vis + vsz > bp)
                    277:                                warnx("word breaks right margin");
                    278:
                    279:                        /* TODO: hyphenate. */
                    280:
                    281:                } else {
                    282:                        if (vis && vis + vsz > bp) {
                    283:                                putchar('\n');
                    284:                                for (j = 0; j < p->rmargin; j++)
                    285:                                        putchar(' ');
1.30      kristaps  286:                                vis = p->rmargin - p->offset;
1.25      kristaps  287:                        } else if (vis + vsz > bp)
                    288:                                warnx("word breaks right margin");
                    289:
                    290:                        /* TODO: hyphenate. */
1.24      kristaps  291:                }
1.3       kristaps  292:
                    293:                /*
                    294:                 * Write out the word and a trailing space.  Omit the
1.25      kristaps  295:                 * space if we're the last word in the line or beyond
                    296:                 * our breakpoint.
1.3       kristaps  297:                 */
                    298:
                    299:                for ( ; i < p->col; i++) {
1.46      kristaps  300:                        if (' ' == p->buf[i])
1.3       kristaps  301:                                break;
                    302:                        putchar(p->buf[i]);
                    303:                }
                    304:                vis += vsz;
1.25      kristaps  305:                if (i < p->col && vis <= bp) {
1.3       kristaps  306:                        putchar(' ');
                    307:                        vis++;
                    308:                }
                    309:        }
                    310:
1.25      kristaps  311:        /*
                    312:         * If we've overstepped our maximum visible no-break space, then
                    313:         * cause a newline and offset at the right margin.
                    314:         */
                    315:
1.22      kristaps  316:        if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) {
1.32      kristaps  317:                if ( ! (TERMP_NONOBREAK & p->flags)) {
                    318:                        putchar('\n');
                    319:                        for (i = 0; i < p->rmargin; i++)
                    320:                                putchar(' ');
                    321:                }
1.22      kristaps  322:                p->col = 0;
                    323:                return;
                    324:        }
                    325:
1.3       kristaps  326:        /*
                    327:         * If we're not to right-marginalise it (newline), then instead
                    328:         * pad to the right margin and stay off.
                    329:         */
                    330:
1.32      kristaps  331:        if (p->flags & TERMP_NOBREAK) {
                    332:                if ( ! (TERMP_NONOBREAK & p->flags))
                    333:                        for ( ; vis < maxvis; vis++)
                    334:                                putchar(' ');
                    335:        } else
1.3       kristaps  336:                putchar('\n');
                    337:
                    338:        p->col = 0;
                    339: }
                    340:
                    341:
1.25      kristaps  342: /*
                    343:  * A newline only breaks an existing line; it won't assert vertical
                    344:  * space.  All data in the output buffer is flushed prior to the newline
                    345:  * assertion.
                    346:  */
1.3       kristaps  347: void
                    348: newln(struct termp *p)
                    349: {
1.1       kristaps  350:
1.3       kristaps  351:        p->flags |= TERMP_NOSPACE;
1.12      kristaps  352:        if (0 == p->col) {
                    353:                p->flags &= ~TERMP_NOLPAD;
1.3       kristaps  354:                return;
1.12      kristaps  355:        }
1.3       kristaps  356:        flushln(p);
1.11      kristaps  357:        p->flags &= ~TERMP_NOLPAD;
1.3       kristaps  358: }
                    359:
                    360:
1.25      kristaps  361: /*
                    362:  * Asserts a vertical space (a full, empty line-break between lines).
                    363:  * Note that if used twice, this will cause two blank spaces and so on.
                    364:  * All data in the output buffer is flushed prior to the newline
                    365:  * assertion.
                    366:  */
1.3       kristaps  367: void
                    368: vspace(struct termp *p)
                    369: {
                    370:
                    371:        newln(p);
                    372:        putchar('\n');
                    373: }
                    374:
                    375:
1.25      kristaps  376: /*
                    377:  * Break apart a word into "pwords" (partial-words, usually from
                    378:  * breaking up a phrase into individual words) and, eventually, put them
                    379:  * into the output buffer.  If we're a literal word, then don't break up
                    380:  * the word and put it verbatim into the output buffer.
                    381:  */
1.3       kristaps  382: void
                    383: word(struct termp *p, const char *word)
                    384: {
                    385:        size_t           i, j, len;
                    386:
                    387:        if (p->flags & TERMP_LITERAL) {
                    388:                pword(p, word, strlen(word));
                    389:                return;
                    390:        }
                    391:
1.40      kristaps  392:        if (0 == (len = strlen(word)))
                    393:                errx(1, "blank line not in literal context");
1.3       kristaps  394:
                    395:        if (mdoc_isdelim(word)) {
                    396:                if ( ! (p->flags & TERMP_IGNDELIM))
                    397:                        p->flags |= TERMP_NOSPACE;
                    398:                p->flags &= ~TERMP_IGNDELIM;
                    399:        }
                    400:
                    401:        /* LINTED */
                    402:        for (j = i = 0; i < len; i++) {
1.46      kristaps  403:                if (' ' != word[i]) {
1.3       kristaps  404:                        j++;
                    405:                        continue;
1.20      kristaps  406:                }
                    407:
                    408:                /* Escaped spaces don't delimit... */
1.46      kristaps  409:                if (i && ' ' == word[i] && '\\' == word[i - 1]) {
1.20      kristaps  410:                        j++;
                    411:                        continue;
1.1       kristaps  412:                }
1.20      kristaps  413:
1.3       kristaps  414:                if (0 == j)
                    415:                        continue;
                    416:                assert(i >= j);
                    417:                pword(p, &word[i - j], j);
                    418:                j = 0;
                    419:        }
                    420:        if (j > 0) {
                    421:                assert(i >= j);
                    422:                pword(p, &word[i - j], j);
                    423:        }
                    424: }
                    425:
                    426:
1.25      kristaps  427: /*
                    428:  * This is the main function for printing out nodes.  It's constituted
                    429:  * of PRE and POST functions, which correspond to prefix and infix
                    430:  * processing.  The termpair structure allows data to persist between
                    431:  * prefix and postfix invocations.
                    432:  */
1.3       kristaps  433: static void
1.12      kristaps  434: body(struct termp *p, struct termpair *ppair,
                    435:                const struct mdoc_meta *meta,
1.3       kristaps  436:                const struct mdoc_node *node)
                    437: {
                    438:        int              dochild;
1.9       kristaps  439:        struct termpair  pair;
1.3       kristaps  440:
1.38      kristaps  441:        /* Some quick sanity-checking. */
                    442:
                    443:        sanity(node);
                    444:
1.3       kristaps  445:        /* Pre-processing. */
                    446:
                    447:        dochild = 1;
1.12      kristaps  448:        pair.ppair = ppair;
1.9       kristaps  449:        pair.type = 0;
1.11      kristaps  450:        pair.offset = pair.rmargin = 0;
1.10      kristaps  451:        pair.flag = 0;
1.12      kristaps  452:        pair.count = 0;
1.3       kristaps  453:
                    454:        if (MDOC_TEXT != node->type) {
                    455:                if (termacts[node->tok].pre)
1.9       kristaps  456:                        if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node))
1.3       kristaps  457:                                dochild = 0;
                    458:        } else /* MDOC_TEXT == node->type */
1.35      kristaps  459:                word(p, node->string);
1.3       kristaps  460:
                    461:        /* Children. */
                    462:
1.10      kristaps  463:        if (TERMPAIR_FLAG & pair.type)
                    464:                p->flags |= pair.flag;
1.9       kristaps  465:
1.3       kristaps  466:        if (dochild && node->child)
1.12      kristaps  467:                body(p, &pair, meta, node->child);
1.3       kristaps  468:
1.10      kristaps  469:        if (TERMPAIR_FLAG & pair.type)
                    470:                p->flags &= ~pair.flag;
1.9       kristaps  471:
1.3       kristaps  472:        /* Post-processing. */
                    473:
                    474:        if (MDOC_TEXT != node->type)
                    475:                if (termacts[node->tok].post)
1.9       kristaps  476:                        (*termacts[node->tok].post)(p, &pair, meta, node);
1.3       kristaps  477:
                    478:        /* Siblings. */
1.1       kristaps  479:
1.3       kristaps  480:        if (node->next)
1.12      kristaps  481:                body(p, ppair, meta, node->next);
1.3       kristaps  482: }
                    483:
                    484:
                    485: static void
                    486: footer(struct termp *p, const struct mdoc_meta *meta)
                    487: {
                    488:        struct tm       *tm;
                    489:        char            *buf, *os;
                    490:
                    491:        if (NULL == (buf = malloc(p->rmargin)))
                    492:                err(1, "malloc");
                    493:        if (NULL == (os = malloc(p->rmargin)))
                    494:                err(1, "malloc");
                    495:
                    496:        tm = localtime(&meta->date);
                    497:
1.7       kristaps  498: #ifdef __OpenBSD__
                    499:        if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    500: #else
1.3       kristaps  501:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    502: #endif
                    503:                err(1, "strftime");
                    504:
1.15      kristaps  505:        (void)strlcpy(os, meta->os, p->rmargin);
1.3       kristaps  506:
1.16      kristaps  507:        /*
                    508:         * This is /slightly/ different from regular groff output
                    509:         * because we don't have page numbers.  Print the following:
                    510:         *
                    511:         * OS                                            MDOCDATE
                    512:         */
                    513:
1.15      kristaps  514:        vspace(p);
1.3       kristaps  515:
1.15      kristaps  516:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    517:        p->rmargin = p->maxrmargin - strlen(buf);
                    518:        p->offset = 0;
1.3       kristaps  519:
1.15      kristaps  520:        word(p, os);
                    521:        flushln(p);
1.3       kristaps  522:
1.15      kristaps  523:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    524:        p->offset = p->rmargin;
                    525:        p->rmargin = p->maxrmargin;
                    526:        p->flags &= ~TERMP_NOBREAK;
                    527:
                    528:        word(p, buf);
                    529:        flushln(p);
1.1       kristaps  530:
1.3       kristaps  531:        free(buf);
                    532:        free(os);
1.1       kristaps  533: }
                    534:
                    535:
1.3       kristaps  536: static void
                    537: header(struct termp *p, const struct mdoc_meta *meta)
                    538: {
1.34      kristaps  539:        char            *buf, *title, *bufp;
1.18      kristaps  540:
                    541:        p->rmargin = p->maxrmargin;
                    542:        p->offset = 0;
1.3       kristaps  543:
                    544:        if (NULL == (buf = malloc(p->rmargin)))
                    545:                err(1, "malloc");
                    546:        if (NULL == (title = malloc(p->rmargin)))
                    547:                err(1, "malloc");
                    548:
1.16      kristaps  549:        /*
                    550:         * The header is strange.  It has three components, which are
                    551:         * really two with the first duplicated.  It goes like this:
                    552:         *
                    553:         * IDENTIFIER              TITLE                   IDENTIFIER
                    554:         *
                    555:         * The IDENTIFIER is NAME(SECTION), which is the command-name
                    556:         * (if given, or "unknown" if not) followed by the manual page
                    557:         * section.  These are given in `Dt'.  The TITLE is a free-form
                    558:         * string depending on the manual volume.  If not specified, it
                    559:         * switches on the manual section.
                    560:         */
                    561:
1.34      kristaps  562:        assert(meta->vol);
                    563:        (void)strlcpy(buf, meta->vol, p->rmargin);
1.13      kristaps  564:
1.34      kristaps  565:        if (meta->arch) {
                    566:                (void)strlcat(buf, " (", p->rmargin);
                    567:                (void)strlcat(buf, meta->arch, p->rmargin);
                    568:                (void)strlcat(buf, ")", p->rmargin);
                    569:        }
1.13      kristaps  570:
1.34      kristaps  571:        (void)snprintf(title, p->rmargin, "%s(%d)",
                    572:                        meta->title, meta->msec);
1.13      kristaps  573:
1.16      kristaps  574:        for (bufp = title; *bufp; bufp++)
1.33      kristaps  575:                *bufp = toupper((u_char)*bufp);
1.16      kristaps  576:
1.13      kristaps  577:        p->offset = 0;
                    578:        p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
1.15      kristaps  579:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1.3       kristaps  580:
1.13      kristaps  581:        word(p, title);
                    582:        flushln(p);
1.3       kristaps  583:
1.15      kristaps  584:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.13      kristaps  585:        p->offset = p->rmargin;
1.15      kristaps  586:        p->rmargin = p->maxrmargin - strlen(title);
1.3       kristaps  587:
1.13      kristaps  588:        word(p, buf);
                    589:        flushln(p);
1.3       kristaps  590:
1.13      kristaps  591:        p->offset = p->rmargin;
                    592:        p->rmargin = p->maxrmargin;
                    593:        p->flags &= ~TERMP_NOBREAK;
1.15      kristaps  594:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1.3       kristaps  595:
1.13      kristaps  596:        word(p, title);
                    597:        flushln(p);
1.3       kristaps  598:
1.13      kristaps  599:        p->rmargin = p->maxrmargin;
                    600:        p->offset = 0;
                    601:        p->flags &= ~TERMP_NOSPACE;
1.3       kristaps  602:
                    603:        free(title);
                    604:        free(buf);
                    605: }
1.25      kristaps  606:
                    607:
                    608: /*
                    609:  * Determine the symbol indicated by an escape sequences, that is, one
                    610:  * starting with a backslash.  Once done, we pass this value into the
                    611:  * output buffer by way of the symbol table.
                    612:  */
                    613: static void
                    614: nescape(struct termp *p, const char *word, size_t len)
                    615: {
1.46      kristaps  616:        const char      *rhs;
                    617:        size_t           sz;
1.25      kristaps  618:
1.46      kristaps  619:        if (NULL == (rhs = a2ascii(p->symtab, word, len, &sz))) {
1.27      kristaps  620:                warnx("unsupported %zu-byte escape sequence", len);
                    621:                return;
1.25      kristaps  622:        }
1.27      kristaps  623:
1.46      kristaps  624:        stringa(p, rhs, sz);
1.25      kristaps  625: }
                    626:
                    627:
                    628: /*
                    629:  * Handle an escape sequence: determine its length and pass it to the
                    630:  * escape-symbol look table.  Note that we assume mdoc(3) has validated
                    631:  * the escape sequence (we assert upon badly-formed escape sequences).
                    632:  */
                    633: static void
                    634: pescape(struct termp *p, const char *word, size_t *i, size_t len)
                    635: {
                    636:        size_t           j;
                    637:
1.36      kristaps  638:        if (++(*i) >= len) {
                    639:                warnx("ignoring bad escape sequence");
                    640:                return;
                    641:        }
1.25      kristaps  642:
                    643:        if ('(' == word[*i]) {
                    644:                (*i)++;
1.36      kristaps  645:                if (*i + 1 >= len) {
                    646:                        warnx("ignoring bad escape sequence");
                    647:                        return;
                    648:                }
1.25      kristaps  649:                nescape(p, &word[*i], 2);
                    650:                (*i)++;
                    651:                return;
                    652:
                    653:        } else if ('*' == word[*i]) {
                    654:                (*i)++;
1.36      kristaps  655:                if (*i >= len) {
                    656:                        warnx("ignoring bad escape sequence");
                    657:                        return;
                    658:                }
1.25      kristaps  659:                switch (word[*i]) {
                    660:                case ('('):
                    661:                        (*i)++;
1.36      kristaps  662:                        if (*i + 1 >= len) {
                    663:                                warnx("ignoring bad escape sequence");
                    664:                                return;
                    665:                        }
1.25      kristaps  666:                        nescape(p, &word[*i], 2);
                    667:                        (*i)++;
                    668:                        return;
                    669:                case ('['):
                    670:                        break;
                    671:                default:
                    672:                        nescape(p, &word[*i], 1);
                    673:                        return;
                    674:                }
                    675:
                    676:        } else if ('[' != word[*i]) {
                    677:                nescape(p, &word[*i], 1);
                    678:                return;
                    679:        }
                    680:
                    681:        (*i)++;
                    682:        for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++)
                    683:                /* Loop... */ ;
                    684:
1.36      kristaps  685:        if (0 == word[*i]) {
                    686:                warnx("ignoring bad escape sequence");
                    687:                return;
                    688:        }
1.25      kristaps  689:        nescape(p, &word[*i - j], j);
                    690: }
                    691:
                    692:
                    693: /*
                    694:  * Handle pwords, partial words, which may be either a single word or a
                    695:  * phrase that cannot be broken down (such as a literal string).  This
                    696:  * handles word styling.
                    697:  */
                    698: static void
                    699: pword(struct termp *p, const char *word, size_t len)
                    700: {
                    701:        size_t           i;
                    702:
                    703:        if ( ! (TERMP_NOSPACE & p->flags) &&
                    704:                        ! (TERMP_LITERAL & p->flags))
                    705:                chara(p, ' ');
                    706:
                    707:        if ( ! (p->flags & TERMP_NONOSPACE))
                    708:                p->flags &= ~TERMP_NOSPACE;
                    709:
                    710:        /*
1.42      kristaps  711:         * If ANSI (word-length styling), then apply our style now,
                    712:         * before the word.
1.25      kristaps  713:         */
                    714:
                    715:        for (i = 0; i < len; i++) {
                    716:                if ('\\' == word[i]) {
                    717:                        pescape(p, word, &i, len);
                    718:                        continue;
                    719:                }
1.42      kristaps  720:
1.45      kristaps  721:                if (TERMP_STYLE & p->flags) {
1.42      kristaps  722:                        if (TERMP_BOLD & p->flags) {
                    723:                                chara(p, word[i]);
                    724:                                chara(p, 8);
                    725:                        }
                    726:                        if (TERMP_UNDER & p->flags) {
                    727:                                chara(p, '_');
                    728:                                chara(p, 8);
                    729:                        }
                    730:                }
                    731:
1.25      kristaps  732:                chara(p, word[i]);
                    733:        }
                    734: }
                    735:
                    736:
                    737: /*
                    738:  * Like chara() but for arbitrary-length buffers.  Resize the buffer by
                    739:  * a factor of two (if the buffer is less than that) or the buffer's
                    740:  * size.
                    741:  */
                    742: static void
                    743: stringa(struct termp *p, const char *c, size_t sz)
                    744: {
                    745:        size_t           s;
                    746:
1.27      kristaps  747:        if (0 == sz)
                    748:                return;
                    749:
1.25      kristaps  750:        s = sz > p->maxcols * 2 ? sz : p->maxcols * 2;
                    751:
                    752:        assert(c);
                    753:        if (p->col + sz >= p->maxcols) {
                    754:                p->buf = realloc(p->buf, s);
                    755:                if (NULL == p->buf)
                    756:                        err(1, "realloc");
                    757:                p->maxcols = s;
                    758:        }
                    759:
                    760:        (void)memcpy(&p->buf[p->col], c, sz);
                    761:        p->col += sz;
                    762: }
                    763:
                    764:
                    765: /*
                    766:  * Insert a single character into the line-buffer.  If the buffer's
                    767:  * space is exceeded, then allocate more space by doubling the buffer
                    768:  * size.
                    769:  */
                    770: static void
                    771: chara(struct termp *p, char c)
                    772: {
                    773:
                    774:        if (p->col + 1 >= p->maxcols) {
                    775:                p->buf = realloc(p->buf, p->maxcols * 2);
                    776:                if (NULL == p->buf)
                    777:                        err(1, "malloc");
                    778:                p->maxcols *= 2;
                    779:        }
                    780:        p->buf[(p->col)++] = c;
                    781: }
1.38      kristaps  782:
                    783:
                    784: static void
                    785: sanity(const struct mdoc_node *n)
                    786: {
                    787:
                    788:        switch (n->type) {
                    789:        case (MDOC_TEXT):
                    790:                if (n->child)
                    791:                        errx(1, "regular form violated (1)");
                    792:                if (NULL == n->parent)
                    793:                        errx(1, "regular form violated (2)");
                    794:                if (NULL == n->string)
                    795:                        errx(1, "regular form violated (3)");
                    796:                switch (n->parent->type) {
                    797:                case (MDOC_TEXT):
                    798:                        /* FALLTHROUGH */
                    799:                case (MDOC_ROOT):
                    800:                        errx(1, "regular form violated (4)");
                    801:                        /* NOTREACHED */
                    802:                default:
                    803:                        break;
                    804:                }
                    805:                break;
                    806:        case (MDOC_ELEM):
                    807:                if (NULL == n->parent)
                    808:                        errx(1, "regular form violated (5)");
                    809:                switch (n->parent->type) {
                    810:                case (MDOC_TAIL):
                    811:                        /* FALLTHROUGH */
                    812:                case (MDOC_BODY):
                    813:                        /* FALLTHROUGH */
                    814:                case (MDOC_HEAD):
                    815:                        break;
                    816:                default:
                    817:                        errx(1, "regular form violated (6)");
                    818:                        /* NOTREACHED */
                    819:                }
                    820:                if (n->child) switch (n->child->type) {
                    821:                case (MDOC_TEXT):
                    822:                        break;
                    823:                default:
                    824:                        errx(1, "regular form violated (7(");
                    825:                        /* NOTREACHED */
                    826:                }
                    827:                break;
                    828:        case (MDOC_HEAD):
                    829:                /* FALLTHROUGH */
                    830:        case (MDOC_BODY):
                    831:                /* FALLTHROUGH */
                    832:        case (MDOC_TAIL):
                    833:                if (NULL == n->parent)
                    834:                        errx(1, "regular form violated (8)");
                    835:                if (MDOC_BLOCK != n->parent->type)
                    836:                        errx(1, "regular form violated (9)");
                    837:                if (n->child) switch (n->child->type) {
                    838:                case (MDOC_BLOCK):
                    839:                        /* FALLTHROUGH */
                    840:                case (MDOC_ELEM):
                    841:                        /* FALLTHROUGH */
                    842:                case (MDOC_TEXT):
                    843:                        break;
                    844:                default:
                    845:                        errx(1, "regular form violated (a)");
                    846:                        /* NOTREACHED */
                    847:                }
                    848:                break;
                    849:        case (MDOC_BLOCK):
                    850:                if (NULL == n->parent)
                    851:                        errx(1, "regular form violated (b)");
                    852:                if (NULL == n->child)
                    853:                        errx(1, "regular form violated (c)");
                    854:                switch (n->parent->type) {
                    855:                case (MDOC_ROOT):
                    856:                        /* FALLTHROUGH */
                    857:                case (MDOC_HEAD):
                    858:                        /* FALLTHROUGH */
                    859:                case (MDOC_BODY):
                    860:                        /* FALLTHROUGH */
                    861:                case (MDOC_TAIL):
                    862:                        break;
                    863:                default:
                    864:                        errx(1, "regular form violated (d)");
                    865:                        /* NOTREACHED */
                    866:                }
                    867:                switch (n->child->type) {
                    868:                case (MDOC_ROOT):
                    869:                        /* FALLTHROUGH */
                    870:                case (MDOC_ELEM):
                    871:                        errx(1, "regular form violated (e)");
                    872:                        /* NOTREACHED */
                    873:                default:
                    874:                        break;
                    875:                }
                    876:                break;
                    877:        case (MDOC_ROOT):
                    878:                if (n->parent)
                    879:                        errx(1, "regular form violated (f)");
                    880:                if (NULL == n->child)
                    881:                        errx(1, "regular form violated (10)");
                    882:                switch (n->child->type) {
                    883:                case (MDOC_BLOCK):
                    884:                        break;
                    885:                default:
                    886:                        errx(1, "regular form violated (11)");
                    887:                        /* NOTREACHED */
                    888:                }
                    889:                break;
                    890:        }
                    891: }
                    892:
1.43      kristaps  893:
1.46      kristaps  894: __dead void
1.43      kristaps  895: punt(struct nroffopt *nroff, char *in)
                    896: {
                    897:        char            *args[32];
                    898:        char             arg0[32], argm[32];
                    899:        int              i;
                    900:
                    901:        warnx("punting to nroff!");
                    902:
                    903:        i = 0;
                    904:
                    905:        (void)strlcpy(arg0, "nroff", 32);
                    906:        args[i++] = arg0;
                    907:
                    908:        if (nroff->fl_h)
                    909:                args[i++] = "-h";
                    910:        if (nroff->fl_i)
                    911:                args[i++] = "-i";
                    912:
                    913:        if (nroff->arg_m) {
                    914:                (void)strlcpy(argm, "-m", 32);
                    915:                (void)strlcat(argm, nroff->arg_m, 32);
                    916:                args[i++] = argm;
                    917:        } else
                    918:                args[i++] = "-mandoc";
                    919:
                    920:        args[i++] = in;
                    921:        args[i++] = (char *)NULL;
                    922:
                    923:        (void)execvp("nroff", args);
                    924:        errx(1, "exec");
                    925:        /* NOTREACHED */
                    926: }
                    927:

CVSweb