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

Annotation of mandoc/term.c, Revision 1.144

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

CVSweb