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

Annotation of mandoc/term.c, Revision 1.145

1.145   ! kristaps    1: /*     $Id: term.c,v 1.144 2010/06/08 09:20:08 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.144     kristaps   25: #include <getopt.h>
1.141     kristaps   26: #include <stdint.h>
1.22      kristaps   27: #include <stdio.h>
1.1       kristaps   28: #include <stdlib.h>
                     29: #include <string.h>
1.113     kristaps   30: #include <time.h>
1.1       kristaps   31:
1.137     kristaps   32: #include "mandoc.h"
1.101     kristaps   33: #include "chars.h"
1.107     kristaps   34: #include "out.h"
1.71      kristaps   35: #include "term.h"
                     36: #include "man.h"
                     37: #include "mdoc.h"
1.105     kristaps   38: #include "main.h"
1.1       kristaps   39:
1.142     kristaps   40: #define        PS_CHAR_WIDTH     6
                     41: #define        PS_CHAR_HEIGHT    12
                     42: #define        PS_CHAR_TOPMARG  (792 - 24)
                     43: #define        PS_CHAR_TOP      (PS_CHAR_TOPMARG - 36)
                     44: #define        PS_CHAR_LEFT      36
                     45: #define        PS_CHAR_BOTMARG   24
                     46: #define        PS_CHAR_BOT      (PS_CHAR_BOTMARG + 36)
                     47:
1.125     kristaps   48: static void              spec(struct termp *, const char *, size_t);
                     49: static void              res(struct termp *, const char *, size_t);
                     50: static void              buffera(struct termp *, const char *, size_t);
                     51: static void              bufferc(struct termp *, char);
                     52: static void              adjbuf(struct termp *p, size_t);
                     53: static void              encode(struct termp *, const char *, size_t);
1.142     kristaps   54: static void              advance(struct termp *, size_t);
                     55: static void              endline(struct termp *);
                     56: static void              letter(struct termp *, char);
                     57: static void              pageopen(struct termp *);
1.1       kristaps   58:
                     59:
1.71      kristaps   60: void *
1.141     kristaps   61: ascii_alloc(char *outopts)
1.10      kristaps   62: {
1.145   ! kristaps   63:        struct termp    *p;
        !            64:        const char      *toks[2];
        !            65:        char            *v;
        !            66:
        !            67:        if (NULL == (p = term_alloc(TERMENC_ASCII)))
        !            68:                return(NULL);
        !            69:
        !            70:        p->type = TERMTYPE_CHAR;
1.1       kristaps   71:
1.145   ! kristaps   72:        toks[0] = "width";
        !            73:        toks[1] = NULL;
1.142     kristaps   74:
1.145   ! kristaps   75:        while (outopts && *outopts)
        !            76:                switch (getsubopt(&outopts, UNCONST(toks), &v)) {
        !            77:                case (0):
        !            78:                        p->defrmargin = (size_t)atoi(v);
        !            79:                        break;
        !            80:                default:
        !            81:                        break;
        !            82:                }
1.142     kristaps   83:
1.145   ! kristaps   84:        /* Enforce a lower boundary. */
        !            85:        if (p->defrmargin < 58)
        !            86:                p->defrmargin = 58;
1.142     kristaps   87:
1.145   ! kristaps   88:        return(p);
1.1       kristaps   89: }
                     90:
                     91:
1.99      kristaps   92: void
1.145   ! kristaps   93: ascii_free(void *arg)
1.11      kristaps   94: {
                     95:
1.71      kristaps   96:        term_free((struct termp *)arg);
1.11      kristaps   97: }
                     98:
                     99:
1.145   ! kristaps  100: void
1.71      kristaps  101: term_free(struct termp *p)
1.14      kristaps  102: {
                    103:
1.71      kristaps  104:        if (p->buf)
                    105:                free(p->buf);
1.102     kristaps  106:        if (p->symtab)
1.101     kristaps  107:                chars_free(p->symtab);
1.145   ! kristaps  108:
1.142     kristaps  109:        free(p);
                    110: }
                    111:
                    112:
                    113: /*
                    114:  * Push a single letter into our output engine.
                    115:  */
                    116: static void
                    117: letter(struct termp *p, char c)
                    118: {
                    119:
                    120:        if (TERMTYPE_CHAR == p->type) {
                    121:                /*
                    122:                 * If using the terminal device, just push the letter
                    123:                 * out into the screen.
                    124:                 */
                    125:                putchar(c);
                    126:                return;
                    127:        }
                    128:
                    129:        if ( ! (PS_INLINE & p->psstate)) {
                    130:                /*
                    131:                 * If we're not in a PostScript "word" context, then
                    132:                 * open one now at the current cursor.
                    133:                 */
                    134:                printf("%zu %zu moveto\n", p->pscol, p->psrow);
                    135:                putchar('(');
                    136:                p->psstate |= PS_INLINE;
                    137:        }
                    138:
                    139:        /*
                    140:         * We need to escape these characters as per the PostScript
                    141:         * specification.  We would also escape non-graphable characters
                    142:         * (like tabs), but none of them would get to this point and
                    143:         * it's superfluous to abort() on them.
                    144:         */
                    145:
                    146:        switch (c) {
                    147:        case ('('):
                    148:                /* FALLTHROUGH */
                    149:        case (')'):
                    150:                /* FALLTHROUGH */
                    151:        case ('\\'):
                    152:                putchar('\\');
                    153:                break;
                    154:        default:
                    155:                break;
                    156:        }
                    157:
                    158:        /* Write the character and adjust where we are on the page. */
                    159:        putchar(c);
                    160:        p->pscol += PS_CHAR_WIDTH;
                    161: }
                    162:
                    163:
                    164: /*
                    165:  * Begin a "terminal" context.  Since terminal encompasses PostScript,
                    166:  * the actual terminal, etc., there are a few things we can do here.
                    167:  */
                    168: void
                    169: term_begin(struct termp *p, term_margin head,
                    170:                term_margin foot, const void *arg)
                    171: {
                    172:
                    173:        p->headf = head;
                    174:        p->footf = foot;
                    175:        p->argf = arg;
                    176:
                    177:        if (TERMTYPE_CHAR == p->type) {
                    178:                /* Emit the header and be done. */
                    179:                (*p->headf)(p, p->argf);
                    180:                return;
                    181:        }
                    182:
                    183:        /*
                    184:         * Emit the standard PostScript prologue, set our initial page
                    185:         * position, then run pageopen() on the initial page.
                    186:         */
                    187:
                    188:        printf("%s\n", "%!PS");
                    189:        printf("%s\n", "/Courier");
                    190:        printf("%s\n", "10 selectfont");
                    191:
                    192:        p->pspage = 1;
                    193:        p->psstate = 0;
                    194:        pageopen(p);
                    195: }
                    196:
                    197:
                    198: /*
                    199:  * Open a page.  This is only used for -Tps at the moment.  It opens a
                    200:  * page context, printing the header and the footer.  THE OUTPUT BUFFER
                    201:  * MUST BE EMPTY.  If it is not, output will ghost on the next line and
                    202:  * we'll be all gross and out of state.
                    203:  */
                    204: static void
                    205: pageopen(struct termp *p)
                    206: {
                    207:
                    208:        assert(TERMTYPE_PS == p->type);
                    209:        assert(0 == p->psstate);
                    210:
                    211:        p->pscol = PS_CHAR_LEFT;
                    212:        p->psrow = PS_CHAR_TOPMARG;
                    213:        p->psstate |= PS_MARGINS;
                    214:
                    215:        (*p->headf)(p, p->argf);
                    216:        endline(p);
                    217:
                    218:        p->psstate &= ~PS_MARGINS;
                    219:        assert(0 == p->psstate);
1.14      kristaps  220:
1.142     kristaps  221:        p->pscol = PS_CHAR_LEFT;
                    222:        p->psrow = PS_CHAR_BOTMARG;
                    223:        p->psstate |= PS_MARGINS;
                    224:
                    225:        (*p->footf)(p, p->argf);
                    226:        endline(p);
                    227:
                    228:        p->psstate &= ~PS_MARGINS;
                    229:        assert(0 == p->psstate);
                    230:
                    231:        p->pscol = PS_CHAR_LEFT;
                    232:        p->psrow = PS_CHAR_TOP;
                    233:
                    234: }
                    235:
                    236:
                    237: void
                    238: term_end(struct termp *p)
                    239: {
                    240:
                    241:        if (TERMTYPE_CHAR == p->type) {
                    242:                (*p->footf)(p, p->argf);
                    243:                return;
                    244:        }
                    245:
                    246:        printf("%s\n", "%%END");
                    247: }
                    248:
                    249:
                    250: static void
                    251: endline(struct termp *p)
                    252: {
                    253:
                    254:        if (TERMTYPE_CHAR == p->type) {
                    255:                putchar('\n');
                    256:                return;
                    257:        }
                    258:
                    259:        if (PS_INLINE & p->psstate) {
                    260:                printf(") show\n");
                    261:                p->psstate &= ~PS_INLINE;
                    262:        }
                    263:
                    264:        if (PS_MARGINS & p->psstate)
                    265:                return;
                    266:
                    267:        p->pscol = PS_CHAR_LEFT;
                    268:        if (p->psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
                    269:                p->psrow -= PS_CHAR_HEIGHT;
                    270:                return;
                    271:        }
                    272:
                    273:        /*
                    274:         * XXX: can't run pageopen() until we're certain a flushln() has
                    275:         * occured, else the buf will reopen in an awkward state on the
                    276:         * next line.
                    277:         */
                    278:        printf("showpage\n");
                    279:        p->psrow = PS_CHAR_TOP;
                    280: }
                    281:
                    282:
                    283: /*
                    284:  * Advance the output engine by a certain amount of whitespace.
                    285:  */
                    286: static void
                    287: advance(struct termp *p, size_t len)
                    288: {
                    289:        size_t          i;
                    290:
                    291:        if (TERMTYPE_CHAR == p->type) {
                    292:                /* Just print whitespace on the terminal. */
                    293:                for (i = 0; i < len; i++)
                    294:                        putchar(' ');
                    295:                return;
                    296:        }
                    297:
                    298:        if (PS_INLINE & p->psstate) {
                    299:                /* Dump out any existing line scope. */
                    300:                printf(") show\n");
                    301:                p->psstate &= ~PS_INLINE;
                    302:        }
                    303:
                    304:        p->pscol += len ? len * PS_CHAR_WIDTH : 0;
1.14      kristaps  305: }
                    306:
                    307:
1.145   ! kristaps  308: struct termp *
        !           309: term_alloc(enum termenc enc)
1.14      kristaps  310: {
1.141     kristaps  311:        struct termp    *p;
1.14      kristaps  312:
1.117     kristaps  313:        p = calloc(1, sizeof(struct termp));
                    314:        if (NULL == p) {
1.120     kristaps  315:                perror(NULL);
1.117     kristaps  316:                exit(EXIT_FAILURE);
                    317:        }
1.141     kristaps  318:
1.138     schwarze  319:        p->tabwidth = 5;
1.71      kristaps  320:        p->enc = enc;
1.145   ! kristaps  321:        p->defrmargin = 78;
1.71      kristaps  322:        return(p);
1.14      kristaps  323: }
                    324:
                    325:
1.71      kristaps  326: /*
                    327:  * Flush a line of text.  A "line" is loosely defined as being something
                    328:  * that should be followed by a newline, regardless of whether it's
                    329:  * broken apart by newlines getting there.  A line can also be a
1.130     kristaps  330:  * fragment of a columnar list (`Bl -tag' or `Bl -column'), which does
                    331:  * not have a trailing newline.
1.71      kristaps  332:  *
1.130     kristaps  333:  * The following flags may be specified:
1.71      kristaps  334:  *
                    335:  *  - TERMP_NOLPAD: when beginning to write the line, don't left-pad the
                    336:  *    offset value.  This is useful when doing columnar lists where the
                    337:  *    prior column has right-padded.
                    338:  *
                    339:  *  - TERMP_NOBREAK: this is the most important and is used when making
                    340:  *    columns.  In short: don't print a newline and instead pad to the
                    341:  *    right margin.  Used in conjunction with TERMP_NOLPAD.
                    342:  *
1.91      kristaps  343:  *  - TERMP_TWOSPACE: when padding, make sure there are at least two
                    344:  *    space characters of padding.  Otherwise, rather break the line.
                    345:  *
1.84      kristaps  346:  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
                    347:  *    the line is overrun, and don't pad-right if it's underrun.
                    348:  *
                    349:  *  - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when
                    350:  *    overruning, instead save the position and continue at that point
                    351:  *    when the next invocation.
1.71      kristaps  352:  *
                    353:  *  In-line line breaking:
                    354:  *
                    355:  *  If TERMP_NOBREAK is specified and the line overruns the right
                    356:  *  margin, it will break and pad-right to the right margin after
                    357:  *  writing.  If maxrmargin is violated, it will break and continue
1.114     kristaps  358:  *  writing from the right-margin, which will lead to the above scenario
                    359:  *  upon exit.  Otherwise, the line will break at the right margin.
1.71      kristaps  360:  */
                    361: void
                    362: term_flushln(struct termp *p)
1.53      kristaps  363: {
1.114     kristaps  364:        int              i;     /* current input position in p->buf */
                    365:        size_t           vis;   /* current visual position on output */
                    366:        size_t           vbl;   /* number of blanks to prepend to output */
1.136     schwarze  367:        size_t           vend;  /* end of word visual position on output */
1.114     kristaps  368:        size_t           bp;    /* visual right border position */
                    369:        int              j;     /* temporary loop index */
1.140     kristaps  370:        int              jhy;   /* last hyphen before line overflow */
1.114     kristaps  371:        size_t           maxvis, mmax;
1.53      kristaps  372:
1.71      kristaps  373:        /*
                    374:         * First, establish the maximum columns of "visible" content.
                    375:         * This is usually the difference between the right-margin and
                    376:         * an indentation, but can be, for tagged lists or columns, a
1.115     kristaps  377:         * small set of values.
1.71      kristaps  378:         */
1.53      kristaps  379:
1.71      kristaps  380:        assert(p->offset < p->rmargin);
1.92      kristaps  381:
1.129     kristaps  382:        maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ?
1.119     kristaps  383:                /* LINTED */
1.129     kristaps  384:                0 : p->rmargin - p->offset - p->overstep;
                    385:        mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ?
1.119     kristaps  386:                /* LINTED */
1.129     kristaps  387:                0 : p->maxrmargin - p->offset - p->overstep;
1.92      kristaps  388:
1.71      kristaps  389:        bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
1.115     kristaps  390:
1.136     schwarze  391:        /*
                    392:         * Indent the first line of a paragraph.
                    393:         */
                    394:        vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
                    395:
1.115     kristaps  396:        /*
                    397:         * FIXME: if bp is zero, we still output the first word before
                    398:         * breaking the line.
                    399:         */
                    400:
1.136     schwarze  401:        vis = vend = i = 0;
                    402:        while (i < (int)p->col) {
1.71      kristaps  403:
                    404:                /*
1.138     schwarze  405:                 * Handle literal tab characters.
                    406:                 */
                    407:                for (j = i; j < (int)p->col; j++) {
                    408:                        if ('\t' != p->buf[j])
                    409:                                break;
                    410:                        vend = (vis/p->tabwidth+1)*p->tabwidth;
                    411:                        vbl += vend - vis;
                    412:                        vis = vend;
                    413:                }
                    414:
                    415:                /*
1.71      kristaps  416:                 * Count up visible word characters.  Control sequences
                    417:                 * (starting with the CSI) aren't counted.  A space
                    418:                 * generates a non-printing word, which is valid (the
                    419:                 * space is printed according to regular spacing rules).
                    420:                 */
                    421:
                    422:                /* LINTED */
1.140     kristaps  423:                for (jhy = 0; j < (int)p->col; j++) {
1.138     schwarze  424:                        if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
1.71      kristaps  425:                                break;
1.140     kristaps  426:                        if (8 != p->buf[j]) {
                    427:                                if (vend > vis && vend < bp &&
                    428:                                    ASCII_HYPH == p->buf[j])
                    429:                                        jhy = j;
                    430:                                vend++;
                    431:                        } else
1.136     schwarze  432:                                vend--;
1.71      kristaps  433:                }
1.53      kristaps  434:
1.71      kristaps  435:                /*
1.81      kristaps  436:                 * Find out whether we would exceed the right margin.
1.136     schwarze  437:                 * If so, break to the next line.
1.81      kristaps  438:                 */
1.140     kristaps  439:                if (vend > bp && 0 == jhy && vis > 0) {
1.136     schwarze  440:                        vend -= vis;
1.142     kristaps  441:                        endline(p);
1.81      kristaps  442:                        if (TERMP_NOBREAK & p->flags) {
1.139     schwarze  443:                                p->viscol = p->rmargin;
1.142     kristaps  444:                                advance(p, p->rmargin);
1.136     schwarze  445:                                vend += p->rmargin - p->offset;
1.81      kristaps  446:                        } else {
1.139     schwarze  447:                                p->viscol = 0;
1.136     schwarze  448:                                vbl = p->offset;
1.81      kristaps  449:                        }
1.130     kristaps  450:
1.129     kristaps  451:                        /* Remove the p->overstep width. */
1.130     kristaps  452:
1.112     kristaps  453:                        bp += (int)/* LINTED */
1.129     kristaps  454:                                p->overstep;
                    455:                        p->overstep = 0;
1.71      kristaps  456:                }
1.53      kristaps  457:
1.138     schwarze  458:                /*
                    459:                 * Skip leading tabs, they were handled above.
                    460:                 */
                    461:                while (i < (int)p->col && '\t' == p->buf[i])
                    462:                        i++;
                    463:
1.130     kristaps  464:                /* Write out the [remaining] word. */
1.136     schwarze  465:                for ( ; i < (int)p->col; i++) {
1.140     kristaps  466:                        if (vend > bp && jhy > 0 && i > jhy)
                    467:                                break;
1.138     schwarze  468:                        if ('\t' == p->buf[i])
                    469:                                break;
1.136     schwarze  470:                        if (' ' == p->buf[i]) {
                    471:                                while (' ' == p->buf[i]) {
                    472:                                        vbl++;
                    473:                                        i++;
                    474:                                }
1.71      kristaps  475:                                break;
1.136     schwarze  476:                        }
                    477:                        if (ASCII_NBRSP == p->buf[i]) {
                    478:                                vbl++;
                    479:                                continue;
                    480:                        }
1.130     kristaps  481:
1.136     schwarze  482:                        /*
                    483:                         * Now we definitely know there will be
                    484:                         * printable characters to output,
                    485:                         * so write preceding white space now.
                    486:                         */
                    487:                        if (vbl) {
1.142     kristaps  488:                                advance(p, vbl);
1.139     schwarze  489:                                p->viscol += vbl;
1.136     schwarze  490:                                vbl = 0;
                    491:                        }
1.140     kristaps  492:
                    493:                        if (ASCII_HYPH == p->buf[i])
1.142     kristaps  494:                                letter(p, '-');
1.140     kristaps  495:                        else
1.142     kristaps  496:                                letter(p, p->buf[i]);
1.140     kristaps  497:
1.139     schwarze  498:                        p->viscol += 1;
1.136     schwarze  499:                }
                    500:                vend += vbl;
                    501:                vis = vend;
1.71      kristaps  502:        }
1.111     kristaps  503:
1.91      kristaps  504:        p->col = 0;
1.129     kristaps  505:        p->overstep = 0;
1.15      kristaps  506:
1.91      kristaps  507:        if ( ! (TERMP_NOBREAK & p->flags)) {
1.139     schwarze  508:                p->viscol = 0;
1.142     kristaps  509:                endline(p);
1.15      kristaps  510:                return;
1.71      kristaps  511:        }
1.15      kristaps  512:
1.91      kristaps  513:        if (TERMP_HANG & p->flags) {
                    514:                /* We need one blank after the tag. */
1.129     kristaps  515:                p->overstep = /* LINTED */
1.92      kristaps  516:                        vis - maxvis + 1;
1.91      kristaps  517:
                    518:                /*
                    519:                 * Behave exactly the same way as groff:
1.92      kristaps  520:                 * If we have overstepped the margin, temporarily move
                    521:                 * it to the right and flag the rest of the line to be
                    522:                 * shorter.
1.91      kristaps  523:                 * If we landed right at the margin, be happy.
1.92      kristaps  524:                 * If we are one step before the margin, temporarily
                    525:                 * move it one step LEFT and flag the rest of the line
                    526:                 * to be longer.
1.91      kristaps  527:                 */
1.129     kristaps  528:                if (p->overstep >= -1) {
                    529:                        assert((int)maxvis + p->overstep >= 0);
1.92      kristaps  530:                        /* LINTED */
1.129     kristaps  531:                        maxvis += p->overstep;
1.92      kristaps  532:                } else
1.129     kristaps  533:                        p->overstep = 0;
1.91      kristaps  534:
                    535:        } else if (TERMP_DANGLE & p->flags)
                    536:                return;
1.15      kristaps  537:
1.92      kristaps  538:        /* Right-pad. */
                    539:        if (maxvis > vis + /* LINTED */
1.139     schwarze  540:                        ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
                    541:                p->viscol += maxvis - vis;
1.142     kristaps  542:                advance(p, maxvis - vis);
                    543:                vis += (maxvis - vis);
1.139     schwarze  544:        } else {        /* ...or newline break. */
1.142     kristaps  545:                endline(p);
1.139     schwarze  546:                p->viscol = p->rmargin;
1.142     kristaps  547:                advance(p, p->rmargin);
1.91      kristaps  548:        }
1.15      kristaps  549: }
                    550:
                    551:
1.71      kristaps  552: /*
                    553:  * A newline only breaks an existing line; it won't assert vertical
                    554:  * space.  All data in the output buffer is flushed prior to the newline
                    555:  * assertion.
                    556:  */
                    557: void
                    558: term_newln(struct termp *p)
1.15      kristaps  559: {
                    560:
1.71      kristaps  561:        p->flags |= TERMP_NOSPACE;
1.139     schwarze  562:        if (0 == p->col && 0 == p->viscol) {
1.71      kristaps  563:                p->flags &= ~TERMP_NOLPAD;
1.15      kristaps  564:                return;
1.16      kristaps  565:        }
1.71      kristaps  566:        term_flushln(p);
                    567:        p->flags &= ~TERMP_NOLPAD;
1.16      kristaps  568: }
                    569:
                    570:
1.71      kristaps  571: /*
                    572:  * Asserts a vertical space (a full, empty line-break between lines).
                    573:  * Note that if used twice, this will cause two blank spaces and so on.
                    574:  * All data in the output buffer is flushed prior to the newline
                    575:  * assertion.
                    576:  */
                    577: void
                    578: term_vspace(struct termp *p)
1.16      kristaps  579: {
                    580:
1.62      kristaps  581:        term_newln(p);
1.139     schwarze  582:        p->viscol = 0;
1.142     kristaps  583:        endline(p);
1.16      kristaps  584: }
                    585:
                    586:
1.71      kristaps  587: static void
1.125     kristaps  588: spec(struct termp *p, const char *word, size_t len)
1.17      kristaps  589: {
1.71      kristaps  590:        const char      *rhs;
                    591:        size_t           sz;
1.17      kristaps  592:
1.101     kristaps  593:        rhs = chars_a2ascii(p->symtab, word, len, &sz);
1.125     kristaps  594:        if (rhs)
                    595:                encode(p, rhs, sz);
1.94      kristaps  596: }
                    597:
                    598:
                    599: static void
1.125     kristaps  600: res(struct termp *p, const char *word, size_t len)
1.94      kristaps  601: {
                    602:        const char      *rhs;
                    603:        size_t           sz;
                    604:
1.101     kristaps  605:        rhs = chars_a2res(p->symtab, word, len, &sz);
1.125     kristaps  606:        if (rhs)
                    607:                encode(p, rhs, sz);
                    608: }
                    609:
                    610:
                    611: void
                    612: term_fontlast(struct termp *p)
                    613: {
                    614:        enum termfont    f;
                    615:
                    616:        f = p->fontl;
                    617:        p->fontl = p->fontq[p->fonti];
                    618:        p->fontq[p->fonti] = f;
                    619: }
                    620:
                    621:
                    622: void
                    623: term_fontrepl(struct termp *p, enum termfont f)
                    624: {
                    625:
                    626:        p->fontl = p->fontq[p->fonti];
                    627:        p->fontq[p->fonti] = f;
                    628: }
                    629:
                    630:
                    631: void
                    632: term_fontpush(struct termp *p, enum termfont f)
                    633: {
                    634:
                    635:        assert(p->fonti + 1 < 10);
                    636:        p->fontl = p->fontq[p->fonti];
                    637:        p->fontq[++p->fonti] = f;
                    638: }
                    639:
                    640:
                    641: const void *
                    642: term_fontq(struct termp *p)
                    643: {
                    644:
                    645:        return(&p->fontq[p->fonti]);
                    646: }
                    647:
                    648:
                    649: enum termfont
                    650: term_fonttop(struct termp *p)
                    651: {
                    652:
                    653:        return(p->fontq[p->fonti]);
                    654: }
                    655:
                    656:
                    657: void
                    658: term_fontpopq(struct termp *p, const void *key)
                    659: {
                    660:
                    661:        while (p->fonti >= 0 && key != &p->fontq[p->fonti])
                    662:                p->fonti--;
                    663:        assert(p->fonti >= 0);
                    664: }
1.94      kristaps  665:
1.125     kristaps  666:
                    667: void
                    668: term_fontpop(struct termp *p)
                    669: {
                    670:
                    671:        assert(p->fonti);
                    672:        p->fonti--;
1.17      kristaps  673: }
                    674:
                    675:
1.71      kristaps  676: /*
                    677:  * Handle pwords, partial words, which may be either a single word or a
                    678:  * phrase that cannot be broken down (such as a literal string).  This
                    679:  * handles word styling.
                    680:  */
1.86      kristaps  681: void
                    682: term_word(struct termp *p, const char *word)
1.65      kristaps  683: {
1.124     kristaps  684:        const char      *sv, *seq;
1.125     kristaps  685:        int              sz;
1.124     kristaps  686:        size_t           ssz;
                    687:        enum roffdeco    deco;
1.71      kristaps  688:
1.100     kristaps  689:        sv = word;
                    690:
1.123     kristaps  691:        if (word[0] && '\0' == word[1])
1.100     kristaps  692:                switch (word[0]) {
                    693:                case('.'):
                    694:                        /* FALLTHROUGH */
                    695:                case(','):
                    696:                        /* FALLTHROUGH */
                    697:                case(';'):
                    698:                        /* FALLTHROUGH */
                    699:                case(':'):
                    700:                        /* FALLTHROUGH */
                    701:                case('?'):
                    702:                        /* FALLTHROUGH */
                    703:                case('!'):
                    704:                        /* FALLTHROUGH */
                    705:                case(')'):
                    706:                        /* FALLTHROUGH */
                    707:                case(']'):
                    708:                        if ( ! (TERMP_IGNDELIM & p->flags))
                    709:                                p->flags |= TERMP_NOSPACE;
                    710:                        break;
                    711:                default:
                    712:                        break;
                    713:                }
1.65      kristaps  714:
1.133     kristaps  715:        if ( ! (TERMP_NOSPACE & p->flags)) {
1.125     kristaps  716:                bufferc(p, ' ');
1.133     kristaps  717:                if (TERMP_SENTENCE & p->flags)
                    718:                        bufferc(p, ' ');
                    719:        }
1.65      kristaps  720:
1.71      kristaps  721:        if ( ! (p->flags & TERMP_NONOSPACE))
                    722:                p->flags &= ~TERMP_NOSPACE;
1.133     kristaps  723:
                    724:        p->flags &= ~TERMP_SENTENCE;
1.65      kristaps  725:
1.125     kristaps  726:        /* FIXME: use strcspn. */
1.124     kristaps  727:
                    728:        while (*word) {
                    729:                if ('\\' != *word) {
1.125     kristaps  730:                        encode(p, word, 1);
1.124     kristaps  731:                        word++;
                    732:                        continue;
                    733:                }
                    734:
                    735:                seq = ++word;
                    736:                sz = a2roffdeco(&deco, &seq, &ssz);
                    737:
                    738:                switch (deco) {
                    739:                case (DECO_RESERVED):
1.125     kristaps  740:                        res(p, seq, ssz);
1.124     kristaps  741:                        break;
                    742:                case (DECO_SPECIAL):
1.125     kristaps  743:                        spec(p, seq, ssz);
1.124     kristaps  744:                        break;
                    745:                case (DECO_BOLD):
1.125     kristaps  746:                        term_fontrepl(p, TERMFONT_BOLD);
1.124     kristaps  747:                        break;
                    748:                case (DECO_ITALIC):
1.125     kristaps  749:                        term_fontrepl(p, TERMFONT_UNDER);
1.124     kristaps  750:                        break;
                    751:                case (DECO_ROMAN):
1.125     kristaps  752:                        term_fontrepl(p, TERMFONT_NONE);
1.124     kristaps  753:                        break;
                    754:                case (DECO_PREVIOUS):
1.125     kristaps  755:                        term_fontlast(p);
1.124     kristaps  756:                        break;
                    757:                default:
                    758:                        break;
                    759:                }
1.127     kristaps  760:
1.124     kristaps  761:                word += sz;
1.127     kristaps  762:                if (DECO_NOSPACE == deco && '\0' == *word)
                    763:                        p->flags |= TERMP_NOSPACE;
1.124     kristaps  764:        }
1.65      kristaps  765:
1.131     kristaps  766:        /*
                    767:         * Note that we don't process the pipe: the parser sees it as
                    768:         * punctuation, but we don't in terms of typography.
                    769:         */
1.100     kristaps  770:        if (sv[0] && 0 == sv[1])
                    771:                switch (sv[0]) {
                    772:                case('('):
                    773:                        /* FALLTHROUGH */
                    774:                case('['):
                    775:                        p->flags |= TERMP_NOSPACE;
                    776:                        break;
                    777:                default:
                    778:                        break;
                    779:                }
1.65      kristaps  780: }
                    781:
                    782:
1.71      kristaps  783: static void
1.125     kristaps  784: adjbuf(struct termp *p, size_t sz)
1.51      kristaps  785: {
                    786:
1.125     kristaps  787:        if (0 == p->maxcols)
                    788:                p->maxcols = 1024;
                    789:        while (sz >= p->maxcols)
                    790:                p->maxcols <<= 2;
                    791:
                    792:        p->buf = realloc(p->buf, p->maxcols);
                    793:        if (NULL == p->buf) {
                    794:                perror(NULL);
                    795:                exit(EXIT_FAILURE);
1.71      kristaps  796:        }
1.51      kristaps  797: }
                    798:
1.79      kristaps  799:
                    800: static void
1.125     kristaps  801: buffera(struct termp *p, const char *word, size_t sz)
1.79      kristaps  802: {
1.125     kristaps  803:
                    804:        if (p->col + sz >= p->maxcols)
                    805:                adjbuf(p, p->col + sz);
                    806:
1.126     kristaps  807:        memcpy(&p->buf[(int)p->col], word, sz);
1.125     kristaps  808:        p->col += sz;
                    809: }
                    810:
                    811:
                    812: static void
                    813: bufferc(struct termp *p, char c)
                    814: {
                    815:
                    816:        if (p->col + 1 >= p->maxcols)
                    817:                adjbuf(p, p->col + 1);
                    818:
1.126     kristaps  819:        p->buf[(int)p->col++] = c;
1.125     kristaps  820: }
                    821:
                    822:
                    823: static void
                    824: encode(struct termp *p, const char *word, size_t sz)
                    825: {
                    826:        enum termfont     f;
                    827:        int               i;
                    828:
                    829:        /*
                    830:         * Encode and buffer a string of characters.  If the current
                    831:         * font mode is unset, buffer directly, else encode then buffer
                    832:         * character by character.
                    833:         */
                    834:
1.142     kristaps  835:        if (TERMTYPE_PS == p->type) {
                    836:                buffera(p, word, sz);
                    837:                return;
                    838:        } else if (TERMFONT_NONE == (f = term_fonttop(p))) {
1.125     kristaps  839:                buffera(p, word, sz);
                    840:                return;
                    841:        }
                    842:
                    843:        for (i = 0; i < (int)sz; i++) {
                    844:                if ( ! isgraph((u_char)word[i])) {
                    845:                        bufferc(p, word[i]);
                    846:                        continue;
1.79      kristaps  847:                }
1.125     kristaps  848:
                    849:                if (TERMFONT_UNDER == f)
                    850:                        bufferc(p, '_');
                    851:                else
                    852:                        bufferc(p, word[i]);
                    853:
                    854:                bufferc(p, 8);
                    855:                bufferc(p, word[i]);
1.79      kristaps  856:        }
                    857: }
1.106     kristaps  858:
                    859:
1.107     kristaps  860: size_t
                    861: term_vspan(const struct roffsu *su)
1.106     kristaps  862: {
                    863:        double           r;
                    864:
1.107     kristaps  865:        switch (su->unit) {
1.106     kristaps  866:        case (SCALE_CM):
1.107     kristaps  867:                r = su->scale * 2;
1.106     kristaps  868:                break;
                    869:        case (SCALE_IN):
1.107     kristaps  870:                r = su->scale * 6;
1.106     kristaps  871:                break;
                    872:        case (SCALE_PC):
1.107     kristaps  873:                r = su->scale;
1.106     kristaps  874:                break;
                    875:        case (SCALE_PT):
1.107     kristaps  876:                r = su->scale / 8;
1.106     kristaps  877:                break;
                    878:        case (SCALE_MM):
1.107     kristaps  879:                r = su->scale / 1000;
1.106     kristaps  880:                break;
                    881:        case (SCALE_VS):
1.107     kristaps  882:                r = su->scale;
1.106     kristaps  883:                break;
                    884:        default:
1.107     kristaps  885:                r = su->scale - 1;
1.106     kristaps  886:                break;
                    887:        }
                    888:
                    889:        if (r < 0.0)
                    890:                r = 0.0;
1.107     kristaps  891:        return(/* LINTED */(size_t)
1.106     kristaps  892:                        r);
                    893: }
                    894:
                    895:
1.107     kristaps  896: size_t
                    897: term_hspan(const struct roffsu *su)
1.106     kristaps  898: {
                    899:        double           r;
                    900:
1.108     kristaps  901:        /* XXX: CM, IN, and PT are approximations. */
                    902:
1.107     kristaps  903:        switch (su->unit) {
1.106     kristaps  904:        case (SCALE_CM):
1.108     kristaps  905:                r = 4 * su->scale;
1.106     kristaps  906:                break;
                    907:        case (SCALE_IN):
1.108     kristaps  908:                /* XXX: this is an approximation. */
                    909:                r = 10 * su->scale;
1.106     kristaps  910:                break;
                    911:        case (SCALE_PC):
1.108     kristaps  912:                r = (10 * su->scale) / 6;
1.106     kristaps  913:                break;
                    914:        case (SCALE_PT):
1.108     kristaps  915:                r = (10 * su->scale) / 72;
1.106     kristaps  916:                break;
                    917:        case (SCALE_MM):
1.107     kristaps  918:                r = su->scale / 1000; /* FIXME: double-check. */
1.106     kristaps  919:                break;
                    920:        case (SCALE_VS):
1.107     kristaps  921:                r = su->scale * 2 - 1; /* FIXME: double-check. */
1.106     kristaps  922:                break;
                    923:        default:
1.107     kristaps  924:                r = su->scale;
1.106     kristaps  925:                break;
                    926:        }
                    927:
                    928:        if (r < 0.0)
                    929:                r = 0.0;
1.107     kristaps  930:        return((size_t)/* LINTED */
1.106     kristaps  931:                        r);
                    932: }
                    933:
                    934:

CVSweb