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

Annotation of mandoc/mandoc.c, Revision 1.121

1.121   ! schwarze    1: /* $Id: mandoc.c,v 1.120 2022/04/13 13:19:34 schwarze Exp $ */
1.1       kristaps    2: /*
1.121   ! schwarze    3:  * Copyright (c) 2010, 2011, 2015, 2017, 2018, 2019, 2020, 2021
        !             4:  *               Ingo Schwarze <schwarze@openbsd.org>
        !             5:  * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.1       kristaps    6:  *
                      7:  * Permission to use, copy, modify, and distribute this software for any
                      8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
                     10:  *
1.36      schwarze   11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1       kristaps   12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.36      schwarze   13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1       kristaps   14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.121   ! schwarze   18:  *
        !            19:  * Utility functions to handle end of sentence punctuation
        !            20:  * and dates and times, for use by mdoc(7) and man(7) parsers.
        !            21:  * Utility functions to handle fonts and numbers,
        !            22:  * for use by mandoc(1) parsers and formatters.
1.1       kristaps   23:  */
1.9       kristaps   24: #include "config.h"
1.7       kristaps   25:
1.2       kristaps   26: #include <sys/types.h>
                     27:
1.1       kristaps   28: #include <assert.h>
                     29: #include <ctype.h>
1.50      kristaps   30: #include <errno.h>
                     31: #include <limits.h>
1.1       kristaps   32: #include <stdlib.h>
1.4       kristaps   33: #include <stdio.h>
                     34: #include <string.h>
1.7       kristaps   35: #include <time.h>
1.1       kristaps   36:
1.101     schwarze   37: #include "mandoc_aux.h"
1.18      kristaps   38: #include "mandoc.h"
1.101     schwarze   39: #include "roff.h"
1.1       kristaps   40: #include "libmandoc.h"
1.114     schwarze   41: #include "roff_int.h"
1.37      schwarze   42:
1.18      kristaps   43: static int      a2time(time_t *, const char *, const char *);
1.37      schwarze   44: static char    *time2a(time_t);
1.7       kristaps   45:
1.45      kristaps   46:
                     47: enum mandoc_esc
1.112     schwarze   48: mandoc_font(const char *cp, int sz)
                     49: {
                     50:        switch (sz) {
                     51:        case 0:
                     52:                return ESCAPE_FONTPREV;
                     53:        case 1:
                     54:                switch (cp[0]) {
                     55:                case 'B':
                     56:                case '3':
                     57:                        return ESCAPE_FONTBOLD;
                     58:                case 'I':
                     59:                case '2':
                     60:                        return ESCAPE_FONTITALIC;
                     61:                case 'P':
                     62:                        return ESCAPE_FONTPREV;
                     63:                case 'R':
                     64:                case '1':
                     65:                        return ESCAPE_FONTROMAN;
                     66:                case '4':
                     67:                        return ESCAPE_FONTBI;
                     68:                default:
                     69:                        return ESCAPE_ERROR;
                     70:                }
                     71:        case 2:
                     72:                switch (cp[0]) {
                     73:                case 'B':
                     74:                        switch (cp[1]) {
                     75:                        case 'I':
                     76:                                return ESCAPE_FONTBI;
                     77:                        default:
                     78:                                return ESCAPE_ERROR;
                     79:                        }
                     80:                case 'C':
                     81:                        switch (cp[1]) {
                     82:                        case 'B':
1.119     schwarze   83:                                return ESCAPE_FONTCB;
1.112     schwarze   84:                        case 'I':
1.119     schwarze   85:                                return ESCAPE_FONTCI;
1.112     schwarze   86:                        case 'R':
                     87:                        case 'W':
1.119     schwarze   88:                                return ESCAPE_FONTCR;
1.112     schwarze   89:                        default:
                     90:                                return ESCAPE_ERROR;
                     91:                        }
                     92:                default:
                     93:                        return ESCAPE_ERROR;
                     94:                }
                     95:        default:
                     96:                return ESCAPE_ERROR;
                     97:        }
1.4       kristaps   98: }
1.7       kristaps   99:
                    100: static int
                    101: a2time(time_t *t, const char *fmt, const char *p)
                    102: {
                    103:        struct tm        tm;
                    104:        char            *pp;
                    105:
                    106:        memset(&tm, 0, sizeof(struct tm));
                    107:
1.56      kristaps  108:        pp = NULL;
1.85      schwarze  109: #if HAVE_STRPTIME
1.7       kristaps  110:        pp = strptime(p, fmt, &tm);
1.56      kristaps  111: #endif
1.7       kristaps  112:        if (NULL != pp && '\0' == *pp) {
                    113:                *t = mktime(&tm);
1.94      schwarze  114:                return 1;
1.7       kristaps  115:        }
                    116:
1.94      schwarze  117:        return 0;
1.7       kristaps  118: }
                    119:
1.37      schwarze  120: static char *
                    121: time2a(time_t t)
                    122: {
1.56      kristaps  123:        struct tm       *tm;
1.38      schwarze  124:        char            *buf, *p;
                    125:        size_t           ssz;
1.37      schwarze  126:        int              isz;
                    127:
1.116     schwarze  128:        buf = NULL;
1.56      kristaps  129:        tm = localtime(&t);
1.89      schwarze  130:        if (tm == NULL)
1.116     schwarze  131:                goto fail;
1.37      schwarze  132:
1.38      schwarze  133:        /*
                    134:         * Reserve space:
                    135:         * up to 9 characters for the month (September) + blank
                    136:         * up to 2 characters for the day + comma + blank
                    137:         * 4 characters for the year and a terminating '\0'
                    138:         */
1.98      schwarze  139:
1.38      schwarze  140:        p = buf = mandoc_malloc(10 + 4 + 4 + 1);
                    141:
1.98      schwarze  142:        if ((ssz = strftime(p, 10 + 1, "%B ", tm)) == 0)
1.38      schwarze  143:                goto fail;
                    144:        p += (int)ssz;
1.37      schwarze  145:
1.98      schwarze  146:        /*
                    147:         * The output format is just "%d" here, not "%2d" or "%02d".
                    148:         * That's also the reason why we can't just format the
                    149:         * date as a whole with "%B %e, %Y" or "%B %d, %Y".
                    150:         * Besides, the present approach is less prone to buffer
                    151:         * overflows, in case anybody should ever introduce the bug
                    152:         * of looking at LC_TIME.
                    153:         */
                    154:
1.116     schwarze  155:        isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday);
                    156:        if (isz < 0 || isz > 4)
1.38      schwarze  157:                goto fail;
1.37      schwarze  158:        p += isz;
                    159:
1.98      schwarze  160:        if (strftime(p, 4 + 1, "%Y", tm) == 0)
1.38      schwarze  161:                goto fail;
1.94      schwarze  162:        return buf;
1.38      schwarze  163:
                    164: fail:
                    165:        free(buf);
1.116     schwarze  166:        return mandoc_strdup("");
1.37      schwarze  167: }
                    168:
                    169: char *
1.117     schwarze  170: mandoc_normdate(struct roff_node *nch, struct roff_node *nbl)
1.7       kristaps  171: {
1.103     schwarze  172:        char            *cp;
1.7       kristaps  173:        time_t           t;
1.116     schwarze  174:
1.117     schwarze  175:        /* No date specified. */
1.7       kristaps  176:
1.117     schwarze  177:        if (nch == NULL) {
                    178:                if (nbl == NULL)
                    179:                        mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL);
                    180:                else
                    181:                        mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line,
                    182:                            nbl->pos, "%s", roff_name[nbl->tok]);
                    183:                return mandoc_strdup("");
                    184:        }
                    185:        if (*nch->string == '\0') {
                    186:                mandoc_msg(MANDOCERR_DATE_MISSING, nch->line,
                    187:                    nch->pos, "%s", roff_name[nbl->tok]);
                    188:                return mandoc_strdup("");
                    189:        }
                    190:        if (strcmp(nch->string, "$" "Mdocdate$") == 0)
1.98      schwarze  191:                return time2a(time(NULL));
                    192:
                    193:        /* Valid mdoc(7) date format. */
                    194:
1.117     schwarze  195:        if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) ||
                    196:            a2time(&t, "%b %d, %Y", nch->string)) {
1.103     schwarze  197:                cp = time2a(t);
                    198:                if (t > time(NULL) + 86400)
1.117     schwarze  199:                        mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line,
                    200:                            nch->pos, "%s %s", roff_name[nbl->tok], cp);
                    201:                else if (*nch->string != '$' &&
                    202:                    strcmp(nch->string, cp) != 0)
                    203:                        mandoc_msg(MANDOCERR_DATE_NORM, nch->line,
                    204:                            nch->pos, "%s %s", roff_name[nbl->tok], cp);
1.103     schwarze  205:                return cp;
                    206:        }
1.98      schwarze  207:
1.101     schwarze  208:        /* In man(7), do not warn about the legacy format. */
1.98      schwarze  209:
1.117     schwarze  210:        if (a2time(&t, "%Y-%m-%d", nch->string) == 0)
                    211:                mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos,
                    212:                    "%s %s", roff_name[nbl->tok], nch->string);
1.103     schwarze  213:        else if (t > time(NULL) + 86400)
1.117     schwarze  214:                mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos,
                    215:                    "%s %s", roff_name[nbl->tok], nch->string);
                    216:        else if (nbl->tok == MDOC_Dd)
                    217:                mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos,
                    218:                    "Dd %s", nch->string);
1.98      schwarze  219:
                    220:        /* Use any non-mdoc(7) date verbatim. */
                    221:
1.117     schwarze  222:        return mandoc_strdup(nch->string);
1.7       kristaps  223: }
                    224:
1.12      kristaps  225: int
1.75      schwarze  226: mandoc_eos(const char *p, size_t sz)
1.12      kristaps  227: {
1.75      schwarze  228:        const char      *q;
                    229:        int              enclosed, found;
1.12      kristaps  230:
1.13      kristaps  231:        if (0 == sz)
1.94      schwarze  232:                return 0;
1.12      kristaps  233:
1.14      kristaps  234:        /*
                    235:         * End-of-sentence recognition must include situations where
                    236:         * some symbols, such as `)', allow prior EOS punctuation to
1.49      kristaps  237:         * propagate outward.
1.14      kristaps  238:         */
                    239:
1.75      schwarze  240:        enclosed = found = 0;
1.25      kristaps  241:        for (q = p + (int)sz - 1; q >= p; q--) {
1.23      schwarze  242:                switch (*q) {
1.79      schwarze  243:                case '\"':
                    244:                case '\'':
                    245:                case ']':
                    246:                case ')':
1.23      schwarze  247:                        if (0 == found)
                    248:                                enclosed = 1;
1.14      kristaps  249:                        break;
1.79      schwarze  250:                case '.':
                    251:                case '!':
                    252:                case '?':
1.23      schwarze  253:                        found = 1;
                    254:                        break;
1.14      kristaps  255:                default:
1.94      schwarze  256:                        return found &&
                    257:                            (!enclosed || isalnum((unsigned char)*q));
1.14      kristaps  258:                }
1.12      kristaps  259:        }
                    260:
1.94      schwarze  261:        return found && !enclosed;
1.44      kristaps  262: }
1.50      kristaps  263:
                    264: /*
                    265:  * Convert a string to a long that may not be <0.
                    266:  * If the string is invalid, or is less than 0, return -1.
                    267:  */
                    268: int
1.54      kristaps  269: mandoc_strntoi(const char *p, size_t sz, int base)
1.50      kristaps  270: {
                    271:        char             buf[32];
                    272:        char            *ep;
                    273:        long             v;
                    274:
                    275:        if (sz > 31)
1.94      schwarze  276:                return -1;
1.50      kristaps  277:
                    278:        memcpy(buf, p, sz);
1.51      kristaps  279:        buf[(int)sz] = '\0';
1.50      kristaps  280:
                    281:        errno = 0;
                    282:        v = strtol(buf, &ep, base);
                    283:
                    284:        if (buf[0] == '\0' || *ep != '\0')
1.94      schwarze  285:                return -1;
1.50      kristaps  286:
1.54      kristaps  287:        if (v > INT_MAX)
                    288:                v = INT_MAX;
                    289:        if (v < INT_MIN)
                    290:                v = INT_MIN;
1.50      kristaps  291:
1.94      schwarze  292:        return (int)v;
1.50      kristaps  293: }

CVSweb