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