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

Annotation of mandoc/validate.c, Revision 1.57

1.57    ! kristaps    1: /* $Id: validate.c,v 1.56 2009/02/24 13:46:54 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19: #include <assert.h>
1.56      kristaps   20: #include <ctype.h>
1.8       kristaps   21: #include <stdlib.h>
1.1       kristaps   22:
                     23: #include "private.h"
                     24:
1.44      kristaps   25: /*
                     26:  * Pre- and post-validate macros as they're parsed.  Pre-validation
                     27:  * occurs when the macro has been detected and its arguments parsed.
                     28:  * Post-validation occurs when all child macros have also been parsed.
                     29:  * In the ELEMENT case, this is simply the parameters of the macro; in
                     30:  * the BLOCK case, this is the HEAD, BODY, TAIL and so on.
                     31:  */
                     32:
1.55      kristaps   33: #define        PRE_ARGS        struct mdoc *mdoc, const struct mdoc_node *n
                     34: #define        POST_ARGS       struct mdoc *mdoc
                     35:
                     36: typedef        int     (*v_pre)(PRE_ARGS);
                     37: typedef        int     (*v_post)(POST_ARGS);
1.14      kristaps   38:
1.36      kristaps   39: /* FIXME: some sections should only occur in specific msecs. */
                     40: /* FIXME: ignoring Pp. */
                     41: /* FIXME: math symbols. */
                     42:
1.14      kristaps   43: struct valids {
1.24      kristaps   44:        v_pre   *pre;
1.17      kristaps   45:        v_post  *post;
1.14      kristaps   46: };
1.1       kristaps   47:
1.37      kristaps   48: /* Utility checks. */
                     49:
1.55      kristaps   50: static int     check_parent(PRE_ARGS, int, enum mdoc_type);
                     51: static int     check_msec(PRE_ARGS, int, enum mdoc_msec *);
                     52: static int     check_stdarg(PRE_ARGS);
                     53:
                     54: static int     check_text(struct mdoc *,
                     55:                        size_t, size_t, const char *);
                     56:
1.51      kristaps   57: static int     err_child_lt(struct mdoc *, const char *, int);
                     58: static int     err_child_gt(struct mdoc *, const char *, int);
                     59: static int     warn_child_gt(struct mdoc *, const char *, int);
                     60: static int     err_child_eq(struct mdoc *, const char *, int);
                     61: static int     warn_child_eq(struct mdoc *, const char *, int);
                     62:
                     63: /* Utility auxiliaries. */
                     64:
                     65: static inline int count_child(struct mdoc *);
                     66: static inline int warn_count(struct mdoc *, const char *,
                     67:                        int, const char *, int);
                     68: static inline int err_count(struct mdoc *, const char *,
                     69:                        int, const char *, int);
1.11      kristaps   70:
1.37      kristaps   71: /* Specific pre-child-parse routines. */
                     72:
1.55      kristaps   73: static int     pre_display(PRE_ARGS);
                     74: static int     pre_sh(PRE_ARGS);
                     75: static int     pre_ss(PRE_ARGS);
                     76: static int     pre_bd(PRE_ARGS);
                     77: static int     pre_bl(PRE_ARGS);
                     78: static int     pre_it(PRE_ARGS);
                     79: static int     pre_cd(PRE_ARGS);
                     80: static int     pre_er(PRE_ARGS);
                     81: static int     pre_ex(PRE_ARGS);
                     82: static int     pre_rv(PRE_ARGS);
                     83: static int     pre_an(PRE_ARGS);
                     84: static int     pre_st(PRE_ARGS);
                     85: static int     pre_prologue(PRE_ARGS);
                     86: static int     pre_prologue(PRE_ARGS);
                     87: static int     pre_prologue(PRE_ARGS);
1.24      kristaps   88:
1.37      kristaps   89: /* Specific post-child-parse routines. */
                     90:
1.55      kristaps   91: static int     herr_ge1(POST_ARGS);
                     92: static int     herr_le1(POST_ARGS);
                     93: static int     herr_eq0(POST_ARGS);
                     94: static int     eerr_eq0(POST_ARGS);
                     95: static int     eerr_le1(POST_ARGS);
                     96: static int     eerr_le2(POST_ARGS);
                     97: static int     eerr_eq1(POST_ARGS);
                     98: static int     eerr_ge1(POST_ARGS);
                     99: static int     ewarn_eq0(POST_ARGS);
                    100: static int     ewarn_eq1(POST_ARGS);
                    101: static int     bwarn_ge1(POST_ARGS);
                    102: static int     ewarn_ge1(POST_ARGS);
                    103: static int     ebool(POST_ARGS);
                    104:
                    105: static int     post_sh(POST_ARGS);
                    106: static int     post_sh_body(POST_ARGS);
                    107: static int     post_sh_head(POST_ARGS);
1.57    ! kristaps  108: static int     post_fd(POST_ARGS);
1.55      kristaps  109: static int     post_bl(POST_ARGS);
                    110: static int     post_it(POST_ARGS);
                    111: static int     post_ex(POST_ARGS);
                    112: static int     post_an(POST_ARGS);
                    113: static int     post_at(POST_ARGS);
                    114: static int     post_xr(POST_ARGS);
                    115: static int     post_nm(POST_ARGS);
                    116: static int     post_bf(POST_ARGS);
                    117: static int     post_root(POST_ARGS);
1.37      kristaps  118:
                    119: /* Collections of pre-child-parse routines. */
1.17      kristaps  120:
1.24      kristaps  121: static v_pre   pres_prologue[] = { pre_prologue, NULL };
                    122: static v_pre   pres_d1[] = { pre_display, NULL };
                    123: static v_pre   pres_bd[] = { pre_display, pre_bd, NULL };
                    124: static v_pre   pres_bl[] = { pre_bl, NULL };
1.25      kristaps  125: static v_pre   pres_it[] = { pre_it, NULL };
1.33      kristaps  126: static v_pre   pres_ss[] = { pre_ss, NULL };
                    127: static v_pre   pres_sh[] = { pre_sh, NULL };
                    128: static v_pre   pres_cd[] = { pre_cd, NULL };
                    129: static v_pre   pres_er[] = { pre_er, NULL };
                    130: static v_pre   pres_ex[] = { pre_ex, NULL };
1.36      kristaps  131: static v_pre   pres_rv[] = { pre_rv, NULL };
1.35      kristaps  132: static v_pre   pres_an[] = { pre_an, NULL };
1.36      kristaps  133: static v_pre   pres_st[] = { pre_st, NULL };
1.24      kristaps  134:
1.37      kristaps  135: /* Collections of post-child-parse routines. */
                    136:
                    137: static v_post  posts_bool[] = { eerr_eq1, ebool, NULL };
                    138: static v_post  posts_bd[] = { herr_eq0, bwarn_ge1, NULL };
                    139: static v_post  posts_text[] = { eerr_ge1, NULL };
                    140: static v_post  posts_wtext[] = { ewarn_ge1, NULL };
                    141: static v_post  posts_notext[] = { eerr_eq0, NULL };
1.43      kristaps  142: static v_post  posts_wline[] = { bwarn_ge1, herr_eq0, NULL };
1.37      kristaps  143: static v_post  posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL };
                    144: static v_post  posts_bl[] = { herr_eq0, bwarn_ge1, post_bl, NULL };
1.25      kristaps  145: static v_post  posts_it[] = { post_it, NULL };
1.39      kristaps  146: static v_post  posts_in[] = { ewarn_eq1, NULL };
1.37      kristaps  147: static v_post  posts_ss[] = { herr_ge1, NULL };
1.52      kristaps  148: static v_post  posts_pf[] = { eerr_eq1, NULL };
1.37      kristaps  149: static v_post  posts_pp[] = { ewarn_eq0, NULL };
                    150: static v_post  posts_ex[] = { eerr_le1, post_ex, NULL };
1.35      kristaps  151: static v_post  posts_an[] = { post_an, NULL };
1.37      kristaps  152: static v_post  posts_at[] = { post_at, NULL };
                    153: static v_post  posts_xr[] = { eerr_ge1, eerr_le2, post_xr, NULL };
                    154: static v_post  posts_nm[] = { post_nm, NULL };
1.41      kristaps  155: static v_post  posts_bf[] = { herr_le1, post_bf, NULL };
1.45      kristaps  156: static v_post  posts_rs[] = { herr_eq0, bwarn_ge1, NULL };
                    157: static v_post  posts_fo[] = { bwarn_ge1, NULL };
                    158: static v_post  posts_bk[] = { herr_eq0, bwarn_ge1, NULL };
1.57    ! kristaps  159: static v_post  posts_fd[] = { ewarn_ge1, post_fd, NULL };
1.9       kristaps  160:
1.37      kristaps  161: /* Per-macro pre- and post-child-check routine collections. */
1.12      kristaps  162:
1.9       kristaps  163: const  struct valids mdoc_valids[MDOC_MAX] = {
1.51      kristaps  164:        { NULL, NULL },                         /* \" */
                    165:        { pres_prologue, posts_text },          /* Dd */
                    166:        { pres_prologue, NULL },                /* Dt */
                    167:        { pres_prologue, NULL },                /* Os */
                    168:        { pres_sh, posts_sh },                  /* Sh */
                    169:        { pres_ss, posts_ss },                  /* Ss */
                    170:        { NULL, posts_pp },                     /* Pp */
                    171:        { pres_d1, posts_wline },               /* D1 */
                    172:        { pres_d1, posts_wline },               /* Dl */
                    173:        { pres_bd, posts_bd },                  /* Bd */
                    174:        { NULL, NULL },                         /* Ed */
                    175:        { pres_bl, posts_bl },                  /* Bl */
                    176:        { NULL, NULL },                         /* El */
                    177:        { pres_it, posts_it },                  /* It */
                    178:        { NULL, posts_text },                   /* Ad */
                    179:        { pres_an, posts_an },                  /* An */
                    180:        { NULL, NULL },                         /* Ar */
                    181:        { pres_cd, posts_text },                /* Cd */
                    182:        { NULL, NULL },                         /* Cm */
                    183:        { NULL, posts_text },                   /* Dv */
                    184:        { pres_er, posts_text },                /* Er */
                    185:        { NULL, posts_text },                   /* Ev */
                    186:        { pres_ex, posts_ex },                  /* Ex */
                    187:        { NULL, posts_text },                   /* Fa */
1.57    ! kristaps  188:        { NULL, posts_fd },                     /* Fd */
1.51      kristaps  189:        { NULL, NULL },                         /* Fl */
                    190:        { NULL, posts_text },                   /* Fn */
                    191:        { NULL, posts_wtext },                  /* Ft */
                    192:        { NULL, posts_text },                   /* Ic */
                    193:        { NULL, posts_in },                     /* In */
                    194:        { NULL, posts_text },                   /* Li */
                    195:        { NULL, posts_wtext },                  /* Nd */
                    196:        { NULL, posts_nm },                     /* Nm */
                    197:        { NULL, posts_wline },                  /* Op */
                    198:        { NULL, NULL },                         /* Ot */
                    199:        { NULL, NULL },                         /* Pa */
                    200:        { pres_rv, posts_notext },              /* Rv */
                    201:        { pres_st, posts_notext },              /* St */
                    202:        { NULL, posts_text },                   /* Va */
                    203:        { NULL, posts_text },                   /* Vt */
                    204:        { NULL, posts_xr },                     /* Xr */
                    205:        { NULL, posts_text },                   /* %A */
                    206:        { NULL, posts_text },                   /* %B */
                    207:        { NULL, posts_text },                   /* %D */
                    208:        { NULL, posts_text },                   /* %I */
                    209:        { NULL, posts_text },                   /* %J */
                    210:        { NULL, posts_text },                   /* %N */
                    211:        { NULL, posts_text },                   /* %O */
                    212:        { NULL, posts_text },                   /* %P */
                    213:        { NULL, posts_text },                   /* %R */
                    214:        { NULL, posts_text },                   /* %T */
                    215:        { NULL, posts_text },                   /* %V */
                    216:        { NULL, NULL },                         /* Ac */
                    217:        { NULL, NULL },                         /* Ao */
                    218:        { NULL, posts_wline },                  /* Aq */
                    219:        { NULL, posts_at },                     /* At */
                    220:        { NULL, NULL },                         /* Bc */
                    221:        { NULL, posts_bf },                     /* Bf */
                    222:        { NULL, NULL },                         /* Bo */
                    223:        { NULL, posts_wline },                  /* Bq */
                    224:        { NULL, NULL },                         /* Bsx */
                    225:        { NULL, NULL },                         /* Bx */
                    226:        { NULL, posts_bool },                   /* Db */
                    227:        { NULL, NULL },                         /* Dc */
                    228:        { NULL, NULL },                         /* Do */
                    229:        { NULL, posts_wline },                  /* Dq */
                    230:        { NULL, NULL },                         /* Ec */
                    231:        { NULL, NULL },                         /* Ef */
                    232:        { NULL, posts_text },                   /* Em */
                    233:        { NULL, NULL },                         /* Eo */
                    234:        { NULL, NULL },                         /* Fx */
                    235:        { NULL, posts_text },                   /* Ms */
                    236:        { NULL, posts_notext },                 /* No */
                    237:        { NULL, posts_notext },                 /* Ns */
                    238:        { NULL, NULL },                         /* Nx */
                    239:        { NULL, NULL },                         /* Ox */
                    240:        { NULL, NULL },                         /* Pc */
1.52      kristaps  241:        { NULL, posts_pf },                     /* Pf */
1.51      kristaps  242:        { NULL, NULL },                         /* Po */
                    243:        { NULL, posts_wline },                  /* Pq */
                    244:        { NULL, NULL },                         /* Qc */
                    245:        { NULL, posts_wline },                  /* Ql */
                    246:        { NULL, NULL },                         /* Qo */
                    247:        { NULL, posts_wline },                  /* Qq */
                    248:        { NULL, NULL },                         /* Re */
                    249:        { NULL, posts_rs },                     /* Rs */
                    250:        { NULL, NULL },                         /* Sc */
                    251:        { NULL, NULL },                         /* So */
                    252:        { NULL, posts_wline },                  /* Sq */
                    253:        { NULL, posts_bool },                   /* Sm */
                    254:        { NULL, posts_text },                   /* Sx */
                    255:        { NULL, posts_text },                   /* Sy */
                    256:        { NULL, posts_text },                   /* Tn */
                    257:        { NULL, NULL },                         /* Ux */
                    258:        { NULL, NULL },                         /* Xc */
                    259:        { NULL, NULL },                         /* Xo */
                    260:        { NULL, posts_fo },                     /* Fo */
                    261:        { NULL, NULL },                         /* Fc */
                    262:        { NULL, NULL },                         /* Oo */
                    263:        { NULL, NULL },                         /* Oc */
                    264:        { NULL, posts_bk },                     /* Bk */
                    265:        { NULL, NULL },                         /* Ek */
                    266:        { NULL, posts_notext },                 /* Bt */
                    267:        { NULL, NULL },                         /* Hf */
                    268:        { NULL, NULL },                         /* Fr */
                    269:        { NULL, posts_notext },                 /* Ud */
1.9       kristaps  270: };
1.6       kristaps  271:
                    272:
1.57    ! kristaps  273: int
        !           274: mdoc_valid_pre(struct mdoc *mdoc,
        !           275:                const struct mdoc_node *node)
        !           276: {
        !           277:        v_pre           *p;
        !           278:        struct mdoc_arg *argv;
        !           279:        size_t           argc, i, j, line, pos;
        !           280:        const char      *tp;
        !           281:
        !           282:        if (MDOC_TEXT == node->type) {
        !           283:                tp = node->data.text.string;
        !           284:                line = node->line;
        !           285:                pos = node->pos;
        !           286:                return(check_text(mdoc, line, pos, tp));
        !           287:        }
        !           288:
        !           289:        if (MDOC_BLOCK == node->type || MDOC_ELEM == node->type) {
        !           290:                argv = MDOC_BLOCK == node->type ?
        !           291:                        node->data.block.argv :
        !           292:                        node->data.elem.argv;
        !           293:                argc = MDOC_BLOCK == node->type ?
        !           294:                        node->data.block.argc :
        !           295:                        node->data.elem.argc;
        !           296:
        !           297:                for (i = 0; i < argc; i++) {
        !           298:                        if (0 == argv[i].sz)
        !           299:                                continue;
        !           300:                        for (j = 0; j < argv[i].sz; j++) {
        !           301:                                tp = argv[i].value[j];
        !           302:                                line = argv[i].line;
        !           303:                                pos = argv[i].pos;
        !           304:                                if ( ! check_text(mdoc, line, pos, tp))
        !           305:                                        return(0);
        !           306:                        }
        !           307:                }
        !           308:        }
        !           309:
        !           310:        if (NULL == mdoc_valids[node->tok].pre)
        !           311:                return(1);
        !           312:        for (p = mdoc_valids[node->tok].pre; *p; p++)
        !           313:                if ( ! (*p)(mdoc, node))
        !           314:                        return(0);
        !           315:        return(1);
        !           316: }
        !           317:
        !           318:
        !           319: int
        !           320: mdoc_valid_post(struct mdoc *mdoc)
        !           321: {
        !           322:        v_post          *p;
        !           323:
        !           324:        /*
        !           325:         * This check occurs after the macro's children have been filled
        !           326:         * in: postfix validation.  Since this happens when we're
        !           327:         * rewinding the scope tree, it's possible to have multiple
        !           328:         * invocations (as by design, for now), we set bit MDOC_VALID to
        !           329:         * indicate that we've validated.
        !           330:         */
        !           331:
        !           332:        if (MDOC_VALID & mdoc->last->flags)
        !           333:                return(1);
        !           334:        mdoc->last->flags |= MDOC_VALID;
        !           335:
        !           336:        if (MDOC_TEXT == mdoc->last->type)
        !           337:                return(1);
        !           338:        if (MDOC_ROOT == mdoc->last->type)
        !           339:                return(post_root(mdoc));
        !           340:
        !           341:        if (NULL == mdoc_valids[mdoc->last->tok].post)
        !           342:                return(1);
        !           343:        for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
        !           344:                if ( ! (*p)(mdoc))
        !           345:                        return(0);
        !           346:
        !           347:        return(1);
        !           348: }
        !           349:
        !           350:
        !           351:
1.51      kristaps  352: static inline int
                    353: warn_count(struct mdoc *m, const char *k,
                    354:                int want, const char *v, int has)
                    355: {
                    356:
1.55      kristaps  357:        return(mdoc_warn(m, WARN_SYNTAX,
                    358:                                "suggests %s %d %s (has %d)",
                    359:                                v, want, k, has));
1.51      kristaps  360: }
                    361:
                    362:
                    363: static inline int
                    364: err_count(struct mdoc *m, const char *k,
                    365:                int want, const char *v, int has)
                    366: {
                    367:
                    368:        return(mdoc_err(m, "requires %s %d %s (has %d)",
                    369:                                v, want, k, has));
                    370: }
                    371:
                    372:
                    373: static inline int
                    374: count_child(struct mdoc *mdoc)
1.36      kristaps  375: {
1.51      kristaps  376:        int               i;
1.36      kristaps  377:        struct mdoc_node *n;
                    378:
                    379:        for (i = 0, n = mdoc->last->child; n; n = n->next, i++)
                    380:                /* Do nothing */ ;
1.53      kristaps  381:
1.36      kristaps  382:        return(i);
                    383: }
                    384:
                    385:
1.53      kristaps  386: /*
                    387:  * Build these up with macros because they're basically the same check
                    388:  * for different inequalities.  Yes, this could be done with functions,
                    389:  * but this is reasonable for now.
                    390:  */
1.36      kristaps  391:
1.53      kristaps  392: #define CHECK_CHILD_DEFN(lvl, name, ineq)                      \
                    393: static int                                                     \
                    394: lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz)   \
                    395: {                                                              \
                    396:        int i;                                                  \
                    397:        if ((i = count_child(mdoc)) ineq sz)                    \
                    398:                return(1);                                      \
                    399:        return(lvl##_count(mdoc, #ineq, sz, p, i));             \
                    400: }
                    401:
                    402: #define CHECK_BODY_DEFN(name, lvl, func, num)                  \
                    403: static int                                                     \
1.55      kristaps  404: b##lvl##_##name(POST_ARGS)                                     \
1.53      kristaps  405: {                                                              \
                    406:        if (MDOC_BODY != mdoc->last->type)                      \
                    407:                return(1);                                      \
                    408:        return(func(mdoc, "multiline parameters", (num)));      \
                    409: }
                    410:
                    411: #define CHECK_ELEM_DEFN(name, lvl, func, num)                  \
                    412: static int                                                     \
1.55      kristaps  413: e##lvl##_##name(POST_ARGS)                                     \
1.53      kristaps  414: {                                                              \
                    415:        assert(MDOC_ELEM == mdoc->last->type);                  \
                    416:        return(func(mdoc, "line parameters", (num)));           \
                    417: }
                    418:
                    419: #define CHECK_HEAD_DEFN(name, lvl, func, num)                  \
                    420: static int                                                     \
1.55      kristaps  421: h##lvl##_##name(POST_ARGS)                                     \
1.53      kristaps  422: {                                                              \
                    423:        if (MDOC_HEAD != mdoc->last->type)                      \
                    424:                return(1);                                      \
                    425:        return(func(mdoc, "multiline parameters", (num)));      \
1.36      kristaps  426: }
                    427:
                    428:
1.53      kristaps  429: CHECK_CHILD_DEFN(warn, gt, >)                  /* warn_child_gt() */
                    430: CHECK_CHILD_DEFN(err, gt, >)                   /* err_child_gt() */
                    431: CHECK_CHILD_DEFN(warn, eq, ==)                 /* warn_child_eq() */
                    432: CHECK_CHILD_DEFN(err, eq, ==)                  /* err_child_eq() */
                    433: CHECK_CHILD_DEFN(err, lt, <)                   /* err_child_lt() */
                    434: CHECK_BODY_DEFN(ge1, warn, warn_child_gt, 0)   /* bwarn_ge1() */
                    435: CHECK_ELEM_DEFN(eq1, warn, warn_child_eq, 1)   /* ewarn_eq1() */
                    436: CHECK_ELEM_DEFN(eq0, warn, warn_child_eq, 0)   /* ewarn_eq0() */
                    437: CHECK_ELEM_DEFN(ge1, warn, warn_child_gt, 0)   /* ewarn_gt1() */
                    438: CHECK_ELEM_DEFN(eq1, err, err_child_eq, 1)     /* eerr_eq1() */
                    439: CHECK_ELEM_DEFN(le2, err, err_child_lt, 3)     /* eerr_le2() */
                    440: CHECK_ELEM_DEFN(le1, err, err_child_lt, 2)     /* eerr_le1() */
                    441: CHECK_ELEM_DEFN(eq0, err, err_child_eq, 0)     /* eerr_eq0() */
                    442: CHECK_ELEM_DEFN(ge1, err, err_child_gt, 0)     /* eerr_ge1() */
                    443: CHECK_HEAD_DEFN(eq0, err, err_child_eq, 0)     /* herr_eq0() */
                    444: CHECK_HEAD_DEFN(le1, err, err_child_lt, 2)     /* herr_le1() */
                    445: CHECK_HEAD_DEFN(ge1, err, err_child_gt, 0)     /* herr_ge1() */
1.36      kristaps  446:
                    447:
                    448: static int
1.55      kristaps  449: check_stdarg(PRE_ARGS)
1.36      kristaps  450: {
                    451:
1.55      kristaps  452:        if (MDOC_Std == n->data.elem.argv[0].arg &&
                    453:                        1 == n->data.elem.argc)
1.36      kristaps  454:                return(1);
1.51      kristaps  455:
1.55      kristaps  456:        return(mdoc_nwarn(mdoc, n, WARN_COMPAT,
1.53      kristaps  457:                                "one argument suggested"));
1.36      kristaps  458: }
                    459:
                    460:
                    461: static int
1.55      kristaps  462: check_msec(PRE_ARGS, int sz, enum mdoc_msec *msecs)
1.33      kristaps  463: {
                    464:        int              i;
                    465:
                    466:        for (i = 0; i < sz; i++)
                    467:                if (msecs[i] == mdoc->meta.msec)
                    468:                        return(1);
1.55      kristaps  469:        return(mdoc_nwarn(mdoc, n, WARN_COMPAT,
                    470:                                "invalid manual section"));
                    471: }
                    472:
                    473:
                    474: static int
                    475: check_text(struct mdoc *mdoc, size_t line, size_t pos, const char *p)
                    476: {
                    477:        size_t           c;
                    478:
                    479:        for ( ; *p; p++) {
1.56      kristaps  480:                if ( ! isprint(*p) && '\t' != *p)
                    481:                        return(mdoc_perr(mdoc, line, pos,
                    482:                                        "invalid characters"));
1.55      kristaps  483:                if ('\\' != *p)
                    484:                        continue;
                    485:                if ((c = mdoc_isescape(p))) {
                    486:                        p += (c - 1);
                    487:                        continue;
                    488:                }
                    489:                return(mdoc_perr(mdoc, line, pos,
1.56      kristaps  490:                                        "invalid escape sequence"));
1.55      kristaps  491:        }
                    492:
                    493:        return(1);
1.14      kristaps  494: }
                    495:
                    496:
1.55      kristaps  497:
                    498:
1.14      kristaps  499: static int
1.55      kristaps  500: check_parent(PRE_ARGS, int tok, enum mdoc_type t)
1.54      kristaps  501: {
                    502:
                    503:        assert(n->parent);
                    504:        if ((MDOC_ROOT == t || tok == n->parent->tok) &&
                    505:                        (t == n->parent->type))
                    506:                return(1);
                    507:
                    508:        return(mdoc_nerr(mdoc, n, "require parent %s",
                    509:                MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]));
                    510: }
                    511:
                    512:
                    513:
                    514: static int
1.55      kristaps  515: pre_display(PRE_ARGS)
1.23      kristaps  516: {
1.55      kristaps  517:        struct mdoc_node *node;
1.23      kristaps  518:
1.53      kristaps  519:        /* Display elements (`Bd', `D1'...) cannot be nested. */
                    520:
1.55      kristaps  521:        if (MDOC_BLOCK != n->type)
1.24      kristaps  522:                return(1);
                    523:
1.38      kristaps  524:        /* LINTED */
1.55      kristaps  525:        for (node = mdoc->last->parent; node; node = node->parent)
                    526:                if (MDOC_BLOCK == node->type)
                    527:                        if (MDOC_Bd == node->tok)
1.23      kristaps  528:                                break;
1.55      kristaps  529:        if (NULL == node)
1.23      kristaps  530:                return(1);
1.53      kristaps  531:
1.55      kristaps  532:        return(mdoc_nerr(mdoc, n, "displays may not be nested"));
1.23      kristaps  533: }
                    534:
                    535:
                    536: static int
1.55      kristaps  537: pre_bl(PRE_ARGS)
1.24      kristaps  538: {
1.53      kristaps  539:        int              type, err, i;
1.24      kristaps  540:        struct mdoc_arg *argv;
1.53      kristaps  541:        size_t           argc;
1.24      kristaps  542:
1.55      kristaps  543:        if (MDOC_BLOCK != n->type)
1.24      kristaps  544:                return(1);
                    545:
1.55      kristaps  546:        argc = n->data.block.argc;
1.24      kristaps  547:
1.53      kristaps  548:        /* Make sure that only one type of list is specified.  */
                    549:
1.38      kristaps  550:        /* LINTED */
1.53      kristaps  551:        for (i = 0, type = err = 0; i < (int)argc; i++) {
1.55      kristaps  552:                argv = &n->data.block.argv[i];
1.53      kristaps  553:
1.24      kristaps  554:                switch (argv->arg) {
                    555:                case (MDOC_Bullet):
                    556:                        /* FALLTHROUGH */
                    557:                case (MDOC_Dash):
                    558:                        /* FALLTHROUGH */
                    559:                case (MDOC_Enum):
                    560:                        /* FALLTHROUGH */
                    561:                case (MDOC_Hyphen):
                    562:                        /* FALLTHROUGH */
                    563:                case (MDOC_Item):
                    564:                        /* FALLTHROUGH */
                    565:                case (MDOC_Tag):
                    566:                        /* FALLTHROUGH */
                    567:                case (MDOC_Diag):
                    568:                        /* FALLTHROUGH */
                    569:                case (MDOC_Hang):
                    570:                        /* FALLTHROUGH */
                    571:                case (MDOC_Ohang):
                    572:                        /* FALLTHROUGH */
                    573:                case (MDOC_Inset):
1.26      kristaps  574:                        /* FALLTHROUGH */
                    575:                case (MDOC_Column):
1.53      kristaps  576:                        if (0 == type++)
                    577:                                break;
                    578:                        return(mdoc_perr(mdoc, argv->line, argv->pos,
                    579:                                        "multiple types specified"));
1.24      kristaps  580:                default:
                    581:                        break;
                    582:                }
                    583:        }
1.53      kristaps  584:
                    585:        if (type)
                    586:                return(1);
                    587:        return(mdoc_err(mdoc, "no type specified"));
1.24      kristaps  588: }
                    589:
                    590:
                    591: static int
1.55      kristaps  592: pre_bd(PRE_ARGS)
1.24      kristaps  593: {
1.53      kristaps  594:        int              type, err, i;
1.24      kristaps  595:        struct mdoc_arg *argv;
1.53      kristaps  596:        size_t           argc;
1.24      kristaps  597:
1.55      kristaps  598:        if (MDOC_BLOCK != n->type)
1.24      kristaps  599:                return(1);
                    600:
1.55      kristaps  601:        argc = n->data.block.argc;
1.24      kristaps  602:
1.53      kristaps  603:        /* Make sure that only one type of display is specified.  */
                    604:
1.38      kristaps  605:        /* LINTED */
1.53      kristaps  606:        for (i = 0, err = type = 0; ! err && i < (int)argc; i++) {
1.55      kristaps  607:                argv = &n->data.block.argv[i];
1.53      kristaps  608:
1.24      kristaps  609:                switch (argv->arg) {
                    610:                case (MDOC_Ragged):
                    611:                        /* FALLTHROUGH */
                    612:                case (MDOC_Unfilled):
                    613:                        /* FALLTHROUGH */
1.29      kristaps  614:                case (MDOC_Filled):
                    615:                        /* FALLTHROUGH */
1.24      kristaps  616:                case (MDOC_Literal):
                    617:                        /* FALLTHROUGH */
                    618:                case (MDOC_File):
1.53      kristaps  619:                        if (0 == type++)
                    620:                                break;
                    621:                        return(mdoc_perr(mdoc, argv->line, argv->pos,
                    622:                                        "multiple types specified"));
1.24      kristaps  623:                default:
                    624:                        break;
                    625:                }
                    626:        }
1.53      kristaps  627:
                    628:        if (type)
                    629:                return(1);
                    630:        return(mdoc_err(mdoc, "no type specified"));
1.24      kristaps  631: }
                    632:
                    633:
                    634: static int
1.55      kristaps  635: pre_ss(PRE_ARGS)
1.33      kristaps  636: {
                    637:
1.55      kristaps  638:        if (MDOC_BLOCK != n->type)
1.33      kristaps  639:                return(1);
1.55      kristaps  640:        return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
1.33      kristaps  641: }
                    642:
                    643:
                    644: static int
1.55      kristaps  645: pre_sh(PRE_ARGS)
1.33      kristaps  646: {
                    647:
1.55      kristaps  648:        if (MDOC_BLOCK != n->type)
1.33      kristaps  649:                return(1);
1.55      kristaps  650:        return(check_parent(mdoc, n, -1, MDOC_ROOT));
1.33      kristaps  651: }
                    652:
                    653:
                    654: static int
1.55      kristaps  655: pre_it(PRE_ARGS)
1.53      kristaps  656: {
                    657:
                    658:        /* TODO: -width attribute must be specified for -tag. */
                    659:        /* TODO: children too big for -width? */
                    660:
1.55      kristaps  661:        if (MDOC_BLOCK != n->type)
1.53      kristaps  662:                return(1);
1.55      kristaps  663:        return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
1.53      kristaps  664: }
                    665:
                    666:
                    667: static int
1.55      kristaps  668: pre_st(PRE_ARGS)
1.36      kristaps  669: {
                    670:
1.55      kristaps  671:        if (1 == n->data.elem.argc)
1.36      kristaps  672:                return(1);
1.55      kristaps  673:        return(mdoc_nerr(mdoc, n, "one argument required"));
1.36      kristaps  674: }
                    675:
                    676:
                    677: static int
1.55      kristaps  678: pre_an(PRE_ARGS)
1.35      kristaps  679: {
1.36      kristaps  680:
1.55      kristaps  681:        if (1 >= n->data.elem.argc)
1.35      kristaps  682:                return(1);
1.55      kristaps  683:        return(mdoc_nerr(mdoc, n, "one argument allowed"));
1.35      kristaps  684: }
                    685:
                    686:
                    687: static int
1.55      kristaps  688: pre_rv(PRE_ARGS)
1.36      kristaps  689: {
1.53      kristaps  690:        enum mdoc_msec msecs[] = { MSEC_2, MSEC_3 };
1.36      kristaps  691:
1.55      kristaps  692:        if ( ! check_msec(mdoc, n, 2, msecs))
1.36      kristaps  693:                return(0);
1.55      kristaps  694:        return(check_stdarg(mdoc, n));
1.36      kristaps  695: }
                    696:
                    697:
                    698: static int
1.55      kristaps  699: pre_ex(PRE_ARGS)
1.33      kristaps  700: {
1.53      kristaps  701:        enum mdoc_msec msecs[] = { MSEC_1, MSEC_6, MSEC_8 };
1.35      kristaps  702:
1.55      kristaps  703:        if ( ! check_msec(mdoc, n, 3, msecs))
1.35      kristaps  704:                return(0);
1.55      kristaps  705:        return(check_stdarg(mdoc, n));
1.33      kristaps  706: }
                    707:
                    708:
                    709: static int
1.55      kristaps  710: pre_er(PRE_ARGS)
1.33      kristaps  711: {
1.53      kristaps  712:        enum mdoc_msec msecs[] = { MSEC_2 };
1.33      kristaps  713:
1.55      kristaps  714:        return(check_msec(mdoc, n, 1, msecs));
1.33      kristaps  715: }
                    716:
                    717:
                    718: static int
1.55      kristaps  719: pre_cd(PRE_ARGS)
1.33      kristaps  720: {
1.53      kristaps  721:        enum mdoc_msec msecs[] = { MSEC_4 };
1.33      kristaps  722:
1.55      kristaps  723:        return(check_msec(mdoc, n, 1, msecs));
1.33      kristaps  724: }
                    725:
                    726:
                    727: static int
1.55      kristaps  728: pre_prologue(PRE_ARGS)
1.20      kristaps  729: {
                    730:
1.46      kristaps  731:        if (SEC_PROLOGUE != mdoc->lastnamed)
1.55      kristaps  732:                return(mdoc_nerr(mdoc, n, "prologue only"));
1.20      kristaps  733:
                    734:        /* Check for ordering. */
                    735:
1.55      kristaps  736:        switch (n->tok) {
1.20      kristaps  737:        case (MDOC_Os):
1.36      kristaps  738:                if (mdoc->meta.title && mdoc->meta.date)
1.20      kristaps  739:                        break;
1.55      kristaps  740:                return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20      kristaps  741:        case (MDOC_Dt):
1.36      kristaps  742:                if (NULL == mdoc->meta.title && mdoc->meta.date)
1.20      kristaps  743:                        break;
1.55      kristaps  744:                return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20      kristaps  745:        case (MDOC_Dd):
1.36      kristaps  746:                if (NULL == mdoc->meta.title && 0 == mdoc->meta.date)
1.20      kristaps  747:                        break;
1.55      kristaps  748:                return(mdoc_nerr(mdoc, n, "prologue out-of-order"));
1.20      kristaps  749:        default:
                    750:                abort();
                    751:                /* NOTREACHED */
                    752:        }
                    753:
                    754:        /* Check for repetition. */
                    755:
1.55      kristaps  756:        switch (n->tok) {
1.20      kristaps  757:        case (MDOC_Os):
1.36      kristaps  758:                if (NULL == mdoc->meta.os)
1.20      kristaps  759:                        return(1);
                    760:                break;
                    761:        case (MDOC_Dd):
                    762:                if (0 == mdoc->meta.date)
                    763:                        return(1);
                    764:                break;
                    765:        case (MDOC_Dt):
1.36      kristaps  766:                if (NULL == mdoc->meta.title)
1.20      kristaps  767:                        return(1);
                    768:                break;
                    769:        default:
                    770:                abort();
                    771:                /* NOTREACHED */
                    772:        }
                    773:
1.55      kristaps  774:        return(mdoc_nerr(mdoc, n, "prologue repetition"));
1.20      kristaps  775: }
                    776:
                    777:
1.35      kristaps  778: static int
1.55      kristaps  779: post_bf(POST_ARGS)
1.41      kristaps  780: {
                    781:        char             *p;
                    782:        struct mdoc_node *head;
                    783:
                    784:        if (MDOC_BLOCK != mdoc->last->type)
                    785:                return(1);
1.53      kristaps  786:
1.41      kristaps  787:        head = mdoc->last->data.block.head;
                    788:
                    789:        if (0 == mdoc->last->data.block.argc) {
1.53      kristaps  790:                if (NULL == head->child)
                    791:                        return(mdoc_err(mdoc, "argument expected"));
                    792:
                    793:                p = head->child->data.text.string;
                    794:                if (xstrcmp(p, "Em"))
                    795:                        return(1);
                    796:                else if (xstrcmp(p, "Li"))
                    797:                        return(1);
                    798:                else if (xstrcmp(p, "Sm"))
                    799:                        return(1);
                    800:                return(mdoc_nerr(mdoc, head->child, "invalid font"));
1.41      kristaps  801:        }
1.53      kristaps  802:
1.41      kristaps  803:        if (head->child)
1.53      kristaps  804:                return(mdoc_err(mdoc, "argument expected"));
                    805:
1.41      kristaps  806:        if (1 == mdoc->last->data.block.argc)
                    807:                return(1);
1.53      kristaps  808:        return(mdoc_err(mdoc, "argument expected"));
1.41      kristaps  809: }
                    810:
                    811:
                    812: static int
1.55      kristaps  813: post_nm(POST_ARGS)
1.37      kristaps  814: {
                    815:
                    816:        if (mdoc->last->child)
                    817:                return(1);
                    818:        if (mdoc->meta.name)
                    819:                return(1);
1.53      kristaps  820:        return(mdoc_err(mdoc, "not yet invoked with name"));
1.37      kristaps  821: }
                    822:
                    823:
                    824: static int
1.55      kristaps  825: post_xr(POST_ARGS)
1.36      kristaps  826: {
                    827:        struct mdoc_node *n;
                    828:
                    829:        if (NULL == (n = mdoc->last->child->next))
                    830:                return(1);
                    831:        if (MSEC_DEFAULT != mdoc_atomsec(n->data.text.string))
                    832:                return(1);
                    833:        return(mdoc_nerr(mdoc, n, "invalid manual section"));
                    834: }
                    835:
                    836:
                    837: static int
1.55      kristaps  838: post_at(POST_ARGS)
1.36      kristaps  839: {
                    840:
1.37      kristaps  841:        if (NULL == mdoc->last->child)
                    842:                return(1);
1.36      kristaps  843:        if (ATT_DEFAULT != mdoc_atoatt(mdoc->last->child->data.text.string))
                    844:                return(1);
1.53      kristaps  845:        return(mdoc_err(mdoc, "require valid symbol"));
1.36      kristaps  846: }
                    847:
                    848:
                    849: static int
1.55      kristaps  850: post_an(POST_ARGS)
1.35      kristaps  851: {
                    852:
                    853:        if (0 != mdoc->last->data.elem.argc) {
                    854:                if (NULL == mdoc->last->child)
                    855:                        return(1);
1.53      kristaps  856:                return(mdoc_err(mdoc, "argument(s) expected"));
1.35      kristaps  857:        }
                    858:
                    859:        if (mdoc->last->child)
                    860:                return(1);
1.53      kristaps  861:        return(mdoc_err(mdoc, "argument(s) expected"));
1.35      kristaps  862: }
                    863:
                    864:
                    865: static int
1.55      kristaps  866: post_ex(POST_ARGS)
1.35      kristaps  867: {
                    868:
                    869:        if (0 == mdoc->last->data.elem.argc) {
                    870:                if (mdoc->last->child)
                    871:                        return(1);
1.53      kristaps  872:                return(mdoc_err(mdoc, "argument(s) expected"));
1.35      kristaps  873:        }
                    874:        if (mdoc->last->child)
1.53      kristaps  875:                return(mdoc_err(mdoc, "argument(s) expected"));
1.35      kristaps  876:        if (1 != mdoc->last->data.elem.argc)
1.53      kristaps  877:                return(mdoc_err(mdoc, "argument(s) expected"));
1.35      kristaps  878:        if (MDOC_Std != mdoc->last->data.elem.argv[0].arg)
1.53      kristaps  879:                return(mdoc_err(mdoc, "argument(s) expected"));
                    880:
1.35      kristaps  881:        return(1);
                    882: }
                    883:
                    884:
1.25      kristaps  885: static int
1.55      kristaps  886: post_it(POST_ARGS)
1.25      kristaps  887: {
1.53      kristaps  888:        int               type, sv, i;
1.25      kristaps  889: #define        TYPE_NONE        (0)
                    890: #define        TYPE_BODY        (1)
                    891: #define        TYPE_HEAD        (2)
1.47      kristaps  892: #define        TYPE_OHEAD       (3)
1.53      kristaps  893:        size_t            argc;
1.25      kristaps  894:        struct mdoc_node *n;
                    895:
                    896:        if (MDOC_BLOCK != mdoc->last->type)
                    897:                return(1);
                    898:
1.53      kristaps  899:        n = mdoc->last->parent->parent;
1.25      kristaps  900:
                    901:        argc = n->data.block.argc;
                    902:        type = TYPE_NONE;
1.38      kristaps  903:        sv = -1;
1.26      kristaps  904:
                    905:        /* Some types require block-head, some not. */
1.25      kristaps  906:
1.38      kristaps  907:        /* LINTED */
1.53      kristaps  908:        for (i = 0; TYPE_NONE == type && i < (int)argc; i++)
                    909:                switch (n->data.block.argv[i].arg) {
1.25      kristaps  910:                case (MDOC_Tag):
                    911:                        /* FALLTHROUGH */
                    912:                case (MDOC_Diag):
                    913:                        /* FALLTHROUGH */
                    914:                case (MDOC_Hang):
                    915:                        /* FALLTHROUGH */
                    916:                case (MDOC_Ohang):
                    917:                        /* FALLTHROUGH */
                    918:                case (MDOC_Inset):
                    919:                        type = TYPE_HEAD;
1.53      kristaps  920:                        sv = n->data.block.argv[i].arg;
1.25      kristaps  921:                        break;
                    922:                case (MDOC_Bullet):
                    923:                        /* FALLTHROUGH */
                    924:                case (MDOC_Dash):
                    925:                        /* FALLTHROUGH */
                    926:                case (MDOC_Enum):
                    927:                        /* FALLTHROUGH */
                    928:                case (MDOC_Hyphen):
                    929:                        /* FALLTHROUGH */
                    930:                case (MDOC_Item):
1.47      kristaps  931:                        type = TYPE_BODY;
1.53      kristaps  932:                        sv = n->data.block.argv[i].arg;
1.47      kristaps  933:                        break;
1.25      kristaps  934:                case (MDOC_Column):
1.47      kristaps  935:                        type = TYPE_OHEAD;
1.53      kristaps  936:                        sv = n->data.block.argv[i].arg;
1.25      kristaps  937:                        break;
                    938:                default:
                    939:                        break;
                    940:                }
                    941:
                    942:        assert(TYPE_NONE != type);
                    943:
1.47      kristaps  944:        n = mdoc->last->data.block.head;
                    945:
1.25      kristaps  946:        if (TYPE_HEAD == type) {
1.33      kristaps  947:                if (NULL == n->child)
1.53      kristaps  948:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    949:                                        "argument(s) suggested"))
1.25      kristaps  950:                                return(0);
                    951:
1.33      kristaps  952:                n = mdoc->last->data.block.body;
                    953:                if (NULL == n->child)
1.53      kristaps  954:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    955:                                        "multiline body suggested"))
1.25      kristaps  956:                                return(0);
                    957:
1.47      kristaps  958:        } else if (TYPE_BODY == type) {
                    959:                if (n->child)
1.53      kristaps  960:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    961:                                        "no argument suggested"))
1.47      kristaps  962:                                return(0);
                    963:
                    964:                n = mdoc->last->data.block.body;
                    965:                if (NULL == n->child)
1.53      kristaps  966:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    967:                                        "multiline body suggested"))
1.47      kristaps  968:                                return(0);
                    969:        } else {
                    970:                if (NULL == n->child)
1.53      kristaps  971:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    972:                                        "argument(s) suggested"))
1.47      kristaps  973:                                return(0);
                    974:
                    975:                n = mdoc->last->data.block.body;
                    976:                if (n->child)
1.53      kristaps  977:                        if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
                    978:                                        "no multiline body suggested"))
1.47      kristaps  979:                                return(0);
1.25      kristaps  980:        }
                    981:
1.47      kristaps  982:        if (MDOC_Column != sv)
1.26      kristaps  983:                return(1);
                    984:
1.38      kristaps  985:        argc = mdoc->last->parent->parent->data.block.argv->sz;
1.26      kristaps  986:        n = mdoc->last->data.block.head->child;
1.25      kristaps  987:
1.26      kristaps  988:        for (i = 0; n; n = n->next)
                    989:                i++;
                    990:
1.53      kristaps  991:        if (i == (int)argc)
1.26      kristaps  992:                return(1);
1.53      kristaps  993:
                    994:        return(mdoc_err(mdoc, "need %zu columns (have %d)", argc, i));
1.25      kristaps  995: #undef TYPE_NONE
                    996: #undef TYPE_BODY
                    997: #undef TYPE_HEAD
1.47      kristaps  998: #undef TYPE_OHEAD
1.25      kristaps  999: }
                   1000:
                   1001:
1.24      kristaps 1002: static int
1.55      kristaps 1003: post_bl(POST_ARGS)
1.24      kristaps 1004: {
1.53      kristaps 1005:        struct mdoc_node        *n;
1.24      kristaps 1006:
                   1007:        if (MDOC_BODY != mdoc->last->type)
                   1008:                return(1);
                   1009:
1.38      kristaps 1010:        /* LINTED */
1.24      kristaps 1011:        for (n = mdoc->last->child; n; n = n->next) {
                   1012:                if (MDOC_BLOCK == n->type)
1.25      kristaps 1013:                        if (MDOC_It == n->tok)
1.24      kristaps 1014:                                continue;
                   1015:                break;
                   1016:        }
1.53      kristaps 1017:
1.24      kristaps 1018:        if (NULL == n)
                   1019:                return(1);
1.53      kristaps 1020:
                   1021:        return(mdoc_nerr(mdoc, n, "bad child of parent list"));
1.24      kristaps 1022: }
                   1023:
                   1024:
1.34      kristaps 1025: static int
1.37      kristaps 1026: ebool(struct mdoc *mdoc)
1.34      kristaps 1027: {
                   1028:        struct mdoc_node *n;
                   1029:
1.38      kristaps 1030:        /* LINTED */
1.34      kristaps 1031:        for (n = mdoc->last->child; n; n = n->next) {
                   1032:                if (MDOC_TEXT != n->type)
                   1033:                        break;
                   1034:                if (xstrcmp(n->data.text.string, "on"))
                   1035:                        continue;
                   1036:                if (xstrcmp(n->data.text.string, "off"))
                   1037:                        continue;
                   1038:                break;
                   1039:        }
1.53      kristaps 1040:
1.34      kristaps 1041:        if (NULL == n)
                   1042:                return(1);
1.53      kristaps 1043:        return(mdoc_nerr(mdoc, n, "expected boolean"));
1.37      kristaps 1044: }
                   1045:
                   1046:
                   1047: static int
1.55      kristaps 1048: post_root(POST_ARGS)
1.37      kristaps 1049: {
                   1050:
1.46      kristaps 1051:        if (NULL == mdoc->first->child)
1.53      kristaps 1052:                return(mdoc_err(mdoc, "document lacks data"));
1.46      kristaps 1053:        if (SEC_PROLOGUE == mdoc->lastnamed)
1.53      kristaps 1054:                return(mdoc_err(mdoc, "document lacks prologue"));
                   1055:
1.46      kristaps 1056:        if (MDOC_BLOCK != mdoc->first->child->type)
1.54      kristaps 1057:                return(mdoc_err(mdoc, "lacking post-prologue %s",
1.53      kristaps 1058:                                        mdoc_macronames[MDOC_Sh]));
1.46      kristaps 1059:        if (MDOC_Sh != mdoc->first->child->tok)
1.54      kristaps 1060:                return(mdoc_err(mdoc, "lacking post-prologue %s",
1.53      kristaps 1061:                                        mdoc_macronames[MDOC_Sh]));
                   1062:
1.37      kristaps 1063:        return(1);
1.34      kristaps 1064: }
                   1065:
                   1066:
1.20      kristaps 1067: static int
1.55      kristaps 1068: post_sh(POST_ARGS)
1.14      kristaps 1069: {
1.46      kristaps 1070:
                   1071:        if (MDOC_HEAD == mdoc->last->type)
                   1072:                return(post_sh_head(mdoc));
                   1073:        if (MDOC_BODY == mdoc->last->type)
                   1074:                return(post_sh_body(mdoc));
1.53      kristaps 1075:
1.46      kristaps 1076:        return(1);
                   1077: }
                   1078:
                   1079:
                   1080: static int
1.55      kristaps 1081: post_sh_body(POST_ARGS)
1.46      kristaps 1082: {
                   1083:        struct mdoc_node *n;
                   1084:
                   1085:        if (SEC_NAME != mdoc->lastnamed)
                   1086:                return(1);
                   1087:
1.51      kristaps 1088:        /*
                   1089:         * Warn if the NAME section doesn't contain the `Nm' and `Nd'
                   1090:         * macros (can have multiple `Nm' and one `Nd').  Note that the
                   1091:         * children of the BODY declaration can also be "text".
                   1092:         */
                   1093:
1.46      kristaps 1094:        if (NULL == (n = mdoc->last->child))
1.54      kristaps 1095:                return(mdoc_warn(mdoc, WARN_SYNTAX,
                   1096:                                        "section should have %s and %s",
1.51      kristaps 1097:                                        mdoc_macronames[MDOC_Nm],
                   1098:                                        mdoc_macronames[MDOC_Nd]));
                   1099:
                   1100:        for ( ; n && n->next; n = n->next) {
                   1101:                if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
                   1102:                        continue;
                   1103:                if (MDOC_TEXT == n->type)
                   1104:                        continue;
1.54      kristaps 1105:                if ( ! (mdoc_nwarn(mdoc, n, WARN_SYNTAX,
                   1106:                                        "section should have %s first",
1.51      kristaps 1107:                                        mdoc_macronames[MDOC_Nm])))
                   1108:                        return(0);
                   1109:        }
                   1110:
                   1111:        if (MDOC_ELEM == n->type && MDOC_Nd == n->tok)
1.46      kristaps 1112:                return(1);
                   1113:
1.54      kristaps 1114:        return(mdoc_warn(mdoc, WARN_SYNTAX,
                   1115:                                "section should have %s last",
1.51      kristaps 1116:                                mdoc_macronames[MDOC_Nd]));
1.46      kristaps 1117: }
                   1118:
                   1119:
                   1120: static int
1.55      kristaps 1121: post_sh_head(POST_ARGS)
1.46      kristaps 1122: {
1.36      kristaps 1123:        char              buf[64];
1.21      kristaps 1124:        enum mdoc_sec     sec;
                   1125:
1.25      kristaps 1126:        assert(MDOC_Sh == mdoc->last->tok);
1.21      kristaps 1127:
1.54      kristaps 1128:        if ( ! xstrlcats(buf, mdoc->last->child, sizeof(buf)))
                   1129:                return(mdoc_err(mdoc, "argument too long"));
1.14      kristaps 1130:
1.46      kristaps 1131:        sec = mdoc_atosec(buf);
                   1132:
                   1133:        if (SEC_BODY == mdoc->lastnamed && SEC_NAME != sec)
1.54      kristaps 1134:                return(mdoc_warn(mdoc, WARN_SYNTAX,
                   1135:                                "section NAME should be first"));
1.46      kristaps 1136:        if (SEC_CUSTOM == sec)
1.21      kristaps 1137:                return(1);
1.46      kristaps 1138:        if (sec == mdoc->lastnamed)
1.54      kristaps 1139:                return(mdoc_warn(mdoc, WARN_SYNTAX,
                   1140:                                "section repeated"));
1.46      kristaps 1141:        if (sec < mdoc->lastnamed)
1.54      kristaps 1142:                return(mdoc_warn(mdoc, WARN_SYNTAX,
                   1143:                                "section out of order"));
1.46      kristaps 1144:
                   1145:        return(1);
1.11      kristaps 1146: }
                   1147:
                   1148:
1.57    ! kristaps 1149: static int
        !          1150: post_fd(POST_ARGS)
1.11      kristaps 1151: {
1.39      kristaps 1152:
1.57    ! kristaps 1153:        if (SEC_SYNOPSIS == mdoc->last->sec)
1.25      kristaps 1154:                return(1);
1.57    ! kristaps 1155:        return(mdoc_warn(mdoc, WARN_COMPAT,
        !          1156:                        "suggested only in section SYNOPSIS"));
1.11      kristaps 1157: }

CVSweb