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

Annotation of mandoc/mandoc.c, Revision 1.37

1.37    ! schwarze    1: /*     $Id: mandoc.c,v 1.36 2011/01/03 22:42:37 schwarze Exp $ */
1.1       kristaps    2: /*
1.22      kristaps    3:  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.36      schwarze    4:  * Copyright (c) 2011 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:  *
1.36      schwarze   10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.1       kristaps   11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.36      schwarze   12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.1       kristaps   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.9       kristaps   18: #ifdef HAVE_CONFIG_H
                     19: #include "config.h"
1.7       kristaps   20: #endif
                     21:
1.2       kristaps   22: #include <sys/types.h>
                     23:
1.1       kristaps   24: #include <assert.h>
                     25: #include <ctype.h>
                     26: #include <stdlib.h>
1.4       kristaps   27: #include <stdio.h>
                     28: #include <string.h>
1.7       kristaps   29: #include <time.h>
1.1       kristaps   30:
1.18      kristaps   31: #include "mandoc.h"
1.1       kristaps   32: #include "libmandoc.h"
                     33:
1.37    ! schwarze   34: #define DATESIZE 32
        !            35:
1.18      kristaps   36: static int      a2time(time_t *, const char *, const char *);
1.37    ! schwarze   37: static char    *time2a(time_t);
1.7       kristaps   38:
1.1       kristaps   39: int
1.18      kristaps   40: mandoc_special(char *p)
1.1       kristaps   41: {
1.22      kristaps   42:        int              len, i;
                     43:        char             term;
1.18      kristaps   44:        char            *sv;
1.1       kristaps   45:
1.22      kristaps   46:        len = 0;
                     47:        term = '\0';
1.18      kristaps   48:        sv = p;
                     49:
1.22      kristaps   50:        assert('\\' == *p);
                     51:        p++;
1.1       kristaps   52:
1.22      kristaps   53:        switch (*p++) {
1.24      kristaps   54: #if 0
                     55:        case ('Z'):
                     56:                /* FALLTHROUGH */
                     57:        case ('X'):
                     58:                /* FALLTHROUGH */
                     59:        case ('x'):
                     60:                /* FALLTHROUGH */
                     61:        case ('S'):
                     62:                /* FALLTHROUGH */
                     63:        case ('R'):
                     64:                /* FALLTHROUGH */
                     65:        case ('N'):
                     66:                /* FALLTHROUGH */
                     67:        case ('l'):
                     68:                /* FALLTHROUGH */
                     69:        case ('L'):
                     70:                /* FALLTHROUGH */
                     71:        case ('H'):
                     72:                /* FALLTHROUGH */
                     73:        case ('h'):
                     74:                /* FALLTHROUGH */
                     75:        case ('D'):
                     76:                /* FALLTHROUGH */
                     77:        case ('C'):
                     78:                /* FALLTHROUGH */
                     79:        case ('b'):
                     80:                /* FALLTHROUGH */
                     81:        case ('B'):
                     82:                /* FALLTHROUGH */
                     83:        case ('a'):
                     84:                /* FALLTHROUGH */
                     85:        case ('A'):
                     86:                if (*p++ != '\'')
                     87:                        return(0);
                     88:                term = '\'';
                     89:                break;
                     90: #endif
1.28      kristaps   91:        case ('h'):
                     92:                /* FALLTHROUGH */
                     93:        case ('v'):
                     94:                /* FALLTHROUGH */
1.8       kristaps   95:        case ('s'):
1.22      kristaps   96:                if (ASCII_HYPH == *p)
                     97:                        *p = '-';
1.28      kristaps   98:
                     99:                i = 0;
                    100:                if ('+' == *p || '-' == *p) {
1.22      kristaps  101:                        p++;
1.28      kristaps  102:                        i = 1;
                    103:                }
1.8       kristaps  104:
1.22      kristaps  105:                switch (*p++) {
                    106:                case ('('):
                    107:                        len = 2;
                    108:                        break;
                    109:                case ('['):
                    110:                        term = ']';
                    111:                        break;
                    112:                case ('\''):
                    113:                        term = '\'';
                    114:                        break;
1.26      kristaps  115:                case ('0'):
1.28      kristaps  116:                        i = 1;
1.26      kristaps  117:                        /* FALLTHROUGH */
1.22      kristaps  118:                default:
                    119:                        len = 1;
                    120:                        p--;
                    121:                        break;
1.8       kristaps  122:                }
                    123:
1.22      kristaps  124:                if (ASCII_HYPH == *p)
                    125:                        *p = '-';
                    126:                if ('+' == *p || '-' == *p) {
1.28      kristaps  127:                        if (i)
1.22      kristaps  128:                                return(0);
                    129:                        p++;
                    130:                }
                    131:
1.33      kristaps  132:                /* Handle embedded numerical subexp or escape. */
                    133:
                    134:                if ('(' == *p) {
                    135:                        while (*p && ')' != *p)
                    136:                                if ('\\' == *p++) {
                    137:                                        i = mandoc_special(--p);
                    138:                                        if (0 == i)
                    139:                                                return(0);
                    140:                                        p += i;
                    141:                                }
                    142:
                    143:                        if (')' == *p++)
                    144:                                break;
                    145:
                    146:                        return(0);
                    147:                } else if ('\\' == *p) {
                    148:                        if (0 == (i = mandoc_special(p)))
                    149:                                return(0);
                    150:                        p += i;
                    151:                }
                    152:
1.22      kristaps  153:                break;
1.24      kristaps  154: #if 0
                    155:        case ('Y'):
                    156:                /* FALLTHROUGH */
                    157:        case ('V'):
                    158:                /* FALLTHROUGH */
                    159:        case ('$'):
                    160:                /* FALLTHROUGH */
                    161:        case ('n'):
                    162:                /* FALLTHROUGH */
1.32      kristaps  163: #endif
1.24      kristaps  164:        case ('k'):
                    165:                /* FALLTHROUGH */
                    166:        case ('M'):
                    167:                /* FALLTHROUGH */
                    168:        case ('m'):
                    169:                /* FALLTHROUGH */
1.11      kristaps  170:        case ('f'):
                    171:                /* FALLTHROUGH */
                    172:        case ('F'):
                    173:                /* FALLTHROUGH */
1.1       kristaps  174:        case ('*'):
1.22      kristaps  175:                switch (*p++) {
1.1       kristaps  176:                case ('('):
1.22      kristaps  177:                        len = 2;
                    178:                        break;
1.1       kristaps  179:                case ('['):
1.22      kristaps  180:                        term = ']';
                    181:                        break;
1.1       kristaps  182:                default:
1.22      kristaps  183:                        len = 1;
                    184:                        p--;
1.1       kristaps  185:                        break;
                    186:                }
1.22      kristaps  187:                break;
1.1       kristaps  188:        case ('('):
1.22      kristaps  189:                len = 2;
                    190:                break;
1.1       kristaps  191:        case ('['):
1.22      kristaps  192:                term = ']';
1.30      kristaps  193:                break;
                    194:        case ('z'):
                    195:                len = 1;
                    196:                if ('\\' == *p) {
1.33      kristaps  197:                        if (0 == (i = mandoc_special(p)))
                    198:                                return(0);
                    199:                        p += i;
1.30      kristaps  200:                        return(*p ? (int)(p - sv) : 0);
                    201:                }
1.1       kristaps  202:                break;
1.34      kristaps  203:        case ('o'):
                    204:                /* FALLTHROUGH */
1.31      kristaps  205:        case ('w'):
                    206:                if ('\'' == *p++) {
                    207:                        term = '\'';
                    208:                        break;
                    209:                }
                    210:                /* FALLTHROUGH */
1.1       kristaps  211:        default:
1.22      kristaps  212:                len = 1;
                    213:                p--;
                    214:                break;
1.1       kristaps  215:        }
                    216:
1.22      kristaps  217:        if (term) {
                    218:                for ( ; *p && term != *p; p++)
                    219:                        if (ASCII_HYPH == *p)
                    220:                                *p = '-';
1.24      kristaps  221:                return(*p ? (int)(p - sv) : 0);
1.22      kristaps  222:        }
1.1       kristaps  223:
1.22      kristaps  224:        for (i = 0; *p && i < len; i++, p++)
                    225:                if (ASCII_HYPH == *p)
                    226:                        *p = '-';
1.24      kristaps  227:        return(i == len ? (int)(p - sv) : 0);
1.1       kristaps  228: }
                    229:
1.4       kristaps  230:
                    231: void *
                    232: mandoc_calloc(size_t num, size_t size)
                    233: {
                    234:        void            *ptr;
                    235:
                    236:        ptr = calloc(num, size);
                    237:        if (NULL == ptr) {
1.6       kristaps  238:                perror(NULL);
1.35      kristaps  239:                exit((int)MANDOCLEVEL_SYSERR);
1.4       kristaps  240:        }
                    241:
                    242:        return(ptr);
                    243: }
                    244:
                    245:
                    246: void *
                    247: mandoc_malloc(size_t size)
                    248: {
                    249:        void            *ptr;
                    250:
                    251:        ptr = malloc(size);
                    252:        if (NULL == ptr) {
1.6       kristaps  253:                perror(NULL);
1.35      kristaps  254:                exit((int)MANDOCLEVEL_SYSERR);
1.4       kristaps  255:        }
                    256:
                    257:        return(ptr);
                    258: }
                    259:
                    260:
                    261: void *
                    262: mandoc_realloc(void *ptr, size_t size)
                    263: {
                    264:
                    265:        ptr = realloc(ptr, size);
                    266:        if (NULL == ptr) {
1.6       kristaps  267:                perror(NULL);
1.35      kristaps  268:                exit((int)MANDOCLEVEL_SYSERR);
1.4       kristaps  269:        }
                    270:
                    271:        return(ptr);
                    272: }
                    273:
                    274:
                    275: char *
                    276: mandoc_strdup(const char *ptr)
                    277: {
                    278:        char            *p;
                    279:
                    280:        p = strdup(ptr);
                    281:        if (NULL == p) {
1.6       kristaps  282:                perror(NULL);
1.35      kristaps  283:                exit((int)MANDOCLEVEL_SYSERR);
1.4       kristaps  284:        }
                    285:
                    286:        return(p);
1.36      schwarze  287: }
                    288:
                    289: /*
                    290:  * Parse a quoted or unquoted roff-style request or macro argument.
                    291:  * Return a pointer to the parsed argument, which is either the original
                    292:  * pointer or advanced by one byte in case the argument is quoted.
                    293:  * Null-terminate the argument in place.
                    294:  * Collapse pairs of quotes inside quoted arguments.
                    295:  * Advance the argument pointer to the next argument,
                    296:  * or to the null byte terminating the argument line.
                    297:  */
                    298: char *
                    299: mandoc_getarg(char **cpp, mandocmsg msg, void *data, int ln, int *pos)
                    300: {
                    301:        char     *start, *cp;
                    302:        int       quoted, pairs, white;
                    303:
                    304:        /* Quoting can only start with a new word. */
                    305:        start = *cpp;
                    306:        if ('"' == *start) {
                    307:                quoted = 1;
                    308:                start++;
                    309:        } else
                    310:                quoted = 0;
                    311:
                    312:        pairs = 0;
                    313:        white = 0;
                    314:        for (cp = start; '\0' != *cp; cp++) {
                    315:                /* Move left after quoted quotes and escaped backslashes. */
                    316:                if (pairs)
                    317:                        cp[-pairs] = cp[0];
                    318:                if ('\\' == cp[0]) {
                    319:                        if ('\\' == cp[1]) {
                    320:                                /* Poor man's copy mode. */
                    321:                                pairs++;
                    322:                                cp++;
                    323:                        } else if (0 == quoted && ' ' == cp[1])
                    324:                                /* Skip escaped blanks. */
                    325:                                cp++;
                    326:                } else if (0 == quoted) {
                    327:                        if (' ' == cp[0]) {
                    328:                                /* Unescaped blanks end unquoted args. */
                    329:                                white = 1;
                    330:                                break;
                    331:                        }
                    332:                } else if ('"' == cp[0]) {
                    333:                        if ('"' == cp[1]) {
                    334:                                /* Quoted quotes collapse. */
                    335:                                pairs++;
                    336:                                cp++;
                    337:                        } else {
                    338:                                /* Unquoted quotes end quoted args. */
                    339:                                quoted = 2;
                    340:                                break;
                    341:                        }
                    342:                }
                    343:        }
                    344:
                    345:        /* Quoted argument without a closing quote. */
                    346:        if (1 == quoted && msg)
                    347:                (*msg)(MANDOCERR_BADQUOTE, data, ln, *pos, NULL);
                    348:
                    349:        /* Null-terminate this argument and move to the next one. */
                    350:        if (pairs)
                    351:                cp[-pairs] = '\0';
                    352:        if ('\0' != *cp) {
                    353:                *cp++ = '\0';
                    354:                while (' ' == *cp)
                    355:                        cp++;
                    356:        }
                    357:        *pos += (cp - start) + (quoted ? 1 : 0);
                    358:        *cpp = cp;
                    359:
                    360:        if ('\0' == *cp && msg && (white || ' ' == cp[-1]))
                    361:                (*msg)(MANDOCERR_EOLNSPACE, data, ln, *pos, NULL);
                    362:
                    363:        return(start);
1.4       kristaps  364: }
1.7       kristaps  365:
                    366:
                    367: static int
                    368: a2time(time_t *t, const char *fmt, const char *p)
                    369: {
                    370:        struct tm        tm;
                    371:        char            *pp;
                    372:
                    373:        memset(&tm, 0, sizeof(struct tm));
                    374:
                    375:        pp = strptime(p, fmt, &tm);
                    376:        if (NULL != pp && '\0' == *pp) {
                    377:                *t = mktime(&tm);
                    378:                return(1);
                    379:        }
                    380:
                    381:        return(0);
                    382: }
                    383:
                    384:
1.37    ! schwarze  385: static char *
        !           386: time2a(time_t t)
        !           387: {
        !           388:        struct tm        tm;
        !           389:        char             buf[DATESIZE];
        !           390:        char            *p;
        !           391:        size_t           nsz, rsz;
        !           392:        int              isz;
        !           393:
        !           394:        localtime_r(&t, &tm);
        !           395:
        !           396:        p = buf;
        !           397:        rsz = DATESIZE;
        !           398:
        !           399:        if (0 == (nsz = strftime(p, rsz, "%B ", &tm)))
        !           400:                return(NULL);
        !           401:
        !           402:        p += (int)nsz;
        !           403:        rsz -= nsz;
        !           404:
        !           405:        if (-1 == (isz = snprintf(p, rsz, "%d, ", tm.tm_mday)))
        !           406:                return(NULL);
        !           407:
        !           408:        p += isz;
        !           409:        rsz -= isz;
        !           410:
        !           411:        return(strftime(p, rsz, "%Y", &tm) ? buf : NULL);
        !           412: }
        !           413:
        !           414:
        !           415: char *
        !           416: mandoc_normdate(char *in, mandocmsg msg, void *data, int ln, int pos)
1.7       kristaps  417: {
1.37    ! schwarze  418:        char            *out;
1.7       kristaps  419:        time_t           t;
                    420:
1.37    ! schwarze  421:        if (NULL == in || '\0' == *in ||
        !           422:            0 == strcmp(in, "$" "Mdocdate$")) {
        !           423:                (*msg)(MANDOCERR_NODATE, data, ln, pos, NULL);
        !           424:                time(&t);
        !           425:        }
        !           426:        else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
        !           427:            !a2time(&t, "%b %d, %Y", in) &&
        !           428:            !a2time(&t, "%Y-%m-%d", in)) {
        !           429:                (*msg)(MANDOCERR_BADDATE, data, ln, pos, NULL);
        !           430:                t = 0;
1.7       kristaps  431:        }
1.37    ! schwarze  432:        out = t ? time2a(t) : NULL;
        !           433:        return(mandoc_strdup(out ? out : in));
1.7       kristaps  434: }
                    435:
1.12      kristaps  436:
                    437: int
1.23      schwarze  438: mandoc_eos(const char *p, size_t sz, int enclosed)
1.12      kristaps  439: {
1.23      schwarze  440:        const char *q;
                    441:        int found;
1.12      kristaps  442:
1.13      kristaps  443:        if (0 == sz)
                    444:                return(0);
1.12      kristaps  445:
1.14      kristaps  446:        /*
                    447:         * End-of-sentence recognition must include situations where
                    448:         * some symbols, such as `)', allow prior EOS punctuation to
                    449:         * propogate outward.
                    450:         */
                    451:
1.23      schwarze  452:        found = 0;
1.25      kristaps  453:        for (q = p + (int)sz - 1; q >= p; q--) {
1.23      schwarze  454:                switch (*q) {
1.14      kristaps  455:                case ('\"'):
                    456:                        /* FALLTHROUGH */
                    457:                case ('\''):
1.15      kristaps  458:                        /* FALLTHROUGH */
                    459:                case (']'):
1.14      kristaps  460:                        /* FALLTHROUGH */
                    461:                case (')'):
1.23      schwarze  462:                        if (0 == found)
                    463:                                enclosed = 1;
1.14      kristaps  464:                        break;
                    465:                case ('.'):
                    466:                        /* FALLTHROUGH */
                    467:                case ('!'):
                    468:                        /* FALLTHROUGH */
                    469:                case ('?'):
1.23      schwarze  470:                        found = 1;
                    471:                        break;
1.14      kristaps  472:                default:
1.27      joerg     473:                        return(found && (!enclosed || isalnum((unsigned char)*q)));
1.14      kristaps  474:                }
1.12      kristaps  475:        }
                    476:
1.23      schwarze  477:        return(found && !enclosed);
1.16      kristaps  478: }
                    479:
                    480:
                    481: int
                    482: mandoc_hyph(const char *start, const char *c)
                    483: {
                    484:
                    485:        /*
                    486:         * Choose whether to break at a hyphenated character.  We only
                    487:         * do this if it's free-standing within a word.
                    488:         */
                    489:
                    490:        /* Skip first/last character of buffer. */
                    491:        if (c == start || '\0' == *(c + 1))
                    492:                return(0);
                    493:        /* Skip first/last character of word. */
                    494:        if ('\t' == *(c + 1) || '\t' == *(c - 1))
                    495:                return(0);
                    496:        if (' ' == *(c + 1) || ' ' == *(c - 1))
                    497:                return(0);
                    498:        /* Skip double invocations. */
                    499:        if ('-' == *(c + 1) || '-' == *(c - 1))
                    500:                return(0);
                    501:        /* Skip escapes. */
                    502:        if ('\\' == *(c - 1))
                    503:                return(0);
                    504:
                    505:        return(1);
1.12      kristaps  506: }

CVSweb