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

Annotation of mandoc/out.c, Revision 1.73

1.73    ! schwarze    1: /*     $Id: out.c,v 1.72 2018/08/18 20:18:14 schwarze Exp $ */
1.1       kristaps    2: /*
1.36      schwarze    3:  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
1.71      schwarze    4:  * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
1.1       kristaps    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
1.12      kristaps   18: #include "config.h"
                     19:
1.1       kristaps   20: #include <sys/types.h>
                     21:
1.6       kristaps   22: #include <assert.h>
1.73    ! schwarze   23: #include <ctype.h>
1.70      schwarze   24: #include <stdint.h>
1.1       kristaps   25: #include <stdlib.h>
1.6       kristaps   26: #include <string.h>
1.7       kristaps   27: #include <time.h>
1.1       kristaps   28:
1.47      schwarze   29: #include "mandoc_aux.h"
1.30      kristaps   30: #include "mandoc.h"
1.1       kristaps   31: #include "out.h"
                     32:
1.30      kristaps   33: static void    tblcalc_data(struct rofftbl *, struct roffcol *,
1.65      schwarze   34:                        const struct tbl_opts *, const struct tbl_dat *,
                     35:                        size_t);
1.30      kristaps   36: static void    tblcalc_literal(struct rofftbl *, struct roffcol *,
1.65      schwarze   37:                        const struct tbl_dat *, size_t);
1.30      kristaps   38: static void    tblcalc_number(struct rofftbl *, struct roffcol *,
1.45      schwarze   39:                        const struct tbl_opts *, const struct tbl_dat *);
1.30      kristaps   40:
1.48      schwarze   41:
                     42: /*
1.55      schwarze   43:  * Parse the *src string and store a scaling unit into *dst.
                     44:  * If the string doesn't specify the unit, use the default.
                     45:  * If no default is specified, fail.
1.64      schwarze   46:  * Return a pointer to the byte after the last byte used,
                     47:  * or NULL on total failure.
1.3       kristaps   48:  */
1.64      schwarze   49: const char *
1.5       kristaps   50: a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
1.1       kristaps   51: {
1.55      schwarze   52:        char            *endptr;
1.1       kristaps   53:
1.56      schwarze   54:        dst->unit = def == SCALE_MAX ? SCALE_BU : def;
                     55:        dst->scale = strtod(src, &endptr);
                     56:        if (endptr == src)
1.64      schwarze   57:                return NULL;
1.5       kristaps   58:
1.56      schwarze   59:        switch (*endptr++) {
1.48      schwarze   60:        case 'c':
1.56      schwarze   61:                dst->unit = SCALE_CM;
1.3       kristaps   62:                break;
1.48      schwarze   63:        case 'i':
1.56      schwarze   64:                dst->unit = SCALE_IN;
                     65:                break;
                     66:        case 'f':
                     67:                dst->unit = SCALE_FS;
                     68:                break;
                     69:        case 'M':
                     70:                dst->unit = SCALE_MM;
                     71:                break;
                     72:        case 'm':
                     73:                dst->unit = SCALE_EM;
                     74:                break;
                     75:        case 'n':
                     76:                dst->unit = SCALE_EN;
1.3       kristaps   77:                break;
1.48      schwarze   78:        case 'P':
1.56      schwarze   79:                dst->unit = SCALE_PC;
1.3       kristaps   80:                break;
1.48      schwarze   81:        case 'p':
1.56      schwarze   82:                dst->unit = SCALE_PT;
1.3       kristaps   83:                break;
1.56      schwarze   84:        case 'u':
                     85:                dst->unit = SCALE_BU;
1.3       kristaps   86:                break;
1.48      schwarze   87:        case 'v':
1.56      schwarze   88:                dst->unit = SCALE_VS;
1.3       kristaps   89:                break;
1.68      schwarze   90:        default:
1.56      schwarze   91:                endptr--;
1.5       kristaps   92:                if (SCALE_MAX == def)
1.64      schwarze   93:                        return NULL;
1.56      schwarze   94:                dst->unit = def;
1.3       kristaps   95:                break;
                     96:        }
1.64      schwarze   97:        return endptr;
1.8       kristaps   98: }
1.30      kristaps   99:
                    100: /*
                    101:  * Calculate the abstract widths and decimal positions of columns in a
                    102:  * table.  This routine allocates the columns structures then runs over
                    103:  * all rows and cells in the table.  The function pointers in "tbl" are
                    104:  * used for the actual width calculations.
                    105:  */
                    106: void
1.52      schwarze  107: tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
1.66      schwarze  108:     size_t offset, size_t rmargin)
1.30      kristaps  109: {
1.65      schwarze  110:        struct roffsu            su;
1.58      schwarze  111:        const struct tbl_opts   *opts;
1.30      kristaps  112:        const struct tbl_dat    *dp;
                    113:        struct roffcol          *col;
1.52      schwarze  114:        size_t                   ewidth, xwidth;
1.43      schwarze  115:        int                      spans;
1.58      schwarze  116:        int                      icol, maxcol, necol, nxcol, quirkcol;
1.30      kristaps  117:
                    118:        /*
                    119:         * Allocate the master column specifiers.  These will hold the
                    120:         * widths and decimal positions for all cells in the column.  It
                    121:         * must be freed and nullified by the caller.
                    122:         */
                    123:
                    124:        assert(NULL == tbl->cols);
1.48      schwarze  125:        tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
                    126:            sizeof(struct roffcol));
1.58      schwarze  127:        opts = sp->opts;
1.30      kristaps  128:
1.53      schwarze  129:        for (maxcol = -1; sp; sp = sp->next) {
1.30      kristaps  130:                if (TBL_SPAN_DATA != sp->pos)
                    131:                        continue;
1.43      schwarze  132:                spans = 1;
1.30      kristaps  133:                /*
                    134:                 * Account for the data cells in the layout, matching it
                    135:                 * to data cells in the data section.
                    136:                 */
                    137:                for (dp = sp->first; dp; dp = dp->next) {
1.43      schwarze  138:                        /* Do not used spanned cells in the calculation. */
                    139:                        if (0 < --spans)
                    140:                                continue;
                    141:                        spans = dp->spans;
                    142:                        if (1 < spans)
                    143:                                continue;
1.59      schwarze  144:                        icol = dp->layout->col;
1.70      schwarze  145:                        while (maxcol < icol)
                    146:                                tbl->cols[++maxcol].spacing = SIZE_MAX;
1.52      schwarze  147:                        col = tbl->cols + icol;
                    148:                        col->flags |= dp->layout->flags;
                    149:                        if (dp->layout->flags & TBL_CELL_WIGN)
                    150:                                continue;
1.65      schwarze  151:                        if (dp->layout->wstr != NULL &&
                    152:                            dp->layout->width == 0 &&
                    153:                            a2roffsu(dp->layout->wstr, &su, SCALE_EN)
                    154:                            != NULL)
                    155:                                dp->layout->width =
                    156:                                    (*tbl->sulen)(&su, tbl->arg);
                    157:                        if (col->width < dp->layout->width)
                    158:                                col->width = dp->layout->width;
1.70      schwarze  159:                        if (dp->layout->spacing != SIZE_MAX &&
                    160:                            (col->spacing == SIZE_MAX ||
                    161:                             col->spacing < dp->layout->spacing))
                    162:                                col->spacing = dp->layout->spacing;
1.66      schwarze  163:                        tblcalc_data(tbl, col, opts, dp,
1.67      schwarze  164:                            dp->block == 0 ? 0 :
                    165:                            dp->layout->width ? dp->layout->width :
1.69      schwarze  166:                            rmargin ? (rmargin + sp->opts->cols / 2)
                    167:                            / (sp->opts->cols + 1) : 0);
1.52      schwarze  168:                }
                    169:        }
                    170:
                    171:        /*
1.72      schwarze  172:         * Align numbers with text.
1.52      schwarze  173:         * Count columns to equalize and columns to maximize.
                    174:         * Find maximum width of the columns to equalize.
                    175:         * Find total width of the columns *not* to maximize.
                    176:         */
                    177:
                    178:        necol = nxcol = 0;
                    179:        ewidth = xwidth = 0;
                    180:        for (icol = 0; icol <= maxcol; icol++) {
                    181:                col = tbl->cols + icol;
1.72      schwarze  182:                if (col->width > col->nwidth)
                    183:                        col->decimal += (col->width - col->nwidth) / 2;
                    184:                else
                    185:                        col->width = col->nwidth;
1.70      schwarze  186:                if (col->spacing == SIZE_MAX || icol == maxcol)
                    187:                        col->spacing = 3;
1.52      schwarze  188:                if (col->flags & TBL_CELL_EQUAL) {
                    189:                        necol++;
                    190:                        if (ewidth < col->width)
                    191:                                ewidth = col->width;
                    192:                }
                    193:                if (col->flags & TBL_CELL_WMAX)
                    194:                        nxcol++;
                    195:                else
                    196:                        xwidth += col->width;
                    197:        }
                    198:
                    199:        /*
                    200:         * Equalize columns, if requested for any of them.
                    201:         * Update total width of the columns not to maximize.
                    202:         */
                    203:
                    204:        if (necol) {
                    205:                for (icol = 0; icol <= maxcol; icol++) {
                    206:                        col = tbl->cols + icol;
                    207:                        if ( ! (col->flags & TBL_CELL_EQUAL))
                    208:                                continue;
                    209:                        if (col->width == ewidth)
                    210:                                continue;
1.66      schwarze  211:                        if (nxcol && rmargin)
1.52      schwarze  212:                                xwidth += ewidth - col->width;
                    213:                        col->width = ewidth;
                    214:                }
                    215:        }
                    216:
                    217:        /*
                    218:         * If there are any columns to maximize, find the total
                    219:         * available width, deducting 3n margins between columns.
                    220:         * Distribute the available width evenly.
                    221:         */
                    222:
1.66      schwarze  223:        if (nxcol && rmargin) {
1.63      schwarze  224:                xwidth += 3*maxcol +
1.58      schwarze  225:                    (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
                    226:                     2 : !!opts->lvert + !!opts->rvert);
1.66      schwarze  227:                if (rmargin <= offset + xwidth)
1.63      schwarze  228:                        return;
1.66      schwarze  229:                xwidth = rmargin - offset - xwidth;
1.58      schwarze  230:
                    231:                /*
                    232:                 * Emulate a bug in GNU tbl width calculation that
                    233:                 * manifests itself for large numbers of x-columns.
                    234:                 * Emulating it for 5 x-columns gives identical
                    235:                 * behaviour for up to 6 x-columns.
                    236:                 */
                    237:
                    238:                if (nxcol == 5) {
                    239:                        quirkcol = xwidth % nxcol + 2;
                    240:                        if (quirkcol != 3 && quirkcol != 4)
                    241:                                quirkcol = -1;
                    242:                } else
                    243:                        quirkcol = -1;
                    244:
                    245:                necol = 0;
                    246:                ewidth = 0;
1.52      schwarze  247:                for (icol = 0; icol <= maxcol; icol++) {
                    248:                        col = tbl->cols + icol;
                    249:                        if ( ! (col->flags & TBL_CELL_WMAX))
                    250:                                continue;
1.58      schwarze  251:                        col->width = (double)xwidth * ++necol / nxcol
                    252:                            - ewidth + 0.4995;
                    253:                        if (necol == quirkcol)
                    254:                                col->width--;
                    255:                        ewidth += col->width;
1.30      kristaps  256:                }
                    257:        }
                    258: }
                    259:
                    260: static void
                    261: tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
1.65      schwarze  262:     const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
1.30      kristaps  263: {
                    264:        size_t           sz;
                    265:
                    266:        /* Branch down into data sub-types. */
                    267:
                    268:        switch (dp->layout->pos) {
1.48      schwarze  269:        case TBL_CELL_HORIZ:
                    270:        case TBL_CELL_DHORIZ:
1.30      kristaps  271:                sz = (*tbl->len)(1, tbl->arg);
                    272:                if (col->width < sz)
                    273:                        col->width = sz;
                    274:                break;
1.48      schwarze  275:        case TBL_CELL_LONG:
                    276:        case TBL_CELL_CENTRE:
                    277:        case TBL_CELL_LEFT:
                    278:        case TBL_CELL_RIGHT:
1.65      schwarze  279:                tblcalc_literal(tbl, col, dp, mw);
1.30      kristaps  280:                break;
1.48      schwarze  281:        case TBL_CELL_NUMBER:
1.45      schwarze  282:                tblcalc_number(tbl, col, opts, dp);
1.35      kristaps  283:                break;
1.48      schwarze  284:        case TBL_CELL_DOWN:
1.30      kristaps  285:                break;
                    286:        default:
                    287:                abort();
                    288:        }
                    289: }
                    290:
                    291: static void
                    292: tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
1.65      schwarze  293:     const struct tbl_dat *dp, size_t mw)
1.30      kristaps  294: {
1.65      schwarze  295:        const char      *str;   /* Beginning of the first line. */
                    296:        const char      *beg;   /* Beginning of the current line. */
                    297:        char            *end;   /* End of the current line. */
1.66      schwarze  298:        size_t           lsz;   /* Length of the current line. */
                    299:        size_t           wsz;   /* Length of the current word. */
1.65      schwarze  300:
                    301:        if (dp->string == NULL || *dp->string == '\0')
                    302:                return;
                    303:        str = mw ? mandoc_strdup(dp->string) : dp->string;
1.66      schwarze  304:        lsz = 0;
1.65      schwarze  305:        for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
                    306:                end = mw ? strchr(beg, ' ') : NULL;
                    307:                if (end != NULL) {
                    308:                        *end++ = '\0';
                    309:                        while (*end == ' ')
                    310:                                end++;
                    311:                }
1.66      schwarze  312:                wsz = (*tbl->slen)(beg, tbl->arg);
                    313:                if (mw && lsz && lsz + 1 + wsz <= mw)
                    314:                        lsz += 1 + wsz;
                    315:                else
                    316:                        lsz = wsz;
                    317:                if (col->width < lsz)
                    318:                        col->width = lsz;
1.65      schwarze  319:        }
                    320:        if (mw)
                    321:                free((void *)str);
1.30      kristaps  322: }
                    323:
                    324: static void
                    325: tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
1.45      schwarze  326:                const struct tbl_opts *opts, const struct tbl_dat *dp)
1.30      kristaps  327: {
1.73    ! schwarze  328:        const char      *cp, *lastdigit, *lastpoint;
        !           329:        size_t           intsz, totsz;
1.30      kristaps  330:        char             buf[2];
                    331:
1.73    ! schwarze  332:        if (dp->string == NULL || *dp->string == '\0')
        !           333:                return;
        !           334:
1.30      kristaps  335:        /*
1.73    ! schwarze  336:         * Find the last digit and
        !           337:         * the last decimal point that is adjacent to a digit.
        !           338:         * The alignment indicator "\&" overrides everything.
1.30      kristaps  339:         */
                    340:
1.73    ! schwarze  341:        lastdigit = lastpoint = NULL;
        !           342:        for (cp = dp->string; cp[0] != '\0'; cp++) {
        !           343:                if (cp[0] == '\\' && cp[1] == '&') {
        !           344:                        lastdigit = lastpoint = cp;
        !           345:                        break;
        !           346:                } else if (cp[0] == opts->decimal &&
        !           347:                    (isdigit((unsigned char)cp[1]) ||
        !           348:                     (cp > dp->string && isdigit((unsigned char)cp[-1]))))
        !           349:                        lastpoint = cp;
        !           350:                else if (isdigit((unsigned char)cp[0]))
        !           351:                        lastdigit = cp;
        !           352:        }
        !           353:
        !           354:        /* Not a number, treat as a literal string. */
        !           355:
        !           356:        totsz = (*tbl->slen)(dp->string, tbl->arg);
        !           357:        if (lastdigit == NULL) {
        !           358:                if (col->width < totsz)
        !           359:                        col->width = totsz;
        !           360:                return;
        !           361:        }
1.30      kristaps  362:
1.73    ! schwarze  363:        /* Measure the width of the integer part. */
1.30      kristaps  364:
1.73    ! schwarze  365:        if (lastpoint == NULL)
        !           366:                lastpoint = lastdigit + 1;
        !           367:        intsz = 0;
1.30      kristaps  368:        buf[1] = '\0';
1.73    ! schwarze  369:        for (cp = dp->string; cp < lastpoint; cp++) {
        !           370:                buf[0] = cp[0];
        !           371:                intsz += (*tbl->slen)(buf, tbl->arg);
        !           372:        }
        !           373:
        !           374:        /*
        !           375:          * If this number has more integer digits than all numbers
        !           376:          * seen on earlier lines, shift them all to the right.
        !           377:         * If it has fewer, shift this number to the right.
        !           378:         */
1.30      kristaps  379:
1.73    ! schwarze  380:        if (intsz > col->decimal) {
        !           381:                col->nwidth += intsz - col->decimal;
        !           382:                col->decimal = intsz;
1.30      kristaps  383:        } else
1.73    ! schwarze  384:                totsz += col->decimal - intsz;
1.30      kristaps  385:
1.73    ! schwarze  386:        /* Update the maximum total width seen so far. */
1.30      kristaps  387:
1.73    ! schwarze  388:        if (totsz > col->nwidth)
        !           389:                col->nwidth = totsz;
1.30      kristaps  390: }

CVSweb