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

Annotation of mandoc/validate.c, Revision 1.53

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

CVSweb