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

Annotation of mandoc/term.c, Revision 1.141

1.141   ! kristaps    1: /*     $Id: term.c,v 1.140 2010/05/25 12:37:20 kristaps Exp $ */
1.1       kristaps    2: /*
1.75      kristaps    3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
1.1       kristaps    4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
1.74      kristaps    6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
1.1       kristaps    8:  *
1.74      kristaps    9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       kristaps   16:  */
1.128     kristaps   17: #ifdef HAVE_CONFIG_H
                     18: #include "config.h"
                     19: #endif
                     20:
1.126     kristaps   21: #include <sys/types.h>
                     22:
1.1       kristaps   23: #include <assert.h>
1.122     kristaps   24: #include <ctype.h>
1.141   ! kristaps   25: #include <stdint.h>
1.22      kristaps   26: #include <stdio.h>
1.1       kristaps   27: #include <stdlib.h>
                     28: #include <string.h>
1.113     kristaps   29: #include <time.h>
1.1       kristaps   30:
1.137     kristaps   31: #include "mandoc.h"
1.101     kristaps   32: #include "chars.h"
1.107     kristaps   33: #include "out.h"
1.71      kristaps   34: #include "term.h"
                     35: #include "man.h"
                     36: #include "mdoc.h"
1.105     kristaps   37: #include "main.h"
1.1       kristaps   38:
1.141   ! kristaps   39: static struct termp     *term_alloc(char *, enum termenc);
1.71      kristaps   40: static void              term_free(struct termp *);
1.125     kristaps   41: static void              spec(struct termp *, const char *, size_t);
                     42: static void              res(struct termp *, const char *, size_t);
                     43: static void              buffera(struct termp *, const char *, size_t);
                     44: static void              bufferc(struct termp *, char);
                     45: static void              adjbuf(struct termp *p, size_t);
                     46: static void              encode(struct termp *, const char *, size_t);
1.1       kristaps   47:
                     48:
1.71      kristaps   49: void *
1.141   ! kristaps   50: ascii_alloc(char *outopts)
1.10      kristaps   51: {
1.1       kristaps   52:
1.141   ! kristaps   53:        return(term_alloc(outopts, TERMENC_ASCII));
1.1       kristaps   54: }
                     55:
                     56:
1.99      kristaps   57: void
1.71      kristaps   58: terminal_free(void *arg)
1.11      kristaps   59: {
                     60:
1.71      kristaps   61:        term_free((struct termp *)arg);
1.11      kristaps   62: }
                     63:
                     64:
1.71      kristaps   65: static void
                     66: term_free(struct termp *p)
1.14      kristaps   67: {
                     68:
1.71      kristaps   69:        if (p->buf)
                     70:                free(p->buf);
1.102     kristaps   71:        if (p->symtab)
1.101     kristaps   72:                chars_free(p->symtab);
1.14      kristaps   73:
1.71      kristaps   74:        free(p);
1.14      kristaps   75: }
                     76:
                     77:
1.71      kristaps   78: static struct termp *
1.141   ! kristaps   79: term_alloc(char *outopts, enum termenc enc)
1.14      kristaps   80: {
1.141   ! kristaps   81:        struct termp    *p;
        !            82:        const char      *toks[2];
        !            83:        char            *v;
        !            84:        size_t           width;
        !            85:
        !            86:        toks[0] = "width";
        !            87:        toks[1] = NULL;
1.14      kristaps   88:
1.117     kristaps   89:        p = calloc(1, sizeof(struct termp));
                     90:        if (NULL == p) {
1.120     kristaps   91:                perror(NULL);
1.117     kristaps   92:                exit(EXIT_FAILURE);
                     93:        }
1.141   ! kristaps   94:
1.138     schwarze   95:        p->tabwidth = 5;
1.71      kristaps   96:        p->enc = enc;
1.141   ! kristaps   97:        width = 80;
        !            98:
        !            99:        while (outopts && *outopts)
        !           100:                switch (getsubopt(&outopts, UNCONST(toks), &v)) {
        !           101:                case (0):
        !           102:                        width = atoi(v);
        !           103:                        break;
        !           104:                default:
        !           105:                        break;
        !           106:                }
        !           107:
1.134     joerg     108:        /* Enforce some lower boundary. */
                    109:        if (width < 60)
                    110:                width = 60;
                    111:        p->defrmargin = width - 2;
1.71      kristaps  112:        return(p);
1.14      kristaps  113: }
                    114:
                    115:
1.71      kristaps  116: /*
                    117:  * Flush a line of text.  A "line" is loosely defined as being something
                    118:  * that should be followed by a newline, regardless of whether it's
                    119:  * broken apart by newlines getting there.  A line can also be a
1.130     kristaps  120:  * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
                    121:  * not have a trailing newline.
1.71      kristaps  122:  *
1.130     kristaps  123:  * The following flags may be specified:
1.71      kristaps  124:  *
                    125:  *  - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
                    126:  *    offset value.  This is useful when doing columnar lists where the
                    127:  *    prior column has right-padded.
                    128:  *
                    129:  *  - TERMP_NOBREAK: this is the most important and is used when making
                    130:  *    columns.  In short: don't print a newline and instead pad to the
                    131:  *    right margin.  Used in conjunction with TERMP_NOLPAD.
                    132:  *
1.91      kristaps  133:  *  - TERMP_TWOSPACE: when padding, make sure there are at least two
                    134:  *    space characters of padding.  Otherwise, rather break the line.
                    135:  *
1.84      kristaps  136:  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
                    137:  *    the line is overrun, and don't pad-right if it's underrun.
                    138:  *
                    139:  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
                    140:  *    overruning, instead save the position and continue at that point
                    141:  *    when the next invocation.
1.71      kristaps  142:  *
                    143:  *  In-line line breaking:
                    144:  *
                    145:  *  If TERMP_NOBREAK is specified and the line overruns the right
                    146:  *  margin, it will break and pad-right to the right margin after
                    147:  *  writing.  If maxrmargin is violated, it will break and continue
1.114     kristaps  148:  *  writing from the right-margin, which will lead to the above scenario
                    149:  *  upon exit.  Otherwise, the line will break at the right margin.
1.71      kristaps  150:  */
                    151: void
                    152: term_flushln(struct termp *p)
1.53      kristaps  153: {
1.114     kristaps  154:        int              i;     /* current input position in p->buf */
                    155:        size_t           vis;   /* current visual position on output */
                    156:        size_t           vbl;   /* number of blanks to prepend to output */
1.136     schwarze  157:        size_t           vend;  /* end of word visual position on output */
1.114     kristaps  158:        size_t           bp;    /* visual right border position */
                    159:        int              j;     /* temporary loop index */
1.140     kristaps  160:        int              jhy;   /* last hyphen before line overflow */
1.114     kristaps  161:        size_t           maxvis, mmax;
1.53      kristaps  162:
1.71      kristaps  163:        /*
                    164:         * First, establish the maximum columns of "visible" content.
                    165:         * This is usually the difference between the right-margin and
                    166:         * an indentation, but can be, for tagged lists or columns, a
1.115     kristaps  167:         * small set of values.
1.71      kristaps  168:         */
1.53      kristaps  169:
1.71      kristaps  170:        assert(p->offset < p->rmargin);
1.92      kristaps  171:
1.129     kristaps  172:        maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
1.119     kristaps  173:                /* LINTED */
1.129     kristaps  174:                0 : p->rmargin - p->offset - p->overstep;
                    175:        mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
1.119     kristaps  176:                /* LINTED */
1.129     kristaps  177:                0 : p->maxrmargin - p->offset - p->overstep;
1.92      kristaps  178:
1.71      kristaps  179:        bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.115     kristaps  180:
1.136     schwarze  181:        /*
                    182:         * Indent the first line of a paragraph.
                    183:         */
                    184:        vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
                    185:
1.115     kristaps  186:        /*
                    187:         * FIXME: if bp is zero, we still output the first word before
                    188:         * breaking the line.
                    189:         */
                    190:
1.136     schwarze  191:        vis = vend = i = 0;
                    192:        while (i < (int)p->col) {
1.71      kristaps  193:
                    194:                /*
1.138     schwarze  195:                 * Handle literal tab characters.
                    196:                 */
                    197:                for (j = i; j < (int)p->col; j++) {
                    198:                        if ('\t' != p->buf[j])
                    199:                                break;
                    200:                        vend = (vis/p->tabwidth+1)*p->tabwidth;
                    201:                        vbl += vend - vis;
                    202:                        vis = vend;
                    203:                }
                    204:
                    205:                /*
1.71      kristaps  206:                 * Count up visible word characters.  Control sequences
                    207:                 * (starting with the CSI) aren't counted.  A space
                    208:                 * generates a non-printing word, which is valid (the
                    209:                 * space is printed according to regular spacing rules).
                    210:                 */
                    211:
                    212:                /* LINTED */
1.140     kristaps  213:                for (jhy = 0; j < (int)p->col; j++) {
1.138     schwarze  214:                        if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
1.71      kristaps  215:                                break;
1.140     kristaps  216:                        if (8 != p->buf[j]) {
                    217:                                if (vend > vis && vend < bp &&
                    218:                                    ASCII_HYPH == p->buf[j])
                    219:                                        jhy = j;
                    220:                                vend++;
                    221:                        } else
1.136     schwarze  222:                                vend--;
1.71      kristaps  223:                }
1.53      kristaps  224:
1.71      kristaps  225:                /*
1.81      kristaps  226:                 * Find out whether we would exceed the right margin.
1.136     schwarze  227:                 * If so, break to the next line.
1.81      kristaps  228:                 */
1.140     kristaps  229:                if (vend > bp && 0 == jhy && vis > 0) {
1.136     schwarze  230:                        vend -= vis;
1.81      kristaps  231:                        putchar('\n');
                    232:                        if (TERMP_NOBREAK & p->flags) {
1.139     schwarze  233:                                p->viscol = p->rmargin;
1.81      kristaps  234:                                for (j = 0; j < (int)p->rmargin; j++)
                    235:                                        putchar(' ');
1.136     schwarze  236:                                vend += p->rmargin - p->offset;
1.81      kristaps  237:                        } else {
1.139     schwarze  238:                                p->viscol = 0;
1.136     schwarze  239:                                vbl = p->offset;
1.81      kristaps  240:                        }
1.130     kristaps  241:
1.129     kristaps  242:                        /* Remove the p->overstep width. */
1.130     kristaps  243:
1.112     kristaps  244:                        bp += (int)/* LINTED */
1.129     kristaps  245:                                p->overstep;
                    246:                        p->overstep = 0;
1.71      kristaps  247:                }
1.53      kristaps  248:
1.138     schwarze  249:                /*
                    250:                 * Skip leading tabs, they were handled above.
                    251:                 */
                    252:                while (i < (int)p->col && '\t' == p->buf[i])
                    253:                        i++;
                    254:
1.130     kristaps  255:                /* Write out the [remaining] word. */
1.136     schwarze  256:                for ( ; i < (int)p->col; i++) {
1.140     kristaps  257:                        if (vend > bp && jhy > 0 && i > jhy)
                    258:                                break;
1.138     schwarze  259:                        if ('\t' == p->buf[i])
                    260:                                break;
1.136     schwarze  261:                        if (' ' == p->buf[i]) {
                    262:                                while (' ' == p->buf[i]) {
                    263:                                        vbl++;
                    264:                                        i++;
                    265:                                }
1.71      kristaps  266:                                break;
1.136     schwarze  267:                        }
                    268:                        if (ASCII_NBRSP == p->buf[i]) {
                    269:                                vbl++;
                    270:                                continue;
                    271:                        }
1.130     kristaps  272:
1.136     schwarze  273:                        /*
                    274:                         * Now we definitely know there will be
                    275:                         * printable characters to output,
                    276:                         * so write preceding white space now.
                    277:                         */
                    278:                        if (vbl) {
                    279:                                for (j = 0; j < (int)vbl; j++)
                    280:                                        putchar(' ');
1.139     schwarze  281:                                p->viscol += vbl;
1.136     schwarze  282:                                vbl = 0;
                    283:                        }
1.140     kristaps  284:
                    285:                        if (ASCII_HYPH == p->buf[i])
                    286:                                putchar('-');
                    287:                        else
                    288:                                putchar(p->buf[i]);
                    289:
1.139     schwarze  290:                        p->viscol += 1;
1.136     schwarze  291:                }
                    292:                vend += vbl;
                    293:                vis = vend;
1.71      kristaps  294:        }
1.111     kristaps  295:
1.91      kristaps  296:        p->col = 0;
1.129     kristaps  297:        p->overstep = 0;
1.15      kristaps  298:
1.91      kristaps  299:        if ( ! (TERMP_NOBREAK & p->flags)) {
1.139     schwarze  300:                p->viscol = 0;
1.91      kristaps  301:                putchar('\n');
1.15      kristaps  302:                return;
1.71      kristaps  303:        }
1.15      kristaps  304:
1.91      kristaps  305:        if (TERMP_HANG & p->flags) {
                    306:                /* We need one blank after the tag. */
1.129     kristaps  307:                p->overstep = /* LINTED */
1.92      kristaps  308:                        vis - maxvis + 1;
1.91      kristaps  309:
                    310:                /*
                    311:                 * Behave exactly the same way as groff:
1.92      kristaps  312:                 * If we have overstepped the margin, temporarily move
                    313:                 * it to the right and flag the rest of the line to be
                    314:                 * shorter.
1.91      kristaps  315:                 * If we landed right at the margin, be happy.
1.92      kristaps  316:                 * If we are one step before the margin, temporarily
                    317:                 * move it one step LEFT and flag the rest of the line
                    318:                 * to be longer.
1.91      kristaps  319:                 */
1.129     kristaps  320:                if (p->overstep >= -1) {
                    321:                        assert((int)maxvis + p->overstep >= 0);
1.92      kristaps  322:                        /* LINTED */
1.129     kristaps  323:                        maxvis += p->overstep;
1.92      kristaps  324:                } else
1.129     kristaps  325:                        p->overstep = 0;
1.91      kristaps  326:
                    327:        } else if (TERMP_DANGLE & p->flags)
                    328:                return;
1.15      kristaps  329:
1.92      kristaps  330:        /* Right-pad. */
                    331:        if (maxvis > vis + /* LINTED */
1.139     schwarze  332:                        ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
                    333:                p->viscol += maxvis - vis;
1.91      kristaps  334:                for ( ; vis < maxvis; vis++)
                    335:                        putchar(' ');
1.139     schwarze  336:        } else {        /* ...or newline break. */
1.71      kristaps  337:                putchar('\n');
1.139     schwarze  338:                p->viscol = p->rmargin;
1.91      kristaps  339:                for (i = 0; i < (int)p->rmargin; i++)
                    340:                        putchar(' ');
                    341:        }
1.15      kristaps  342: }
                    343:
                    344:
1.71      kristaps  345: /*
                    346:  * A newline only breaks an existing line; it won't assert vertical
                    347:  * space.  All data in the output buffer is flushed prior to the newline
                    348:  * assertion.
                    349:  */
                    350: void
                    351: term_newln(struct termp *p)
1.15      kristaps  352: {
                    353:
1.71      kristaps  354:        p->flags |= TERMP_NOSPACE;
1.139     schwarze  355:        if (0 == p->col && 0 == p->viscol) {
1.71      kristaps  356:                p->flags &= ~TERMP_NOLPAD;
1.15      kristaps  357:                return;
1.16      kristaps  358:        }
1.71      kristaps  359:        term_flushln(p);
                    360:        p->flags &= ~TERMP_NOLPAD;
1.16      kristaps  361: }
                    362:
                    363:
1.71      kristaps  364: /*
                    365:  * Asserts a vertical space (a full, empty line-break between lines).
                    366:  * Note that if used twice, this will cause two blank spaces and so on.
                    367:  * All data in the output buffer is flushed prior to the newline
                    368:  * assertion.
                    369:  */
                    370: void
                    371: term_vspace(struct termp *p)
1.16      kristaps  372: {
                    373:
1.62      kristaps  374:        term_newln(p);
1.139     schwarze  375:        p->viscol = 0;
1.71      kristaps  376:        putchar('\n');
1.16      kristaps  377: }
                    378:
                    379:
1.71      kristaps  380: static void
1.125     kristaps  381: spec(struct termp *p, const char *word, size_t len)
1.17      kristaps  382: {
1.71      kristaps  383:        const char      *rhs;
                    384:        size_t           sz;
1.17      kristaps  385:
1.101     kristaps  386:        rhs = chars_a2ascii(p->symtab, word, len, &sz);
1.125     kristaps  387:        if (rhs)
                    388:                encode(p, rhs, sz);
1.94      kristaps  389: }
                    390:
                    391:
                    392: static void
1.125     kristaps  393: res(struct termp *p, const char *word, size_t len)
1.94      kristaps  394: {
                    395:        const char      *rhs;
                    396:        size_t           sz;
                    397:
1.101     kristaps  398:        rhs = chars_a2res(p->symtab, word, len, &sz);
1.125     kristaps  399:        if (rhs)
                    400:                encode(p, rhs, sz);
                    401: }
                    402:
                    403:
                    404: void
                    405: term_fontlast(struct termp *p)
                    406: {
                    407:        enum termfont    f;
                    408:
                    409:        f = p->fontl;
                    410:        p->fontl = p->fontq[p->fonti];
                    411:        p->fontq[p->fonti] = f;
                    412: }
                    413:
                    414:
                    415: void
                    416: term_fontrepl(struct termp *p, enum termfont f)
                    417: {
                    418:
                    419:        p->fontl = p->fontq[p->fonti];
                    420:        p->fontq[p->fonti] = f;
                    421: }
                    422:
                    423:
                    424: void
                    425: term_fontpush(struct termp *p, enum termfont f)
                    426: {
                    427:
                    428:        assert(p->fonti + 1 < 10);
                    429:        p->fontl = p->fontq[p->fonti];
                    430:        p->fontq[++p->fonti] = f;
                    431: }
                    432:
                    433:
                    434: const void *
                    435: term_fontq(struct termp *p)
                    436: {
                    437:
                    438:        return(&p->fontq[p->fonti]);
                    439: }
                    440:
                    441:
                    442: enum termfont
                    443: term_fonttop(struct termp *p)
                    444: {
                    445:
                    446:        return(p->fontq[p->fonti]);
                    447: }
                    448:
                    449:
                    450: void
                    451: term_fontpopq(struct termp *p, const void *key)
                    452: {
                    453:
                    454:        while (p->fonti >= 0 && key != &p->fontq[p->fonti])
                    455:                p->fonti--;
                    456:        assert(p->fonti >= 0);
                    457: }
1.94      kristaps  458:
1.125     kristaps  459:
                    460: void
                    461: term_fontpop(struct termp *p)
                    462: {
                    463:
                    464:        assert(p->fonti);
                    465:        p->fonti--;
1.17      kristaps  466: }
                    467:
                    468:
1.71      kristaps  469: /*
                    470:  * Handle pwords, partial words, which may be either a single word or a
                    471:  * phrase that cannot be broken down (such as a literal string).  This
                    472:  * handles word styling.
                    473:  */
1.86      kristaps  474: void
                    475: term_word(struct termp *p, const char *word)
1.65      kristaps  476: {
1.124     kristaps  477:        const char      *sv, *seq;
1.125     kristaps  478:        int              sz;
1.124     kristaps  479:        size_t           ssz;
                    480:        enum roffdeco    deco;
1.71      kristaps  481:
1.100     kristaps  482:        sv = word;
                    483:
1.123     kristaps  484:        if (word[0] && '\0' == word[1])
1.100     kristaps  485:                switch (word[0]) {
                    486:                case('.'):
                    487:                        /* FALLTHROUGH */
                    488:                case(','):
                    489:                        /* FALLTHROUGH */
                    490:                case(';'):
                    491:                        /* FALLTHROUGH */
                    492:                case(':'):
                    493:                        /* FALLTHROUGH */
                    494:                case('?'):
                    495:                        /* FALLTHROUGH */
                    496:                case('!'):
                    497:                        /* FALLTHROUGH */
                    498:                case(')'):
                    499:                        /* FALLTHROUGH */
                    500:                case(']'):
                    501:                        if ( ! (TERMP_IGNDELIM & p->flags))
                    502:                                p->flags |= TERMP_NOSPACE;
                    503:                        break;
                    504:                default:
                    505:                        break;
                    506:                }
1.65      kristaps  507:
1.133     kristaps  508:        if ( ! (TERMP_NOSPACE & p->flags)) {
1.125     kristaps  509:                bufferc(p, ' ');
1.133     kristaps  510:                if (TERMP_SENTENCE & p->flags)
                    511:                        bufferc(p, ' ');
                    512:        }
1.65      kristaps  513:
1.71      kristaps  514:        if ( ! (p->flags & TERMP_NONOSPACE))
                    515:                p->flags &= ~TERMP_NOSPACE;
1.133     kristaps  516:
                    517:        p->flags &= ~TERMP_SENTENCE;
1.65      kristaps  518:
1.125     kristaps  519:        /* FIXME: use strcspn. */
1.124     kristaps  520:
                    521:        while (*word) {
                    522:                if ('\\' != *word) {
1.125     kristaps  523:                        encode(p, word, 1);
1.124     kristaps  524:                        word++;
                    525:                        continue;
                    526:                }
                    527:
                    528:                seq = ++word;
                    529:                sz = a2roffdeco(&deco, &seq, &ssz);
                    530:
                    531:                switch (deco) {
                    532:                case (DECO_RESERVED):
1.125     kristaps  533:                        res(p, seq, ssz);
1.124     kristaps  534:                        break;
                    535:                case (DECO_SPECIAL):
1.125     kristaps  536:                        spec(p, seq, ssz);
1.124     kristaps  537:                        break;
                    538:                case (DECO_BOLD):
1.125     kristaps  539:                        term_fontrepl(p, TERMFONT_BOLD);
1.124     kristaps  540:                        break;
                    541:                case (DECO_ITALIC):
1.125     kristaps  542:                        term_fontrepl(p, TERMFONT_UNDER);
1.124     kristaps  543:                        break;
                    544:                case (DECO_ROMAN):
1.125     kristaps  545:                        term_fontrepl(p, TERMFONT_NONE);
1.124     kristaps  546:                        break;
                    547:                case (DECO_PREVIOUS):
1.125     kristaps  548:                        term_fontlast(p);
1.124     kristaps  549:                        break;
                    550:                default:
                    551:                        break;
                    552:                }
1.127     kristaps  553:
1.124     kristaps  554:                word += sz;
1.127     kristaps  555:                if (DECO_NOSPACE == deco && '\0' == *word)
                    556:                        p->flags |= TERMP_NOSPACE;
1.124     kristaps  557:        }
1.65      kristaps  558:
1.131     kristaps  559:        /*
                    560:         * Note that we don't process the pipe: the parser sees it as
                    561:         * punctuation, but we don't in terms of typography.
                    562:         */
1.100     kristaps  563:        if (sv[0] && 0 == sv[1])
                    564:                switch (sv[0]) {
                    565:                case('('):
                    566:                        /* FALLTHROUGH */
                    567:                case('['):
                    568:                        p->flags |= TERMP_NOSPACE;
                    569:                        break;
                    570:                default:
                    571:                        break;
                    572:                }
1.65      kristaps  573: }
                    574:
                    575:
1.71      kristaps  576: static void
1.125     kristaps  577: adjbuf(struct termp *p, size_t sz)
1.51      kristaps  578: {
                    579:
1.125     kristaps  580:        if (0 == p->maxcols)
                    581:                p->maxcols = 1024;
                    582:        while (sz >= p->maxcols)
                    583:                p->maxcols <<= 2;
                    584:
                    585:        p->buf = realloc(p->buf, p->maxcols);
                    586:        if (NULL == p->buf) {
                    587:                perror(NULL);
                    588:                exit(EXIT_FAILURE);
1.71      kristaps  589:        }
1.51      kristaps  590: }
                    591:
1.79      kristaps  592:
                    593: static void
1.125     kristaps  594: buffera(struct termp *p, const char *word, size_t sz)
1.79      kristaps  595: {
1.125     kristaps  596:
                    597:        if (p->col + sz >= p->maxcols)
                    598:                adjbuf(p, p->col + sz);
                    599:
1.126     kristaps  600:        memcpy(&p->buf[(int)p->col], word, sz);
1.125     kristaps  601:        p->col += sz;
                    602: }
                    603:
                    604:
                    605: static void
                    606: bufferc(struct termp *p, char c)
                    607: {
                    608:
                    609:        if (p->col + 1 >= p->maxcols)
                    610:                adjbuf(p, p->col + 1);
                    611:
1.126     kristaps  612:        p->buf[(int)p->col++] = c;
1.125     kristaps  613: }
                    614:
                    615:
                    616: static void
                    617: encode(struct termp *p, const char *word, size_t sz)
                    618: {
                    619:        enum termfont     f;
                    620:        int               i;
                    621:
                    622:        /*
                    623:         * Encode and buffer a string of characters.  If the current
                    624:         * font mode is unset, buffer directly, else encode then buffer
                    625:         * character by character.
                    626:         */
                    627:
                    628:        if (TERMFONT_NONE == (f = term_fonttop(p))) {
                    629:                buffera(p, word, sz);
                    630:                return;
                    631:        }
                    632:
                    633:        for (i = 0; i < (int)sz; i++) {
                    634:                if ( ! isgraph((u_char)word[i])) {
                    635:                        bufferc(p, word[i]);
                    636:                        continue;
1.79      kristaps  637:                }
1.125     kristaps  638:
                    639:                if (TERMFONT_UNDER == f)
                    640:                        bufferc(p, '_');
                    641:                else
                    642:                        bufferc(p, word[i]);
                    643:
                    644:                bufferc(p, 8);
                    645:                bufferc(p, word[i]);
1.79      kristaps  646:        }
                    647: }
1.106     kristaps  648:
                    649:
1.107     kristaps  650: size_t
                    651: term_vspan(const struct roffsu *su)
1.106     kristaps  652: {
                    653:        double           r;
                    654:
1.107     kristaps  655:        switch (su->unit) {
1.106     kristaps  656:        case (SCALE_CM):
1.107     kristaps  657:                r = su->scale * 2;
1.106     kristaps  658:                break;
                    659:        case (SCALE_IN):
1.107     kristaps  660:                r = su->scale * 6;
1.106     kristaps  661:                break;
                    662:        case (SCALE_PC):
1.107     kristaps  663:                r = su->scale;
1.106     kristaps  664:                break;
                    665:        case (SCALE_PT):
1.107     kristaps  666:                r = su->scale / 8;
1.106     kristaps  667:                break;
                    668:        case (SCALE_MM):
1.107     kristaps  669:                r = su->scale / 1000;
1.106     kristaps  670:                break;
                    671:        case (SCALE_VS):
1.107     kristaps  672:                r = su->scale;
1.106     kristaps  673:                break;
                    674:        default:
1.107     kristaps  675:                r = su->scale - 1;
1.106     kristaps  676:                break;
                    677:        }
                    678:
                    679:        if (r < 0.0)
                    680:                r = 0.0;
1.107     kristaps  681:        return(/* LINTED */(size_t)
1.106     kristaps  682:                        r);
                    683: }
                    684:
                    685:
1.107     kristaps  686: size_t
                    687: term_hspan(const struct roffsu *su)
1.106     kristaps  688: {
                    689:        double           r;
                    690:
1.108     kristaps  691:        /* XXX: CM, IN, and PT are approximations. */
                    692:
1.107     kristaps  693:        switch (su->unit) {
1.106     kristaps  694:        case (SCALE_CM):
1.108     kristaps  695:                r = 4 * su->scale;
1.106     kristaps  696:                break;
                    697:        case (SCALE_IN):
1.108     kristaps  698:                /* XXX: this is an approximation. */
                    699:                r = 10 * su->scale;
1.106     kristaps  700:                break;
                    701:        case (SCALE_PC):
1.108     kristaps  702:                r = (10 * su->scale) / 6;
1.106     kristaps  703:                break;
                    704:        case (SCALE_PT):
1.108     kristaps  705:                r = (10 * su->scale) / 72;
1.106     kristaps  706:                break;
                    707:        case (SCALE_MM):
1.107     kristaps  708:                r = su->scale / 1000; /* FIXME: double-check. */
1.106     kristaps  709:                break;
                    710:        case (SCALE_VS):
1.107     kristaps  711:                r = su->scale * 2 - 1; /* FIXME: double-check. */
1.106     kristaps  712:                break;
                    713:        default:
1.107     kristaps  714:                r = su->scale;
1.106     kristaps  715:                break;
                    716:        }
                    717:
                    718:        if (r < 0.0)
                    719:                r = 0.0;
1.107     kristaps  720:        return((size_t)/* LINTED */
1.106     kristaps  721:                        r);
                    722: }
                    723:
                    724:

CVSweb