Annotation of src/usr.bin/mandoc/mdoc_validate.c, Revision 1.60
1.60 ! schwarze 1: /* $Id: mdoc_validate.c,v 1.59 2010/06/06 18:08:41 schwarze Exp $ */
1.1 kristaps 2: /*
1.56 schwarze 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 16: */
17: #include <sys/types.h>
18:
19: #include <assert.h>
20: #include <ctype.h>
1.31 schwarze 21: #include <limits.h>
1.57 schwarze 22: #include <stdio.h>
1.1 kristaps 23: #include <stdlib.h>
24: #include <string.h>
25:
1.55 schwarze 26: #include "mandoc.h"
1.1 kristaps 27: #include "libmdoc.h"
1.15 schwarze 28: #include "libmandoc.h"
1.1 kristaps 29:
30: /* FIXME: .Bl -diag can't have non-text children in HEAD. */
31: /* TODO: ignoring Pp (it's superfluous in some invocations). */
32:
1.60 ! schwarze 33: #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
1.28 schwarze 34: #define POST_ARGS struct mdoc *mdoc
1.1 kristaps 35:
36: typedef int (*v_pre)(PRE_ARGS);
37: typedef int (*v_post)(POST_ARGS);
38:
39: struct valids {
40: v_pre *pre;
41: v_post *post;
42: };
43:
1.44 schwarze 44: static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
1.28 schwarze 45: static int check_stdarg(PRE_ARGS);
46: static int check_text(struct mdoc *, int, int, const char *);
47: static int check_argv(struct mdoc *,
1.1 kristaps 48: const struct mdoc_node *,
49: const struct mdoc_argv *);
1.28 schwarze 50: static int check_args(struct mdoc *,
1.1 kristaps 51: const struct mdoc_node *);
1.28 schwarze 52: static int err_child_lt(struct mdoc *, const char *, int);
53: static int warn_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: static int warn_count(struct mdoc *, const char *,
1.1 kristaps 59: int, const char *, int);
1.28 schwarze 60: static int err_count(struct mdoc *, const char *,
1.1 kristaps 61: int, const char *, int);
1.28 schwarze 62:
63: static int berr_ge1(POST_ARGS);
64: static int bwarn_ge1(POST_ARGS);
65: static int ebool(POST_ARGS);
66: static int eerr_eq0(POST_ARGS);
67: static int eerr_eq1(POST_ARGS);
68: static int eerr_ge1(POST_ARGS);
1.37 schwarze 69: static int eerr_le1(POST_ARGS);
1.28 schwarze 70: static int ewarn_ge1(POST_ARGS);
71: static int herr_eq0(POST_ARGS);
72: static int herr_ge1(POST_ARGS);
73: static int hwarn_eq1(POST_ARGS);
1.48 schwarze 74: static int hwarn_eq0(POST_ARGS);
1.28 schwarze 75: static int hwarn_le1(POST_ARGS);
76:
77: static int post_an(POST_ARGS);
78: static int post_at(POST_ARGS);
79: static int post_bf(POST_ARGS);
80: static int post_bl(POST_ARGS);
81: static int post_bl_head(POST_ARGS);
1.60 ! schwarze 82: static int post_dt(POST_ARGS);
1.28 schwarze 83: static int post_it(POST_ARGS);
84: static int post_lb(POST_ARGS);
85: static int post_nm(POST_ARGS);
86: static int post_root(POST_ARGS);
1.36 schwarze 87: static int post_rs(POST_ARGS);
1.28 schwarze 88: static int post_sh(POST_ARGS);
89: static int post_sh_body(POST_ARGS);
90: static int post_sh_head(POST_ARGS);
91: static int post_st(POST_ARGS);
1.59 schwarze 92: static int post_eoln(POST_ARGS);
1.42 schwarze 93: static int post_vt(POST_ARGS);
1.28 schwarze 94: static int pre_an(PRE_ARGS);
95: static int pre_bd(PRE_ARGS);
96: static int pre_bl(PRE_ARGS);
97: static int pre_dd(PRE_ARGS);
98: static int pre_display(PRE_ARGS);
99: static int pre_dt(PRE_ARGS);
100: static int pre_it(PRE_ARGS);
101: static int pre_os(PRE_ARGS);
102: static int pre_rv(PRE_ARGS);
103: static int pre_sh(PRE_ARGS);
104: static int pre_ss(PRE_ARGS);
105:
106: static v_post posts_an[] = { post_an, NULL };
107: static v_post posts_at[] = { post_at, NULL };
1.48 schwarze 108: static v_post posts_bd[] = { hwarn_eq0, bwarn_ge1, NULL };
1.28 schwarze 109: static v_post posts_bf[] = { hwarn_le1, post_bf, NULL };
110: static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
111: static v_post posts_bool[] = { eerr_eq1, ebool, NULL };
1.59 schwarze 112: static v_post posts_eoln[] = { post_eoln, NULL };
1.60 ! schwarze 113: static v_post posts_dt[] = { post_dt, NULL };
1.28 schwarze 114: static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
115: static v_post posts_it[] = { post_it, NULL };
116: static v_post posts_lb[] = { eerr_eq1, post_lb, NULL };
117: static v_post posts_nd[] = { berr_ge1, NULL };
118: static v_post posts_nm[] = { post_nm, NULL };
119: static v_post posts_notext[] = { eerr_eq0, NULL };
1.36 schwarze 120: static v_post posts_rs[] = { berr_ge1, herr_eq0, post_rs, NULL };
1.28 schwarze 121: static v_post posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL };
1.37 schwarze 122: static v_post posts_sp[] = { eerr_le1, NULL };
1.28 schwarze 123: static v_post posts_ss[] = { herr_ge1, NULL };
124: static v_post posts_st[] = { eerr_eq1, post_st, NULL };
125: static v_post posts_text[] = { eerr_ge1, NULL };
1.38 schwarze 126: static v_post posts_text1[] = { eerr_eq1, NULL };
1.42 schwarze 127: static v_post posts_vt[] = { post_vt, NULL };
1.28 schwarze 128: static v_post posts_wline[] = { bwarn_ge1, herr_eq0, NULL };
129: static v_post posts_wtext[] = { ewarn_ge1, NULL };
130: static v_pre pres_an[] = { pre_an, NULL };
131: static v_pre pres_bd[] = { pre_display, pre_bd, NULL };
132: static v_pre pres_bl[] = { pre_bl, NULL };
133: static v_pre pres_d1[] = { pre_display, NULL };
134: static v_pre pres_dd[] = { pre_dd, NULL };
135: static v_pre pres_dt[] = { pre_dt, NULL };
1.48 schwarze 136: static v_pre pres_er[] = { NULL, NULL };
1.53 schwarze 137: static v_pre pres_ex[] = { NULL, NULL };
138: static v_pre pres_fd[] = { NULL, NULL };
1.28 schwarze 139: static v_pre pres_it[] = { pre_it, NULL };
140: static v_pre pres_os[] = { pre_os, NULL };
141: static v_pre pres_rv[] = { pre_rv, NULL };
142: static v_pre pres_sh[] = { pre_sh, NULL };
143: static v_pre pres_ss[] = { pre_ss, NULL };
1.1 kristaps 144:
145: const struct valids mdoc_valids[MDOC_MAX] = {
1.7 schwarze 146: { NULL, NULL }, /* Ap */
1.1 kristaps 147: { pres_dd, posts_text }, /* Dd */
1.60 ! schwarze 148: { pres_dt, posts_dt }, /* Dt */
1.1 kristaps 149: { pres_os, NULL }, /* Os */
150: { pres_sh, posts_sh }, /* Sh */
151: { pres_ss, posts_ss }, /* Ss */
1.31 schwarze 152: { NULL, posts_notext }, /* Pp */
1.1 kristaps 153: { pres_d1, posts_wline }, /* D1 */
154: { pres_d1, posts_wline }, /* Dl */
155: { pres_bd, posts_bd }, /* Bd */
156: { NULL, NULL }, /* Ed */
157: { pres_bl, posts_bl }, /* Bl */
158: { NULL, NULL }, /* El */
159: { pres_it, posts_it }, /* It */
160: { NULL, posts_text }, /* Ad */
161: { pres_an, posts_an }, /* An */
162: { NULL, NULL }, /* Ar */
1.50 schwarze 163: { NULL, posts_text }, /* Cd */
1.1 kristaps 164: { NULL, NULL }, /* Cm */
165: { NULL, NULL }, /* Dv */
166: { pres_er, posts_text }, /* Er */
167: { NULL, NULL }, /* Ev */
1.35 schwarze 168: { pres_ex, NULL }, /* Ex */
1.1 kristaps 169: { NULL, NULL }, /* Fa */
170: { pres_fd, posts_wtext }, /* Fd */
171: { NULL, NULL }, /* Fl */
172: { NULL, posts_text }, /* Fn */
173: { NULL, posts_wtext }, /* Ft */
174: { NULL, posts_text }, /* Ic */
1.38 schwarze 175: { NULL, posts_text1 }, /* In */
1.1 kristaps 176: { NULL, NULL }, /* Li */
1.25 schwarze 177: { NULL, posts_nd }, /* Nd */
1.1 kristaps 178: { NULL, posts_nm }, /* Nm */
179: { NULL, posts_wline }, /* Op */
180: { NULL, NULL }, /* Ot */
181: { NULL, NULL }, /* Pa */
1.35 schwarze 182: { pres_rv, NULL }, /* Rv */
1.1 kristaps 183: { NULL, posts_st }, /* St */
184: { NULL, NULL }, /* Va */
1.42 schwarze 185: { NULL, posts_vt }, /* Vt */
1.59 schwarze 186: { NULL, posts_wtext }, /* Xr */
1.1 kristaps 187: { NULL, posts_text }, /* %A */
1.37 schwarze 188: { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */
1.40 schwarze 189: { NULL, posts_text }, /* %D */ /* FIXME: check date with mandoc_a2time(). */
1.1 kristaps 190: { NULL, posts_text }, /* %I */
191: { NULL, posts_text }, /* %J */
192: { NULL, posts_text }, /* %N */
193: { NULL, posts_text }, /* %O */
194: { NULL, posts_text }, /* %P */
195: { NULL, posts_text }, /* %R */
1.37 schwarze 196: { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */
1.1 kristaps 197: { NULL, posts_text }, /* %V */
198: { NULL, NULL }, /* Ac */
199: { NULL, NULL }, /* Ao */
200: { NULL, posts_wline }, /* Aq */
201: { NULL, posts_at }, /* At */
202: { NULL, NULL }, /* Bc */
203: { NULL, posts_bf }, /* Bf */
204: { NULL, NULL }, /* Bo */
205: { NULL, posts_wline }, /* Bq */
206: { NULL, NULL }, /* Bsx */
207: { NULL, NULL }, /* Bx */
208: { NULL, posts_bool }, /* Db */
209: { NULL, NULL }, /* Dc */
210: { NULL, NULL }, /* Do */
211: { NULL, posts_wline }, /* Dq */
212: { NULL, NULL }, /* Ec */
213: { NULL, NULL }, /* Ef */
214: { NULL, NULL }, /* Em */
215: { NULL, NULL }, /* Eo */
216: { NULL, NULL }, /* Fx */
217: { NULL, posts_text }, /* Ms */
218: { NULL, posts_notext }, /* No */
219: { NULL, posts_notext }, /* Ns */
220: { NULL, NULL }, /* Nx */
221: { NULL, NULL }, /* Ox */
222: { NULL, NULL }, /* Pc */
1.38 schwarze 223: { NULL, posts_text1 }, /* Pf */
1.1 kristaps 224: { NULL, NULL }, /* Po */
225: { NULL, posts_wline }, /* Pq */
226: { NULL, NULL }, /* Qc */
227: { NULL, posts_wline }, /* Ql */
228: { NULL, NULL }, /* Qo */
229: { NULL, posts_wline }, /* Qq */
230: { NULL, NULL }, /* Re */
1.36 schwarze 231: { NULL, posts_rs }, /* Rs */
1.1 kristaps 232: { NULL, NULL }, /* Sc */
233: { NULL, NULL }, /* So */
234: { NULL, posts_wline }, /* Sq */
235: { NULL, posts_bool }, /* Sm */
236: { NULL, posts_text }, /* Sx */
237: { NULL, posts_text }, /* Sy */
238: { NULL, posts_text }, /* Tn */
239: { NULL, NULL }, /* Ux */
240: { NULL, NULL }, /* Xc */
241: { NULL, NULL }, /* Xo */
242: { NULL, posts_fo }, /* Fo */
243: { NULL, NULL }, /* Fc */
244: { NULL, NULL }, /* Oo */
245: { NULL, NULL }, /* Oc */
246: { NULL, posts_wline }, /* Bk */
247: { NULL, NULL }, /* Ek */
1.59 schwarze 248: { NULL, posts_eoln }, /* Bt */
1.1 kristaps 249: { NULL, NULL }, /* Hf */
250: { NULL, NULL }, /* Fr */
1.59 schwarze 251: { NULL, posts_eoln }, /* Ud */
252: { NULL, posts_lb }, /* Lb */
1.31 schwarze 253: { NULL, posts_notext }, /* Lp */
1.38 schwarze 254: { NULL, posts_text }, /* Lk */
1.1 kristaps 255: { NULL, posts_text }, /* Mt */
256: { NULL, posts_wline }, /* Brq */
257: { NULL, NULL }, /* Bro */
258: { NULL, NULL }, /* Brc */
259: { NULL, posts_text }, /* %C */
260: { NULL, NULL }, /* Es */
261: { NULL, NULL }, /* En */
262: { NULL, NULL }, /* Dx */
263: { NULL, posts_text }, /* %Q */
1.31 schwarze 264: { NULL, posts_notext }, /* br */
265: { NULL, posts_sp }, /* sp */
1.38 schwarze 266: { NULL, posts_text1 }, /* %U */
1.60 ! schwarze 267: { NULL, NULL }, /* Ta */
1.1 kristaps 268: };
269:
270:
271: int
1.60 ! schwarze 272: mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
1.1 kristaps 273: {
274: v_pre *p;
275: int line, pos;
276: const char *tp;
277:
278: if (MDOC_TEXT == n->type) {
279: tp = n->string;
280: line = n->line;
281: pos = n->pos;
282: return(check_text(mdoc, line, pos, tp));
283: }
284:
285: if ( ! check_args(mdoc, n))
286: return(0);
287: if (NULL == mdoc_valids[n->tok].pre)
288: return(1);
289: for (p = mdoc_valids[n->tok].pre; *p; p++)
290: if ( ! (*p)(mdoc, n))
291: return(0);
292: return(1);
293: }
294:
295:
296: int
297: mdoc_valid_post(struct mdoc *mdoc)
298: {
299: v_post *p;
300:
301: if (MDOC_VALID & mdoc->last->flags)
302: return(1);
303: mdoc->last->flags |= MDOC_VALID;
304:
305: if (MDOC_TEXT == mdoc->last->type)
306: return(1);
307: if (MDOC_ROOT == mdoc->last->type)
308: return(post_root(mdoc));
309:
310: if (NULL == mdoc_valids[mdoc->last->tok].post)
311: return(1);
312: for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
313: if ( ! (*p)(mdoc))
314: return(0);
315:
316: return(1);
317: }
318:
319:
320: static inline int
321: warn_count(struct mdoc *m, const char *k,
322: int want, const char *v, int has)
323: {
324:
1.55 schwarze 325: return(mdoc_vmsg(m, MANDOCERR_ARGCOUNT,
326: m->last->line, m->last->pos,
327: "%s %s %d (have %d)", v, k, want, has));
1.1 kristaps 328: }
329:
330:
331: static inline int
332: err_count(struct mdoc *m, const char *k,
333: int want, const char *v, int has)
334: {
335:
1.55 schwarze 336: mdoc_vmsg(m, MANDOCERR_SYNTARGCOUNT,
337: m->last->line, m->last->pos,
338: "%s %s %d (have %d)",
339: v, k, want, has);
340: return(0);
1.1 kristaps 341: }
342:
343:
344: /*
345: * Build these up with macros because they're basically the same check
346: * for different inequalities. Yes, this could be done with functions,
347: * but this is reasonable for now.
348: */
349:
350: #define CHECK_CHILD_DEFN(lvl, name, ineq) \
351: static int \
352: lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz) \
353: { \
1.12 schwarze 354: if (mdoc->last->nchild ineq sz) \
1.1 kristaps 355: return(1); \
1.12 schwarze 356: return(lvl##_count(mdoc, #ineq, sz, p, mdoc->last->nchild)); \
1.1 kristaps 357: }
358:
359: #define CHECK_BODY_DEFN(name, lvl, func, num) \
360: static int \
361: b##lvl##_##name(POST_ARGS) \
362: { \
363: if (MDOC_BODY != mdoc->last->type) \
364: return(1); \
365: return(func(mdoc, "multi-line arguments", (num))); \
366: }
367:
368: #define CHECK_ELEM_DEFN(name, lvl, func, num) \
369: static int \
370: e##lvl##_##name(POST_ARGS) \
371: { \
372: assert(MDOC_ELEM == mdoc->last->type); \
373: return(func(mdoc, "line arguments", (num))); \
374: }
375:
376: #define CHECK_HEAD_DEFN(name, lvl, func, num) \
377: static int \
378: h##lvl##_##name(POST_ARGS) \
379: { \
380: if (MDOC_HEAD != mdoc->last->type) \
381: return(1); \
382: return(func(mdoc, "line arguments", (num))); \
383: }
384:
385:
386: CHECK_CHILD_DEFN(warn, gt, >) /* warn_child_gt() */
387: CHECK_CHILD_DEFN(err, gt, >) /* err_child_gt() */
388: CHECK_CHILD_DEFN(warn, eq, ==) /* warn_child_eq() */
389: CHECK_CHILD_DEFN(err, eq, ==) /* err_child_eq() */
390: CHECK_CHILD_DEFN(err, lt, <) /* err_child_lt() */
391: CHECK_CHILD_DEFN(warn, lt, <) /* warn_child_lt() */
392: CHECK_BODY_DEFN(ge1, warn, warn_child_gt, 0) /* bwarn_ge1() */
1.25 schwarze 393: CHECK_BODY_DEFN(ge1, err, err_child_gt, 0) /* berr_ge1() */
1.59 schwarze 394: CHECK_ELEM_DEFN(ge1, warn, warn_child_gt, 0) /* ewarn_ge1() */
1.1 kristaps 395: CHECK_ELEM_DEFN(eq1, err, err_child_eq, 1) /* eerr_eq1() */
1.37 schwarze 396: CHECK_ELEM_DEFN(le1, err, err_child_lt, 2) /* eerr_le1() */
1.1 kristaps 397: CHECK_ELEM_DEFN(eq0, err, err_child_eq, 0) /* eerr_eq0() */
398: CHECK_ELEM_DEFN(ge1, err, err_child_gt, 0) /* eerr_ge1() */
399: CHECK_HEAD_DEFN(eq0, err, err_child_eq, 0) /* herr_eq0() */
400: CHECK_HEAD_DEFN(le1, warn, warn_child_lt, 2) /* hwarn_le1() */
401: CHECK_HEAD_DEFN(ge1, err, err_child_gt, 0) /* herr_ge1() */
402: CHECK_HEAD_DEFN(eq1, warn, warn_child_eq, 1) /* hwarn_eq1() */
1.48 schwarze 403: CHECK_HEAD_DEFN(eq0, warn, warn_child_eq, 0) /* hwarn_eq0() */
1.1 kristaps 404:
405:
406: static int
407: check_stdarg(PRE_ARGS)
408: {
409:
410: if (n->args && 1 == n->args->argc)
411: if (MDOC_Std == n->args->argv[0].arg)
412: return(1);
1.55 schwarze 413: return(mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV));
1.1 kristaps 414: }
415:
416:
417: static int
418: check_args(struct mdoc *m, const struct mdoc_node *n)
419: {
420: int i;
421:
422: if (NULL == n->args)
423: return(1);
424:
425: assert(n->args->argc);
426: for (i = 0; i < (int)n->args->argc; i++)
427: if ( ! check_argv(m, n, &n->args->argv[i]))
428: return(0);
429:
430: return(1);
431: }
432:
433:
434: static int
435: check_argv(struct mdoc *m, const struct mdoc_node *n,
436: const struct mdoc_argv *v)
437: {
438: int i;
439:
440: for (i = 0; i < (int)v->sz; i++)
441: if ( ! check_text(m, v->line, v->pos, v->value[i]))
442: return(0);
443:
444: if (MDOC_Std == v->arg) {
445: if (v->sz || m->meta.name)
446: return(1);
1.55 schwarze 447: if ( ! mdoc_nmsg(m, n, MANDOCERR_NONAME))
448: return(0);
1.1 kristaps 449: }
450:
451: return(1);
452: }
453:
454:
455: static int
456: check_text(struct mdoc *mdoc, int line, int pos, const char *p)
457: {
1.15 schwarze 458: int c;
1.1 kristaps 459:
1.15 schwarze 460: for ( ; *p; p++, pos++) {
1.1 kristaps 461: if ('\t' == *p) {
462: if ( ! (MDOC_LITERAL & mdoc->flags))
1.55 schwarze 463: if ( ! mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADCHAR))
1.1 kristaps 464: return(0);
1.58 schwarze 465: } else if ( ! isprint((u_char)*p) && ASCII_HYPH != *p)
1.55 schwarze 466: if ( ! mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADCHAR))
1.1 kristaps 467: return(0);
468:
469: if ('\\' != *p)
470: continue;
471:
1.15 schwarze 472: c = mandoc_special(p);
1.1 kristaps 473: if (c) {
1.15 schwarze 474: p += c - 1;
475: pos += c - 1;
1.1 kristaps 476: continue;
477: }
1.55 schwarze 478:
479: c = mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADESCAPE);
480: if ( ! (MDOC_IGN_ESCAPE & mdoc->pflags) && ! c)
481: return(c);
1.1 kristaps 482: }
483:
484: return(1);
485: }
486:
487:
488:
489:
490: static int
1.44 schwarze 491: check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
1.1 kristaps 492: {
493:
494: assert(n->parent);
495: if ((MDOC_ROOT == t || tok == n->parent->tok) &&
496: (t == n->parent->type))
497: return(1);
498:
1.55 schwarze 499: mdoc_vmsg(mdoc, MANDOCERR_SYNTCHILD,
500: n->line, n->pos, "want parent %s",
501: MDOC_ROOT == t ? "<root>" :
502: mdoc_macronames[tok]);
503: return(0);
1.1 kristaps 504: }
505:
506:
507:
508: static int
509: pre_display(PRE_ARGS)
510: {
511: struct mdoc_node *node;
512:
513: /* Display elements (`Bd', `D1'...) cannot be nested. */
514:
515: if (MDOC_BLOCK != n->type)
516: return(1);
517:
518: /* LINTED */
519: for (node = mdoc->last->parent; node; node = node->parent)
520: if (MDOC_BLOCK == node->type)
521: if (MDOC_Bd == node->tok)
522: break;
523: if (NULL == node)
524: return(1);
525:
1.55 schwarze 526: mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
527: return(0);
1.1 kristaps 528: }
529:
530:
531: static int
532: pre_bl(PRE_ARGS)
533: {
1.60 ! schwarze 534: int i, width, offs, cmpt, dupl;
! 535: enum mdoc_list lt;
1.1 kristaps 536:
1.60 ! schwarze 537: if (MDOC_BLOCK != n->type) {
! 538: assert(n->parent);
! 539: assert(MDOC_BLOCK == n->parent->type);
! 540: assert(MDOC_Bl == n->parent->tok);
! 541: assert(LIST__NONE != n->parent->data.list);
! 542: n->data.list = n->parent->data.list;
1.1 kristaps 543: return(1);
1.55 schwarze 544: }
1.1 kristaps 545:
1.60 ! schwarze 546: /*
! 547: * First figure out which kind of list to use: bind ourselves to
! 548: * the first mentioned list type and warn about any remaining
! 549: * ones. If we find no list type, we default to LIST_item.
! 550: */
1.1 kristaps 551:
1.60 ! schwarze 552: assert(LIST__NONE == n->data.list);
! 553: offs = width = cmpt = -1;
1.1 kristaps 554:
555: /* LINTED */
1.60 ! schwarze 556: for (i = 0; n->args && i < (int)n->args->argc; i++) {
! 557: lt = LIST__NONE;
! 558: dupl = 0;
! 559: switch (n->args->argv[i].arg) {
! 560: /* Set list types. */
1.1 kristaps 561: case (MDOC_Bullet):
1.60 ! schwarze 562: lt = LIST_bullet;
! 563: break;
1.1 kristaps 564: case (MDOC_Dash):
1.60 ! schwarze 565: lt = LIST_dash;
! 566: break;
1.1 kristaps 567: case (MDOC_Enum):
1.60 ! schwarze 568: lt = LIST_enum;
! 569: break;
1.1 kristaps 570: case (MDOC_Hyphen):
1.60 ! schwarze 571: lt = LIST_hyphen;
! 572: break;
1.1 kristaps 573: case (MDOC_Item):
1.60 ! schwarze 574: lt = LIST_item;
! 575: break;
1.1 kristaps 576: case (MDOC_Tag):
1.60 ! schwarze 577: lt = LIST_tag;
! 578: break;
1.1 kristaps 579: case (MDOC_Diag):
1.60 ! schwarze 580: lt = LIST_diag;
! 581: break;
1.1 kristaps 582: case (MDOC_Hang):
1.60 ! schwarze 583: lt = LIST_hang;
! 584: break;
1.1 kristaps 585: case (MDOC_Ohang):
1.60 ! schwarze 586: lt = LIST_ohang;
! 587: break;
1.1 kristaps 588: case (MDOC_Inset):
1.60 ! schwarze 589: lt = LIST_inset;
! 590: break;
1.1 kristaps 591: case (MDOC_Column):
1.60 ! schwarze 592: lt = LIST_column;
! 593: break;
! 594: /* Set list arguments. */
1.37 schwarze 595: case (MDOC_Compact):
1.60 ! schwarze 596: if (cmpt >= 0)
! 597: dupl++;
! 598: cmpt = i;
! 599: break;
1.1 kristaps 600: case (MDOC_Width):
1.37 schwarze 601: if (width >= 0)
1.60 ! schwarze 602: dupl++;
! 603: width = i;
1.5 schwarze 604: break;
1.1 kristaps 605: case (MDOC_Offset):
1.60 ! schwarze 606: if (offs >= 0)
! 607: dupl++;
! 608: offs = i;
1.1 kristaps 609: break;
610: }
611:
1.60 ! schwarze 612: /* Check: duplicate auxiliary arguments. */
! 613:
! 614: if (dupl)
! 615: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP))
! 616: return(0);
! 617:
! 618: /* Check: multiple list types. */
! 619:
! 620: if (LIST__NONE != lt && n->data.list != LIST__NONE)
! 621: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP))
! 622: return(0);
! 623:
! 624: /* Assign list type. */
! 625:
! 626: if (LIST__NONE != lt && n->data.list == LIST__NONE)
! 627: n->data.list = lt;
! 628:
! 629: /* The list type should come first. */
! 630:
! 631: if (n->data.list == LIST__NONE)
! 632: if (width >= 0 || offs >= 0 || cmpt >= 0)
! 633: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST))
! 634: return(0);
! 635:
! 636: continue;
! 637: }
! 638:
! 639: /* Allow lists to default to LIST_item. */
! 640:
! 641: if (LIST__NONE == n->data.list) {
! 642: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE))
! 643: return(0);
! 644: n->data.list = LIST_item;
1.55 schwarze 645: }
1.1 kristaps 646:
1.5 schwarze 647: /*
648: * Validate the width field. Some list types don't need width
649: * types and should be warned about them. Others should have it
650: * and must also be warned.
651: */
652:
1.60 ! schwarze 653: switch (n->data.list) {
! 654: case (LIST_tag):
! 655: if (width >= 0)
! 656: break;
! 657: if (mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG))
! 658: break;
! 659: return(0);
! 660: case (LIST_column):
1.1 kristaps 661: /* FALLTHROUGH */
1.60 ! schwarze 662: case (LIST_diag):
1.1 kristaps 663: /* FALLTHROUGH */
1.60 ! schwarze 664: case (LIST_ohang):
1.40 schwarze 665: /* FALLTHROUGH */
1.60 ! schwarze 666: case (LIST_inset):
1.1 kristaps 667: /* FALLTHROUGH */
1.60 ! schwarze 668: case (LIST_item):
! 669: if (width < 0)
! 670: break;
! 671: if (mdoc_nmsg(mdoc, n, MANDOCERR_WIDTHARG))
! 672: break;
! 673: return(0);
1.5 schwarze 674: default:
675: break;
676: }
677:
1.1 kristaps 678: return(1);
679: }
680:
681:
682: static int
683: pre_bd(PRE_ARGS)
684: {
685: int i, type, err;
686:
687: if (MDOC_BLOCK != n->type)
688: return(1);
1.55 schwarze 689: if (NULL == n->args) {
690: mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
691: return(0);
692: }
1.1 kristaps 693:
694: /* Make sure that only one type of display is specified. */
695:
696: /* LINTED */
697: for (i = 0, err = type = 0; ! err &&
698: i < (int)n->args->argc; i++)
699: switch (n->args->argv[i].arg) {
1.38 schwarze 700: case (MDOC_Centred):
701: /* FALLTHROUGH */
1.1 kristaps 702: case (MDOC_Ragged):
703: /* FALLTHROUGH */
704: case (MDOC_Unfilled):
705: /* FALLTHROUGH */
706: case (MDOC_Filled):
707: /* FALLTHROUGH */
708: case (MDOC_Literal):
709: if (0 == type++)
710: break;
1.55 schwarze 711: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP))
712: return(0);
713: break;
1.1 kristaps 714: default:
715: break;
716: }
717:
718: if (type)
719: return(1);
1.55 schwarze 720: mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
721: return(0);
1.1 kristaps 722: }
723:
724:
725: static int
726: pre_ss(PRE_ARGS)
727: {
728:
729: if (MDOC_BLOCK != n->type)
730: return(1);
731: return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
732: }
733:
734:
735: static int
736: pre_sh(PRE_ARGS)
737: {
738:
739: if (MDOC_BLOCK != n->type)
740: return(1);
1.50 schwarze 741: return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
1.1 kristaps 742: }
743:
744:
745: static int
746: pre_it(PRE_ARGS)
747: {
748:
749: if (MDOC_BLOCK != n->type)
750: return(1);
1.55 schwarze 751: /*
752: * FIXME: this can probably be lifted if we make the It into
753: * something else on-the-fly?
754: */
1.1 kristaps 755: return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
756: }
757:
758:
759: static int
760: pre_an(PRE_ARGS)
761: {
762:
763: if (NULL == n->args || 1 == n->args->argc)
764: return(1);
1.55 schwarze 765: mdoc_vmsg(mdoc, MANDOCERR_SYNTARGCOUNT,
766: n->line, n->pos,
767: "line arguments == 1 (have %d)",
768: n->args->argc);
769: return(0);
1.1 kristaps 770: }
771:
772:
773: static int
774: pre_rv(PRE_ARGS)
775: {
776:
777: return(check_stdarg(mdoc, n));
778: }
779:
780:
781: static int
1.60 ! schwarze 782: post_dt(POST_ARGS)
! 783: {
! 784: const struct mdoc_node *nn;
! 785: const char *p;
! 786:
! 787: if (NULL != (nn = mdoc->last->child))
! 788: for (p = nn->string; *p; p++) {
! 789: if (toupper((u_char)*p) == *p)
! 790: continue;
! 791: if ( ! mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE))
! 792: return(0);
! 793: break;
! 794: }
! 795:
! 796: return(1);
! 797: }
! 798:
! 799:
! 800: static int
1.1 kristaps 801: pre_dt(PRE_ARGS)
802: {
1.40 schwarze 803:
1.1 kristaps 804: if (0 == mdoc->meta.date || mdoc->meta.os)
1.55 schwarze 805: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO))
1.1 kristaps 806: return(0);
807: if (mdoc->meta.title)
1.55 schwarze 808: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP))
1.1 kristaps 809: return(0);
810: return(1);
811: }
812:
813:
814: static int
815: pre_os(PRE_ARGS)
816: {
817:
818: if (NULL == mdoc->meta.title || 0 == mdoc->meta.date)
1.55 schwarze 819: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO))
1.1 kristaps 820: return(0);
821: if (mdoc->meta.os)
1.55 schwarze 822: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP))
1.1 kristaps 823: return(0);
824: return(1);
825: }
826:
827:
828: static int
829: pre_dd(PRE_ARGS)
830: {
831:
832: if (mdoc->meta.title || mdoc->meta.os)
1.55 schwarze 833: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO))
1.1 kristaps 834: return(0);
835: if (mdoc->meta.date)
1.55 schwarze 836: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP))
1.1 kristaps 837: return(0);
838: return(1);
839: }
840:
841:
842: static int
843: post_bf(POST_ARGS)
844: {
845: char *p;
846: struct mdoc_node *head;
847:
848: if (MDOC_BLOCK != mdoc->last->type)
849: return(1);
850:
851: head = mdoc->last->head;
852:
1.55 schwarze 853: if (mdoc->last->args && head->child) {
854: /* FIXME: this should provide a default. */
855: mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT);
856: return(0);
857: } else if (mdoc->last->args)
1.10 schwarze 858: return(1);
859:
1.55 schwarze 860: if (NULL == head->child || MDOC_TEXT != head->child->type) {
861: /* FIXME: this should provide a default. */
862: mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT);
863: return(0);
864: }
1.1 kristaps 865:
1.10 schwarze 866: p = head->child->string;
1.1 kristaps 867:
1.10 schwarze 868: if (0 == strcmp(p, "Em"))
869: return(1);
870: else if (0 == strcmp(p, "Li"))
871: return(1);
1.30 schwarze 872: else if (0 == strcmp(p, "Sy"))
1.10 schwarze 873: return(1);
1.1 kristaps 874:
1.55 schwarze 875: mdoc_nmsg(mdoc, head, MANDOCERR_FONTTYPE);
876: return(0);
1.27 schwarze 877: }
878:
879:
880: static int
881: post_lb(POST_ARGS)
882: {
883:
884: if (mdoc_a2lib(mdoc->last->child->string))
885: return(1);
1.55 schwarze 886: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADLIB));
1.59 schwarze 887: }
888:
889:
890: static int
891: post_eoln(POST_ARGS)
892: {
893:
894: if (NULL == mdoc->last->child)
895: return(1);
896: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST));
1.42 schwarze 897: }
898:
899:
900: static int
901: post_vt(POST_ARGS)
902: {
903: const struct mdoc_node *n;
904:
905: /*
906: * The Vt macro comes in both ELEM and BLOCK form, both of which
907: * have different syntaxes (yet more context-sensitive
908: * behaviour). ELEM types must have a child; BLOCK types,
909: * specifically the BODY, should only have TEXT children.
910: */
911:
912: if (MDOC_ELEM == mdoc->last->type)
913: return(eerr_ge1(mdoc));
914: if (MDOC_BODY != mdoc->last->type)
915: return(1);
916:
917: for (n = mdoc->last->child; n; n = n->next)
1.52 schwarze 918: if (MDOC_TEXT != n->type)
1.55 schwarze 919: if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_CHILD))
1.42 schwarze 920: return(0);
921:
922: return(1);
1.1 kristaps 923: }
924:
925:
926: static int
927: post_nm(POST_ARGS)
928: {
929:
930: if (mdoc->last->child)
931: return(1);
932: if (mdoc->meta.name)
933: return(1);
1.55 schwarze 934: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME));
1.1 kristaps 935: }
936:
937:
938: static int
939: post_at(POST_ARGS)
940: {
941:
942: if (NULL == mdoc->last->child)
943: return(1);
1.55 schwarze 944: assert(MDOC_TEXT == mdoc->last->child->type);
1.1 kristaps 945: if (mdoc_a2att(mdoc->last->child->string))
946: return(1);
1.55 schwarze 947: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT));
1.1 kristaps 948: }
949:
950:
951: static int
952: post_an(POST_ARGS)
953: {
954:
955: if (mdoc->last->args) {
956: if (NULL == mdoc->last->child)
957: return(1);
1.55 schwarze 958: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGCOUNT));
1.1 kristaps 959: }
960:
961: if (mdoc->last->child)
962: return(1);
1.55 schwarze 963: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS));
1.1 kristaps 964: }
965:
966:
967: static int
968: post_it(POST_ARGS)
969: {
1.60 ! schwarze 970: int i, cols, rc;
! 971: enum mdoc_list lt;
1.1 kristaps 972: struct mdoc_node *n, *c;
1.60 ! schwarze 973: enum mandocerr er;
1.1 kristaps 974:
975: if (MDOC_BLOCK != mdoc->last->type)
976: return(1);
977:
978: n = mdoc->last->parent->parent;
1.60 ! schwarze 979: lt = n->data.list;
! 980:
! 981: if (LIST__NONE == lt) {
1.55 schwarze 982: mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
983: return(0);
984: }
1.1 kristaps 985:
1.60 ! schwarze 986: switch (lt) {
! 987: case (LIST_tag):
! 988: if (mdoc->last->head->child)
1.1 kristaps 989: break;
1.60 ! schwarze 990: /* FIXME: give this a dummy value. */
! 991: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS))
! 992: return(0);
1.1 kristaps 993: break;
1.60 ! schwarze 994: case (LIST_hang):
1.1 kristaps 995: /* FALLTHROUGH */
1.60 ! schwarze 996: case (LIST_ohang):
1.1 kristaps 997: /* FALLTHROUGH */
1.60 ! schwarze 998: case (LIST_inset):
1.1 kristaps 999: /* FALLTHROUGH */
1.60 ! schwarze 1000: case (LIST_diag):
1.1 kristaps 1001: if (NULL == mdoc->last->head->child)
1.55 schwarze 1002: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS))
1.1 kristaps 1003: return(0);
1004: if (NULL == mdoc->last->body->child)
1.55 schwarze 1005: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
1.1 kristaps 1006: return(0);
1007: break;
1.60 ! schwarze 1008: case (LIST_bullet):
1.1 kristaps 1009: /* FALLTHROUGH */
1.60 ! schwarze 1010: case (LIST_dash):
1.1 kristaps 1011: /* FALLTHROUGH */
1.60 ! schwarze 1012: case (LIST_enum):
1.1 kristaps 1013: /* FALLTHROUGH */
1.60 ! schwarze 1014: case (LIST_hyphen):
1.1 kristaps 1015: /* FALLTHROUGH */
1.60 ! schwarze 1016: case (LIST_item):
1.1 kristaps 1017: if (mdoc->last->head->child)
1.55 schwarze 1018: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST))
1.1 kristaps 1019: return(0);
1020: if (NULL == mdoc->last->body->child)
1.55 schwarze 1021: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
1.1 kristaps 1022: return(0);
1023: break;
1.60 ! schwarze 1024: case (LIST_column):
! 1025: cols = -1;
! 1026: for (i = 0; i < (int)n->args->argc; i++)
! 1027: if (MDOC_Column == n->args->argv[i].arg) {
! 1028: cols = (int)n->args->argv[i].sz;
! 1029: break;
! 1030: }
! 1031:
! 1032: assert(-1 != cols);
! 1033: assert(NULL == mdoc->last->head->child);
! 1034:
! 1035: if (NULL == mdoc->last->body->child)
! 1036: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
1.32 schwarze 1037: return(0);
1.60 ! schwarze 1038:
! 1039: for (i = 0, c = mdoc->last->child; c; c = c->next)
! 1040: if (MDOC_BODY == c->type)
! 1041: i++;
! 1042:
! 1043: if (i < cols)
! 1044: er = MANDOCERR_ARGCOUNT;
! 1045: else if (i == cols || i == cols + 1)
1.1 kristaps 1046: break;
1.60 ! schwarze 1047: else
! 1048: er = MANDOCERR_SYNTARGCOUNT;
1.32 schwarze 1049:
1.60 ! schwarze 1050: rc = mdoc_vmsg(mdoc, er,
1.55 schwarze 1051: mdoc->last->line, mdoc->last->pos,
1052: "columns == %d (have %d)", cols, i);
1.60 ! schwarze 1053: return(rc);
1.1 kristaps 1054: default:
1055: break;
1056: }
1057:
1058: return(1);
1059: }
1060:
1061:
1062: static int
1.11 schwarze 1063: post_bl_head(POST_ARGS)
1064: {
1.60 ! schwarze 1065: int i;
! 1066: struct mdoc_node *n;
1.11 schwarze 1067:
1.60 ! schwarze 1068: assert(mdoc->last->parent);
1.11 schwarze 1069: n = mdoc->last->parent;
1070:
1.60 ! schwarze 1071: if (LIST_column == n->data.list) {
! 1072: for (i = 0; i < (int)n->args->argc; i++)
! 1073: if (MDOC_Column == n->args->argv[i].arg)
! 1074: break;
! 1075: assert(i < (int)n->args->argc);
! 1076:
! 1077: if (n->args->argv[i].sz && mdoc->last->nchild) {
! 1078: mdoc_nmsg(mdoc, n, MANDOCERR_COLUMNS);
! 1079: return(0);
1.48 schwarze 1080: }
1.60 ! schwarze 1081: return(1);
1.48 schwarze 1082: }
1.11 schwarze 1083:
1.48 schwarze 1084: if (0 == (i = mdoc->last->nchild))
1.11 schwarze 1085: return(1);
1.48 schwarze 1086: return(warn_count(mdoc, "==", 0, "line arguments", i));
1.11 schwarze 1087: }
1088:
1089:
1090: static int
1.1 kristaps 1091: post_bl(POST_ARGS)
1092: {
1093: struct mdoc_node *n;
1094:
1.11 schwarze 1095: if (MDOC_HEAD == mdoc->last->type)
1096: return(post_bl_head(mdoc));
1.1 kristaps 1097: if (MDOC_BODY != mdoc->last->type)
1098: return(1);
1099: if (NULL == mdoc->last->child)
1100: return(1);
1101:
1.41 schwarze 1102: /*
1103: * We only allow certain children of `Bl'. This is usually on
1104: * `It', but apparently `Sm' occurs here and there, so we let
1105: * that one through, too.
1106: */
1107:
1.1 kristaps 1108: /* LINTED */
1109: for (n = mdoc->last->child; n; n = n->next) {
1.41 schwarze 1110: if (MDOC_BLOCK == n->type && MDOC_It == n->tok)
1111: continue;
1112: if (MDOC_Sm == n->tok)
1113: continue;
1.55 schwarze 1114: mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1115: return(0);
1.1 kristaps 1116: }
1117:
1118: return(1);
1119: }
1120:
1121:
1122: static int
1123: ebool(struct mdoc *mdoc)
1124: {
1125: struct mdoc_node *n;
1126:
1127: /* LINTED */
1128: for (n = mdoc->last->child; n; n = n->next) {
1129: if (MDOC_TEXT != n->type)
1130: break;
1131: if (0 == strcmp(n->string, "on"))
1132: continue;
1133: if (0 == strcmp(n->string, "off"))
1134: continue;
1135: break;
1136: }
1137:
1138: if (NULL == n)
1139: return(1);
1.55 schwarze 1140: return(mdoc_nmsg(mdoc, n, MANDOCERR_BADBOOL));
1.1 kristaps 1141: }
1142:
1143:
1144: static int
1145: post_root(POST_ARGS)
1146: {
1147:
1148: if (NULL == mdoc->first->child)
1.55 schwarze 1149: mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY);
1150: else if ( ! (MDOC_PBODY & mdoc->flags))
1151: mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1152: else if (MDOC_BLOCK != mdoc->first->child->type)
1153: mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY);
1154: else if (MDOC_Sh != mdoc->first->child->tok)
1155: mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY);
1156: else
1157: return(1);
1.1 kristaps 1158:
1.55 schwarze 1159: return(0);
1.1 kristaps 1160: }
1161:
1162:
1163: static int
1164: post_st(POST_ARGS)
1165: {
1166:
1167: if (mdoc_a2st(mdoc->last->child->string))
1168: return(1);
1.55 schwarze 1169: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD));
1.36 schwarze 1170: }
1171:
1172:
1173: static int
1174: post_rs(POST_ARGS)
1175: {
1176: struct mdoc_node *nn;
1177:
1178: if (MDOC_BODY != mdoc->last->type)
1179: return(1);
1180:
1181: for (nn = mdoc->last->child; nn; nn = nn->next)
1182: switch (nn->tok) {
1.38 schwarze 1183: case(MDOC__U):
1184: /* FALLTHROUGH */
1.36 schwarze 1185: case(MDOC__Q):
1186: /* FALLTHROUGH */
1187: case(MDOC__C):
1188: /* FALLTHROUGH */
1189: case(MDOC__A):
1190: /* FALLTHROUGH */
1191: case(MDOC__B):
1192: /* FALLTHROUGH */
1193: case(MDOC__D):
1194: /* FALLTHROUGH */
1195: case(MDOC__I):
1196: /* FALLTHROUGH */
1197: case(MDOC__J):
1198: /* FALLTHROUGH */
1199: case(MDOC__N):
1200: /* FALLTHROUGH */
1201: case(MDOC__O):
1202: /* FALLTHROUGH */
1203: case(MDOC__P):
1204: /* FALLTHROUGH */
1205: case(MDOC__R):
1206: /* FALLTHROUGH */
1207: case(MDOC__T):
1208: /* FALLTHROUGH */
1209: case(MDOC__V):
1210: break;
1211: default:
1.55 schwarze 1212: mdoc_nmsg(mdoc, nn, MANDOCERR_SYNTCHILD);
1213: return(0);
1.36 schwarze 1214: }
1215:
1216: return(1);
1.1 kristaps 1217: }
1218:
1219:
1220: static int
1221: post_sh(POST_ARGS)
1222: {
1223:
1224: if (MDOC_HEAD == mdoc->last->type)
1225: return(post_sh_head(mdoc));
1226: if (MDOC_BODY == mdoc->last->type)
1227: return(post_sh_body(mdoc));
1228:
1229: return(1);
1230: }
1231:
1232:
1233: static int
1234: post_sh_body(POST_ARGS)
1235: {
1236: struct mdoc_node *n;
1237:
1.26 schwarze 1238: if (SEC_NAME != mdoc->lastsec)
1.1 kristaps 1239: return(1);
1240:
1241: /*
1242: * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1243: * macros (can have multiple `Nm' and one `Nd'). Note that the
1244: * children of the BODY declaration can also be "text".
1245: */
1246:
1247: if (NULL == (n = mdoc->last->child))
1.55 schwarze 1248: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC));
1.1 kristaps 1249:
1250: for ( ; n && n->next; n = n->next) {
1251: if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1252: continue;
1253: if (MDOC_TEXT == n->type)
1254: continue;
1.55 schwarze 1255: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC))
1.1 kristaps 1256: return(0);
1257: }
1258:
1.34 schwarze 1259: assert(n);
1.25 schwarze 1260: if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1.1 kristaps 1261: return(1);
1.55 schwarze 1262: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC));
1.1 kristaps 1263: }
1264:
1265:
1266: static int
1267: post_sh_head(POST_ARGS)
1268: {
1.57 schwarze 1269: char buf[BUFSIZ];
1.1 kristaps 1270: enum mdoc_sec sec;
1271: const struct mdoc_node *n;
1272:
1273: /*
1274: * Process a new section. Sections are either "named" or
1275: * "custom"; custom sections are user-defined, while named ones
1276: * usually follow a conventional order and may only appear in
1277: * certain manual sections.
1278: */
1279:
1.55 schwarze 1280: buf[0] = '\0';
1281:
1282: /*
1283: * FIXME: yes, these can use a dynamic buffer, but I don't do so
1284: * in the interests of simplicity.
1285: */
1.1 kristaps 1286:
1287: for (n = mdoc->last->child; n; n = n->next) {
1.9 schwarze 1288: /* XXX - copied from compact(). */
1.1 kristaps 1289: assert(MDOC_TEXT == n->type);
1.9 schwarze 1290:
1.57 schwarze 1291: if (strlcat(buf, n->string, BUFSIZ) >= BUFSIZ) {
1.55 schwarze 1292: mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
1293: return(0);
1294: }
1.1 kristaps 1295: if (NULL == n->next)
1296: continue;
1.57 schwarze 1297: if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
1.55 schwarze 1298: mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
1299: return(0);
1300: }
1.1 kristaps 1301: }
1302:
1.51 schwarze 1303: sec = mdoc_str2sec(buf);
1.1 kristaps 1304:
1.9 schwarze 1305: /*
1306: * Check: NAME should always be first, CUSTOM has no roles,
1307: * non-CUSTOM has a conventional order to be followed.
1308: */
1.1 kristaps 1309:
1.51 schwarze 1310: if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1.55 schwarze 1311: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST))
1.51 schwarze 1312: return(0);
1313:
1.1 kristaps 1314: if (SEC_CUSTOM == sec)
1315: return(1);
1.51 schwarze 1316:
1.1 kristaps 1317: if (sec == mdoc->lastnamed)
1.55 schwarze 1318: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP))
1.23 schwarze 1319: return(0);
1.51 schwarze 1320:
1.1 kristaps 1321: if (sec < mdoc->lastnamed)
1.55 schwarze 1322: if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO))
1.23 schwarze 1323: return(0);
1.1 kristaps 1324:
1.9 schwarze 1325: /*
1326: * Check particular section/manual conventions. LIBRARY can
1.54 schwarze 1327: * only occur in manual section 2, 3, and 9.
1.9 schwarze 1328: */
1.1 kristaps 1329:
1330: switch (sec) {
1331: case (SEC_LIBRARY):
1.54 schwarze 1332: assert(mdoc->meta.msec);
1333: if (*mdoc->meta.msec == '2')
1334: break;
1335: if (*mdoc->meta.msec == '3')
1336: break;
1337: if (*mdoc->meta.msec == '9')
1.1 kristaps 1338: break;
1.55 schwarze 1339: return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC));
1.1 kristaps 1340: default:
1341: break;
1342: }
1343:
1344: return(1);
1345: }