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

Annotation of mandoc/terminal.c, Revision 1.5

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

CVSweb