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

Annotation of mandoc/mdocterm.c, Revision 1.48

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

CVSweb