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

Annotation of mandoc/terminal.c, Revision 1.1

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

CVSweb