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

Annotation of mandoc/terminal.c, Revision 1.7

1.7     ! kristaps    1: /* $Id: terminal.c,v 1.6 2009/03/22 19:10:48 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19: #include <assert.h>
                     20: #include <err.h>
                     21: #include <stdio.h>
                     22: #include <stdlib.h>
                     23: #include <string.h>
                     24:
                     25: #include "term.h"
                     26:
1.2       kristaps   27: #ifdef __linux__
                     28: extern size_t            strlcpy(char *, const char *, size_t);
                     29: extern size_t            strlcat(char *, const char *, size_t);
                     30: #endif
                     31:
1.5       kristaps   32: static struct termp     *term_alloc(enum termenc);
                     33: static void              term_free(struct termp *);
                     34: static void              term_body(struct termp *, struct termpair *,
1.1       kristaps   35:                                const struct mdoc_meta *,
                     36:                                const struct mdoc_node *);
1.5       kristaps   37: static void              term_head(struct termp *,
1.1       kristaps   38:                                const struct mdoc_meta *);
1.5       kristaps   39: static void              term_foot(struct termp *,
1.1       kristaps   40:                                const struct mdoc_meta *);
1.5       kristaps   41: static void              term_pword(struct termp *, const char *, int);
                     42: static void              term_pescape(struct termp *,
1.1       kristaps   43:                                const char *, int *, int);
1.5       kristaps   44: static void              term_nescape(struct termp *,
1.1       kristaps   45:                                const char *, size_t);
1.5       kristaps   46: static void              term_chara(struct termp *, char);
                     47: static void              term_stringa(struct termp *,
1.1       kristaps   48:                                const char *, size_t);
1.6       kristaps   49: static int               term_isopendelim(const char *, int);
                     50: static int               term_isclosedelim(const char *, int);
1.1       kristaps   51: static void              sanity(const struct mdoc_node *); /* XXX */
                     52:
                     53:
                     54: void *
                     55: latin1_alloc(void)
                     56: {
                     57:
1.5       kristaps   58:        return(term_alloc(TERMENC_LATIN1));
1.1       kristaps   59: }
                     60:
                     61:
                     62: void *
                     63: utf8_alloc(void)
                     64: {
                     65:
1.5       kristaps   66:        return(term_alloc(TERMENC_UTF8));
1.1       kristaps   67: }
                     68:
                     69:
                     70: void *
                     71: ascii_alloc(void)
                     72: {
                     73:
1.5       kristaps   74:        return(term_alloc(TERMENC_ASCII));
1.1       kristaps   75: }
                     76:
                     77:
                     78: int
1.7     ! kristaps   79: terminal_run(void *arg, const struct man *man,
        !            80:                const struct mdoc *mdoc)
1.1       kristaps   81: {
                     82:        struct termp    *p;
                     83:
1.7     ! kristaps   84:        if (NULL == mdoc)
        !            85:                return(1);
        !            86:
1.1       kristaps   87:        p = (struct termp *)arg;
                     88:
                     89:        if (NULL == p->symtab)
1.3       kristaps   90:                p->symtab = term_ascii2htab();
1.1       kristaps   91:
1.5       kristaps   92:        term_head(p, mdoc_meta(mdoc));
                     93:        term_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc));
                     94:        term_foot(p, mdoc_meta(mdoc));
1.1       kristaps   95:
                     96:        return(1);
                     97: }
                     98:
                     99:
                    100: void
                    101: terminal_free(void *arg)
                    102: {
                    103:
1.5       kristaps  104:        term_free((struct termp *)arg);
1.1       kristaps  105: }
                    106:
                    107:
                    108: static void
1.5       kristaps  109: term_free(struct termp *p)
1.1       kristaps  110: {
                    111:
                    112:        if (p->buf)
                    113:                free(p->buf);
                    114:        if (TERMENC_ASCII == p->enc && p->symtab)
1.3       kristaps  115:                term_asciifree(p->symtab);
1.1       kristaps  116:
                    117:        free(p);
                    118: }
                    119:
                    120:
                    121: static struct termp *
1.5       kristaps  122: term_alloc(enum termenc enc)
1.1       kristaps  123: {
                    124:        struct termp *p;
                    125:
                    126:        if (NULL == (p = malloc(sizeof(struct termp))))
                    127:                err(1, "malloc");
                    128:        bzero(p, sizeof(struct termp));
                    129:        p->maxrmargin = 78;
                    130:        p->enc = enc;
                    131:        return(p);
                    132: }
                    133:
                    134:
1.5       kristaps  135: static int
1.6       kristaps  136: term_isclosedelim(const char *p, int len)
1.5       kristaps  137: {
                    138:
                    139:        if (1 != len)
                    140:                return(0);
                    141:
                    142:        switch (*p) {
                    143:        case('.'):
                    144:                /* FALLTHROUGH */
                    145:        case(','):
                    146:                /* FALLTHROUGH */
                    147:        case(';'):
                    148:                /* FALLTHROUGH */
                    149:        case(':'):
                    150:                /* FALLTHROUGH */
                    151:        case('?'):
                    152:                /* FALLTHROUGH */
                    153:        case('!'):
                    154:                /* FALLTHROUGH */
                    155:        case(')'):
                    156:                /* FALLTHROUGH */
                    157:        case(']'):
                    158:                /* FALLTHROUGH */
                    159:        case('}'):
                    160:                return(1);
                    161:        default:
                    162:                break;
                    163:        }
                    164:
                    165:        return(0);
                    166: }
                    167:
                    168:
                    169: static int
1.6       kristaps  170: term_isopendelim(const char *p, int len)
1.5       kristaps  171: {
                    172:
                    173:        if (1 != len)
                    174:                return(0);
                    175:
                    176:        switch (*p) {
                    177:        case('('):
                    178:                /* FALLTHROUGH */
                    179:        case('['):
                    180:                /* FALLTHROUGH */
                    181:        case('{'):
                    182:                return(1);
                    183:        default:
                    184:                break;
                    185:        }
                    186:
                    187:        return(0);
                    188: }
                    189:
                    190:
1.1       kristaps  191: /*
                    192:  * Flush a line of text.  A "line" is loosely defined as being something
                    193:  * that should be followed by a newline, regardless of whether it's
                    194:  * broken apart by newlines getting there.  A line can also be a
                    195:  * fragment of a columnar list.
                    196:  *
                    197:  * Specifically, a line is whatever's in p->buf of length p->col, which
                    198:  * is zeroed after this function returns.
                    199:  *
                    200:  * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of
                    201:  * critical importance here.  Their behaviour follows:
                    202:  *
                    203:  *  - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
                    204:  *    offset value.  This is useful when doing columnar lists where the
                    205:  *    prior column has right-padded.
                    206:  *
                    207:  *  - TERMP_NOBREAK: this is the most important and is used when making
                    208:  *    columns.  In short: don't print a newline and instead pad to the
                    209:  *    right margin.  Used in conjunction with TERMP_NOLPAD.
                    210:  *
                    211:  *  - TERMP_NONOBREAK: don't newline when TERMP_NOBREAK is specified.
                    212:  *
                    213:  *  In-line line breaking:
                    214:  *
                    215:  *  If TERMP_NOBREAK is specified and the line overruns the right
                    216:  *  margin, it will break and pad-right to the right margin after
                    217:  *  writing.  If maxrmargin is violated, it will break and continue
                    218:  *  writing from the right-margin, which will lead to the above
                    219:  *  scenario upon exit.
                    220:  *
                    221:  *  Otherwise, the line will break at the right margin.  Extremely long
                    222:  *  lines will cause the system to emit a warning (TODO: hyphenate, if
                    223:  *  possible).
                    224:  */
                    225: void
1.3       kristaps  226: term_flushln(struct termp *p)
1.1       kristaps  227: {
                    228:        int              i, j;
                    229:        size_t           vsz, vis, maxvis, mmax, bp;
                    230:
                    231:        /*
                    232:         * First, establish the maximum columns of "visible" content.
                    233:         * This is usually the difference between the right-margin and
                    234:         * an indentation, but can be, for tagged lists or columns, a
                    235:         * small set of values.
                    236:         */
                    237:
                    238:        assert(p->offset < p->rmargin);
                    239:        maxvis = p->rmargin - p->offset;
                    240:        mmax = p->maxrmargin - p->offset;
                    241:        bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
                    242:        vis = 0;
                    243:
                    244:        /*
                    245:         * If in the standard case (left-justified), then begin with our
                    246:         * indentation, otherwise (columns, etc.) just start spitting
                    247:         * out text.
                    248:         */
                    249:
                    250:        if ( ! (p->flags & TERMP_NOLPAD))
                    251:                /* LINTED */
                    252:                for (j = 0; j < (int)p->offset; j++)
                    253:                        putchar(' ');
                    254:
                    255:        for (i = 0; i < (int)p->col; i++) {
                    256:                /*
                    257:                 * Count up visible word characters.  Control sequences
                    258:                 * (starting with the CSI) aren't counted.  A space
                    259:                 * generates a non-printing word, which is valid (the
                    260:                 * space is printed according to regular spacing rules).
                    261:                 */
                    262:
                    263:                /* LINTED */
                    264:                for (j = i, vsz = 0; j < (int)p->col; j++) {
                    265:                        if (' ' == p->buf[j])
                    266:                                break;
                    267:                        else if (8 == p->buf[j])
                    268:                                j += 1;
                    269:                        else
                    270:                                vsz++;
                    271:                }
                    272:
                    273:                /*
                    274:                 * Do line-breaking.  If we're greater than our
                    275:                 * break-point and already in-line, break to the next
                    276:                 * line and start writing.  If we're at the line start,
                    277:                 * then write out the word (TODO: hyphenate) and break
                    278:                 * in a subsequent loop invocation.
                    279:                 */
                    280:
                    281:                if ( ! (TERMP_NOBREAK & p->flags)) {
                    282:                        if (vis && vis + vsz > bp) {
                    283:                                putchar('\n');
                    284:                                for (j = 0; j < (int)p->offset; j++)
                    285:                                        putchar(' ');
                    286:                                vis = 0;
1.4       kristaps  287:                        }
                    288:                } else if (vis && vis + vsz > bp) {
                    289:                        putchar('\n');
                    290:                        for (j = 0; j < (int)p->rmargin; j++)
                    291:                                putchar(' ');
                    292:                        vis = p->rmargin - p->offset;
1.1       kristaps  293:                }
                    294:
                    295:                /*
                    296:                 * Write out the word and a trailing space.  Omit the
                    297:                 * space if we're the last word in the line or beyond
                    298:                 * our breakpoint.
                    299:                 */
                    300:
                    301:                for ( ; i < (int)p->col; i++) {
                    302:                        if (' ' == p->buf[i])
                    303:                                break;
                    304:                        putchar(p->buf[i]);
                    305:                }
                    306:                vis += vsz;
                    307:                if (i < (int)p->col && vis <= bp) {
                    308:                        putchar(' ');
                    309:                        vis++;
                    310:                }
                    311:        }
                    312:
                    313:        /*
                    314:         * If we've overstepped our maximum visible no-break space, then
                    315:         * cause a newline and offset at the right margin.
                    316:         */
                    317:
                    318:        if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) {
                    319:                if ( ! (TERMP_NONOBREAK & p->flags)) {
                    320:                        putchar('\n');
                    321:                        for (i = 0; i < (int)p->rmargin; i++)
                    322:                                putchar(' ');
                    323:                }
                    324:                p->col = 0;
                    325:                return;
                    326:        }
                    327:
                    328:        /*
                    329:         * If we're not to right-marginalise it (newline), then instead
                    330:         * pad to the right margin and stay off.
                    331:         */
                    332:
                    333:        if (p->flags & TERMP_NOBREAK) {
                    334:                if ( ! (TERMP_NONOBREAK & p->flags))
                    335:                        for ( ; vis < maxvis; vis++)
                    336:                                putchar(' ');
                    337:        } else
                    338:                putchar('\n');
                    339:
                    340:        p->col = 0;
                    341: }
                    342:
                    343:
                    344: /*
                    345:  * A newline only breaks an existing line; it won't assert vertical
                    346:  * space.  All data in the output buffer is flushed prior to the newline
                    347:  * assertion.
                    348:  */
                    349: void
1.3       kristaps  350: term_newln(struct termp *p)
1.1       kristaps  351: {
                    352:
                    353:        p->flags |= TERMP_NOSPACE;
                    354:        if (0 == p->col) {
                    355:                p->flags &= ~TERMP_NOLPAD;
                    356:                return;
                    357:        }
1.3       kristaps  358:        term_flushln(p);
1.1       kristaps  359:        p->flags &= ~TERMP_NOLPAD;
                    360: }
                    361:
                    362:
                    363: /*
                    364:  * Asserts a vertical space (a full, empty line-break between lines).
                    365:  * Note that if used twice, this will cause two blank spaces and so on.
                    366:  * All data in the output buffer is flushed prior to the newline
                    367:  * assertion.
                    368:  */
                    369: void
1.3       kristaps  370: term_vspace(struct termp *p)
1.1       kristaps  371: {
                    372:
1.3       kristaps  373:        term_newln(p);
1.1       kristaps  374:        putchar('\n');
                    375: }
                    376:
                    377:
                    378: /*
                    379:  * Break apart a word into "pwords" (partial-words, usually from
                    380:  * breaking up a phrase into individual words) and, eventually, put them
                    381:  * into the output buffer.  If we're a literal word, then don't break up
                    382:  * the word and put it verbatim into the output buffer.
                    383:  */
                    384: void
1.3       kristaps  385: term_word(struct termp *p, const char *word)
1.1       kristaps  386: {
                    387:        int              i, j, len;
                    388:
1.3       kristaps  389:        len = (int)strlen(word);
                    390:
1.1       kristaps  391:        if (p->flags & TERMP_LITERAL) {
1.5       kristaps  392:                term_pword(p, word, len);
1.1       kristaps  393:                return;
                    394:        }
                    395:
                    396:        /* LINTED */
                    397:        for (j = i = 0; i < len; i++) {
                    398:                if (' ' != word[i]) {
                    399:                        j++;
                    400:                        continue;
                    401:                }
                    402:
                    403:                /* Escaped spaces don't delimit... */
                    404:                if (i && ' ' == word[i] && '\\' == word[i - 1]) {
                    405:                        j++;
                    406:                        continue;
                    407:                }
                    408:
                    409:                if (0 == j)
                    410:                        continue;
                    411:                assert(i >= j);
1.5       kristaps  412:                term_pword(p, &word[i - j], j);
1.1       kristaps  413:                j = 0;
                    414:        }
                    415:        if (j > 0) {
                    416:                assert(i >= j);
1.5       kristaps  417:                term_pword(p, &word[i - j], j);
1.1       kristaps  418:        }
                    419: }
                    420:
                    421:
1.3       kristaps  422: static void
1.5       kristaps  423: term_body(struct termp *p, struct termpair *ppair,
1.3       kristaps  424:                const struct mdoc_meta *meta,
                    425:                const struct mdoc_node *node)
                    426: {
                    427:
                    428:        term_node(p, ppair, meta, node);
                    429:        if (node->next)
1.5       kristaps  430:                term_body(p, ppair, meta, node->next);
1.3       kristaps  431: }
                    432:
                    433:
1.1       kristaps  434: /*
                    435:  * This is the main function for printing out nodes.  It's constituted
                    436:  * of PRE and POST functions, which correspond to prefix and infix
                    437:  * processing.  The termpair structure allows data to persist between
                    438:  * prefix and postfix invocations.
                    439:  */
1.3       kristaps  440: void
                    441: term_node(struct termp *p, struct termpair *ppair,
1.1       kristaps  442:                const struct mdoc_meta *meta,
                    443:                const struct mdoc_node *node)
                    444: {
                    445:        int              dochild;
                    446:        struct termpair  pair;
                    447:
                    448:        /* Some quick sanity-checking. */
                    449:
                    450:        sanity(node);
                    451:
                    452:        /* Pre-processing. */
                    453:
                    454:        dochild = 1;
                    455:        pair.ppair = ppair;
                    456:        pair.type = 0;
                    457:        pair.offset = pair.rmargin = 0;
                    458:        pair.flag = 0;
                    459:        pair.count = 0;
                    460:
                    461:        if (MDOC_TEXT != node->type) {
                    462:                if (termacts[node->tok].pre)
                    463:                        if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node))
                    464:                                dochild = 0;
                    465:        } else /* MDOC_TEXT == node->type */
1.3       kristaps  466:                term_word(p, node->string);
1.1       kristaps  467:
                    468:        /* Children. */
                    469:
                    470:        if (TERMPAIR_FLAG & pair.type)
                    471:                p->flags |= pair.flag;
                    472:
                    473:        if (dochild && node->child)
1.5       kristaps  474:                term_body(p, &pair, meta, node->child);
1.1       kristaps  475:
                    476:        if (TERMPAIR_FLAG & pair.type)
                    477:                p->flags &= ~pair.flag;
                    478:
                    479:        /* Post-processing. */
                    480:
                    481:        if (MDOC_TEXT != node->type)
                    482:                if (termacts[node->tok].post)
                    483:                        (*termacts[node->tok].post)(p, &pair, meta, node);
                    484: }
                    485:
                    486:
                    487: static void
1.5       kristaps  488: term_foot(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  489: {
                    490:        struct tm       *tm;
                    491:        char            *buf, *os;
                    492:
                    493:        if (NULL == (buf = malloc(p->rmargin)))
                    494:                err(1, "malloc");
                    495:        if (NULL == (os = malloc(p->rmargin)))
                    496:                err(1, "malloc");
                    497:
                    498:        tm = localtime(&meta->date);
                    499:
                    500: #ifdef __OpenBSD__
                    501:        if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    502: #else
                    503:        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
                    504: #endif
                    505:                err(1, "strftime");
                    506:
                    507:        (void)strlcpy(os, meta->os, p->rmargin);
                    508:
                    509:        /*
                    510:         * This is /slightly/ different from regular groff output
                    511:         * because we don't have page numbers.  Print the following:
                    512:         *
                    513:         * OS                                            MDOCDATE
                    514:         */
                    515:
1.3       kristaps  516:        term_vspace(p);
1.1       kristaps  517:
                    518:        p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
                    519:        p->rmargin = p->maxrmargin - strlen(buf);
                    520:        p->offset = 0;
                    521:
1.3       kristaps  522:        term_word(p, os);
                    523:        term_flushln(p);
1.1       kristaps  524:
                    525:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    526:        p->offset = p->rmargin;
                    527:        p->rmargin = p->maxrmargin;
                    528:        p->flags &= ~TERMP_NOBREAK;
                    529:
1.3       kristaps  530:        term_word(p, buf);
                    531:        term_flushln(p);
1.1       kristaps  532:
                    533:        free(buf);
                    534:        free(os);
                    535: }
                    536:
                    537:
                    538: static void
1.5       kristaps  539: term_head(struct termp *p, const struct mdoc_meta *meta)
1.1       kristaps  540: {
                    541:        char            *buf, *title;
                    542:
                    543:        p->rmargin = p->maxrmargin;
                    544:        p->offset = 0;
                    545:
                    546:        if (NULL == (buf = malloc(p->rmargin)))
                    547:                err(1, "malloc");
                    548:        if (NULL == (title = malloc(p->rmargin)))
                    549:                err(1, "malloc");
                    550:
                    551:        /*
                    552:         * The header is strange.  It has three components, which are
                    553:         * really two with the first duplicated.  It goes like this:
                    554:         *
                    555:         * IDENTIFIER              TITLE                   IDENTIFIER
                    556:         *
                    557:         * The IDENTIFIER is NAME(SECTION), which is the command-name
                    558:         * (if given, or "unknown" if not) followed by the manual page
                    559:         * section.  These are given in `Dt'.  The TITLE is a free-form
                    560:         * string depending on the manual volume.  If not specified, it
                    561:         * switches on the manual section.
                    562:         */
                    563:
                    564:        assert(meta->vol);
                    565:        (void)strlcpy(buf, meta->vol, p->rmargin);
                    566:
                    567:        if (meta->arch) {
                    568:                (void)strlcat(buf, " (", p->rmargin);
                    569:                (void)strlcat(buf, meta->arch, p->rmargin);
                    570:                (void)strlcat(buf, ")", p->rmargin);
                    571:        }
                    572:
                    573:        (void)snprintf(title, p->rmargin, "%s(%d)",
                    574:                        meta->title, meta->msec);
                    575:
                    576:        p->offset = 0;
                    577:        p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
                    578:        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
                    579:
1.3       kristaps  580:        term_word(p, title);
                    581:        term_flushln(p);
1.1       kristaps  582:
                    583:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    584:        p->offset = p->rmargin;
                    585:        p->rmargin = p->maxrmargin - strlen(title);
                    586:
1.3       kristaps  587:        term_word(p, buf);
                    588:        term_flushln(p);
1.1       kristaps  589:
                    590:        p->offset = p->rmargin;
                    591:        p->rmargin = p->maxrmargin;
                    592:        p->flags &= ~TERMP_NOBREAK;
                    593:        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
                    594:
1.3       kristaps  595:        term_word(p, title);
                    596:        term_flushln(p);
1.1       kristaps  597:
                    598:        p->rmargin = p->maxrmargin;
                    599:        p->offset = 0;
                    600:        p->flags &= ~TERMP_NOSPACE;
                    601:
                    602:        free(title);
                    603:        free(buf);
                    604: }
                    605:
                    606:
                    607: /*
                    608:  * Determine the symbol indicated by an escape sequences, that is, one
                    609:  * starting with a backslash.  Once done, we pass this value into the
                    610:  * output buffer by way of the symbol table.
                    611:  */
                    612: static void
1.5       kristaps  613: term_nescape(struct termp *p, const char *word, size_t len)
1.1       kristaps  614: {
                    615:        const char      *rhs;
                    616:        size_t           sz;
                    617:
1.3       kristaps  618:        if (NULL == (rhs = term_a2ascii(p->symtab, word, len, &sz)))
1.1       kristaps  619:                return;
1.5       kristaps  620:        term_stringa(p, rhs, sz);
1.1       kristaps  621: }
                    622:
                    623:
                    624: /*
                    625:  * Handle an escape sequence: determine its length and pass it to the
                    626:  * escape-symbol look table.  Note that we assume mdoc(3) has validated
                    627:  * the escape sequence (we assert upon badly-formed escape sequences).
                    628:  */
                    629: static void
1.5       kristaps  630: term_pescape(struct termp *p, const char *word, int *i, int len)
1.1       kristaps  631: {
                    632:        int              j;
                    633:
                    634:        if (++(*i) >= len)
                    635:                return;
                    636:
                    637:        if ('(' == word[*i]) {
                    638:                (*i)++;
                    639:                if (*i + 1 >= len)
                    640:                        return;
                    641:
1.5       kristaps  642:                term_nescape(p, &word[*i], 2);
1.1       kristaps  643:                (*i)++;
                    644:                return;
                    645:
                    646:        } else if ('*' == word[*i]) {
                    647:                (*i)++;
                    648:                if (*i >= len)
                    649:                        return;
                    650:
                    651:                switch (word[*i]) {
                    652:                case ('('):
                    653:                        (*i)++;
                    654:                        if (*i + 1 >= len)
                    655:                                return;
                    656:
1.5       kristaps  657:                        term_nescape(p, &word[*i], 2);
1.1       kristaps  658:                        (*i)++;
                    659:                        return;
                    660:                case ('['):
                    661:                        break;
                    662:                default:
1.5       kristaps  663:                        term_nescape(p, &word[*i], 1);
1.1       kristaps  664:                        return;
                    665:                }
                    666:
                    667:        } else if ('[' != word[*i]) {
1.5       kristaps  668:                term_nescape(p, &word[*i], 1);
1.1       kristaps  669:                return;
                    670:        }
                    671:
                    672:        (*i)++;
                    673:        for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++)
                    674:                /* Loop... */ ;
                    675:
                    676:        if (0 == word[*i])
                    677:                return;
                    678:
1.5       kristaps  679:        term_nescape(p, &word[*i - j], (size_t)j);
1.1       kristaps  680: }
                    681:
                    682:
                    683: /*
                    684:  * Handle pwords, partial words, which may be either a single word or a
                    685:  * phrase that cannot be broken down (such as a literal string).  This
                    686:  * handles word styling.
                    687:  */
                    688: static void
1.5       kristaps  689: term_pword(struct termp *p, const char *word, int len)
1.1       kristaps  690: {
                    691:        int              i;
                    692:
1.5       kristaps  693:        if (term_isclosedelim(word, len))
                    694:                if ( ! (TERMP_IGNDELIM & p->flags))
                    695:                        p->flags |= TERMP_NOSPACE;
                    696:
1.3       kristaps  697:        if ( ! (TERMP_NOSPACE & p->flags))
1.5       kristaps  698:                term_chara(p, ' ');
1.1       kristaps  699:
                    700:        if ( ! (p->flags & TERMP_NONOSPACE))
                    701:                p->flags &= ~TERMP_NOSPACE;
                    702:
                    703:        /*
                    704:         * If ANSI (word-length styling), then apply our style now,
                    705:         * before the word.
                    706:         */
                    707:
                    708:        for (i = 0; i < len; i++) {
                    709:                if ('\\' == word[i]) {
1.5       kristaps  710:                        term_pescape(p, word, &i, len);
1.1       kristaps  711:                        continue;
                    712:                }
                    713:
                    714:                if (TERMP_STYLE & p->flags) {
                    715:                        if (TERMP_BOLD & p->flags) {
1.5       kristaps  716:                                term_chara(p, word[i]);
                    717:                                term_chara(p, 8);
1.1       kristaps  718:                        }
                    719:                        if (TERMP_UNDER & p->flags) {
1.5       kristaps  720:                                term_chara(p, '_');
                    721:                                term_chara(p, 8);
1.1       kristaps  722:                        }
                    723:                }
                    724:
1.5       kristaps  725:                term_chara(p, word[i]);
1.1       kristaps  726:        }
1.5       kristaps  727:
                    728:        if (term_isopendelim(word, len))
                    729:                p->flags |= TERMP_NOSPACE;
1.1       kristaps  730: }
                    731:
                    732:
                    733: /*
1.5       kristaps  734:  * Like term_chara() but for arbitrary-length buffers.  Resize the
1.1       kristaps  735:  * buffer by a factor of two (if the buffer is less than that) or the
                    736:  * buffer's size.
                    737:  */
                    738: static void
1.5       kristaps  739: term_stringa(struct termp *p, const char *c, size_t sz)
1.1       kristaps  740: {
                    741:        size_t           s;
                    742:
                    743:        if (0 == sz)
                    744:                return;
                    745:
                    746:        assert(c);
                    747:        if (p->col + sz >= p->maxcols) {
                    748:                if (0 == p->maxcols)
                    749:                        p->maxcols = 256;
                    750:                s = sz > p->maxcols * 2 ? sz : p->maxcols * 2;
                    751:                p->buf = realloc(p->buf, s);
                    752:                if (NULL == p->buf)
                    753:                        err(1, "realloc");
                    754:                p->maxcols = s;
                    755:        }
                    756:
                    757:        (void)memcpy(&p->buf[(int)p->col], c, sz);
                    758:        p->col += sz;
                    759: }
                    760:
                    761:
                    762: /*
                    763:  * Insert a single character into the line-buffer.  If the buffer's
                    764:  * space is exceeded, then allocate more space by doubling the buffer
                    765:  * size.
                    766:  */
                    767: static void
1.5       kristaps  768: term_chara(struct termp *p, char c)
1.1       kristaps  769: {
                    770:        size_t           s;
                    771:
                    772:        if (p->col + 1 >= p->maxcols) {
                    773:                if (0 == p->maxcols)
                    774:                        p->maxcols = 256;
                    775:                s = p->maxcols * 2;
                    776:                p->buf = realloc(p->buf, s);
                    777:                if (NULL == p->buf)
                    778:                        err(1, "realloc");
                    779:                p->maxcols = s;
                    780:        }
                    781:        p->buf[(int)(p->col)++] = c;
                    782: }
                    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: }

CVSweb