Bug Summary

File:src/usr.bin/mandoc/mdoc_validate.c
Warning:line 786, column 35
Access to field 'line' results in a dereference of a null pointer (loaded from variable 'wa')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name mdoc_validate.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/mandoc/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -Wno-unused-parameter -fdebug-compilation-dir=/usr/src/usr.bin/mandoc/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/mandoc/mdoc_validate.c
1/* $OpenBSD: mdoc_validate.c,v 1.305 2021/10/04 14:18:42 schwarze Exp $ */
2/*
3 * Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Validation module for mdoc(7) syntax trees used by mandoc(1).
20 */
21#include <sys/types.h>
22#ifndef OSNAME
23#include <sys/utsname.h>
24#endif
25
26#include <assert.h>
27#include <ctype.h>
28#include <limits.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <time.h>
33
34#include "mandoc_aux.h"
35#include "mandoc.h"
36#include "mandoc_xr.h"
37#include "roff.h"
38#include "mdoc.h"
39#include "libmandoc.h"
40#include "roff_int.h"
41#include "libmdoc.h"
42#include "tag.h"
43
44/* FIXME: .Bl -diag can't have non-text children in HEAD. */
45
46#define POST_ARGSstruct roff_man *mdoc struct roff_man *mdoc
47
48enum check_ineq {
49 CHECK_LT,
50 CHECK_GT,
51 CHECK_EQ
52};
53
54typedef void (*v_post)(POST_ARGSstruct roff_man *mdoc);
55
56static int build_list(struct roff_man *, int);
57static void check_argv(struct roff_man *,
58 struct roff_node *, struct mdoc_argv *);
59static void check_args(struct roff_man *, struct roff_node *);
60static void check_text(struct roff_man *, int, int, char *);
61static void check_text_em(struct roff_man *, int, int, char *);
62static void check_toptext(struct roff_man *, int, int, const char *);
63static int child_an(const struct roff_node *);
64static size_t macro2len(enum roff_tok);
65static void rewrite_macro2len(struct roff_man *, char **);
66static int similar(const char *, const char *);
67
68static void post_abort(POST_ARGSstruct roff_man *mdoc) __attribute__((__noreturn__));
69static void post_an(POST_ARGSstruct roff_man *mdoc);
70static void post_an_norm(POST_ARGSstruct roff_man *mdoc);
71static void post_at(POST_ARGSstruct roff_man *mdoc);
72static void post_bd(POST_ARGSstruct roff_man *mdoc);
73static void post_bf(POST_ARGSstruct roff_man *mdoc);
74static void post_bk(POST_ARGSstruct roff_man *mdoc);
75static void post_bl(POST_ARGSstruct roff_man *mdoc);
76static void post_bl_block(POST_ARGSstruct roff_man *mdoc);
77static void post_bl_head(POST_ARGSstruct roff_man *mdoc);
78static void post_bl_norm(POST_ARGSstruct roff_man *mdoc);
79static void post_bx(POST_ARGSstruct roff_man *mdoc);
80static void post_defaults(POST_ARGSstruct roff_man *mdoc);
81static void post_display(POST_ARGSstruct roff_man *mdoc);
82static void post_dd(POST_ARGSstruct roff_man *mdoc);
83static void post_delim(POST_ARGSstruct roff_man *mdoc);
84static void post_delim_nb(POST_ARGSstruct roff_man *mdoc);
85static void post_dt(POST_ARGSstruct roff_man *mdoc);
86static void post_em(POST_ARGSstruct roff_man *mdoc);
87static void post_en(POST_ARGSstruct roff_man *mdoc);
88static void post_er(POST_ARGSstruct roff_man *mdoc);
89static void post_es(POST_ARGSstruct roff_man *mdoc);
90static void post_eoln(POST_ARGSstruct roff_man *mdoc);
91static void post_ex(POST_ARGSstruct roff_man *mdoc);
92static void post_fa(POST_ARGSstruct roff_man *mdoc);
93static void post_fl(POST_ARGSstruct roff_man *mdoc);
94static void post_fn(POST_ARGSstruct roff_man *mdoc);
95static void post_fname(POST_ARGSstruct roff_man *mdoc);
96static void post_fo(POST_ARGSstruct roff_man *mdoc);
97static void post_hyph(POST_ARGSstruct roff_man *mdoc);
98static void post_it(POST_ARGSstruct roff_man *mdoc);
99static void post_lb(POST_ARGSstruct roff_man *mdoc);
100static void post_nd(POST_ARGSstruct roff_man *mdoc);
101static void post_nm(POST_ARGSstruct roff_man *mdoc);
102static void post_ns(POST_ARGSstruct roff_man *mdoc);
103static void post_obsolete(POST_ARGSstruct roff_man *mdoc);
104static void post_os(POST_ARGSstruct roff_man *mdoc);
105static void post_par(POST_ARGSstruct roff_man *mdoc);
106static void post_prevpar(POST_ARGSstruct roff_man *mdoc);
107static void post_root(POST_ARGSstruct roff_man *mdoc);
108static void post_rs(POST_ARGSstruct roff_man *mdoc);
109static void post_rv(POST_ARGSstruct roff_man *mdoc);
110static void post_section(POST_ARGSstruct roff_man *mdoc);
111static void post_sh(POST_ARGSstruct roff_man *mdoc);
112static void post_sh_head(POST_ARGSstruct roff_man *mdoc);
113static void post_sh_name(POST_ARGSstruct roff_man *mdoc);
114static void post_sh_see_also(POST_ARGSstruct roff_man *mdoc);
115static void post_sh_authors(POST_ARGSstruct roff_man *mdoc);
116static void post_sm(POST_ARGSstruct roff_man *mdoc);
117static void post_st(POST_ARGSstruct roff_man *mdoc);
118static void post_std(POST_ARGSstruct roff_man *mdoc);
119static void post_sx(POST_ARGSstruct roff_man *mdoc);
120static void post_tag(POST_ARGSstruct roff_man *mdoc);
121static void post_tg(POST_ARGSstruct roff_man *mdoc);
122static void post_useless(POST_ARGSstruct roff_man *mdoc);
123static void post_xr(POST_ARGSstruct roff_man *mdoc);
124static void post_xx(POST_ARGSstruct roff_man *mdoc);
125
126static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
127 post_dd, /* Dd */
128 post_dt, /* Dt */
129 post_os, /* Os */
130 post_sh, /* Sh */
131 post_section, /* Ss */
132 post_par, /* Pp */
133 post_display, /* D1 */
134 post_display, /* Dl */
135 post_display, /* Bd */
136 NULL((void *)0), /* Ed */
137 post_bl, /* Bl */
138 NULL((void *)0), /* El */
139 post_it, /* It */
140 post_delim_nb, /* Ad */
141 post_an, /* An */
142 NULL((void *)0), /* Ap */
143 post_defaults, /* Ar */
144 NULL((void *)0), /* Cd */
145 post_tag, /* Cm */
146 post_tag, /* Dv */
147 post_er, /* Er */
148 post_tag, /* Ev */
149 post_ex, /* Ex */
150 post_fa, /* Fa */
151 NULL((void *)0), /* Fd */
152 post_fl, /* Fl */
153 post_fn, /* Fn */
154 post_delim_nb, /* Ft */
155 post_tag, /* Ic */
156 post_delim_nb, /* In */
157 post_tag, /* Li */
158 post_nd, /* Nd */
159 post_nm, /* Nm */
160 post_delim_nb, /* Op */
161 post_abort, /* Ot */
162 post_defaults, /* Pa */
163 post_rv, /* Rv */
164 post_st, /* St */
165 post_tag, /* Va */
166 post_delim_nb, /* Vt */
167 post_xr, /* Xr */
168 NULL((void *)0), /* %A */
169 post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
170 NULL((void *)0), /* %D */
171 NULL((void *)0), /* %I */
172 NULL((void *)0), /* %J */
173 post_hyph, /* %N */
174 post_hyph, /* %O */
175 NULL((void *)0), /* %P */
176 post_hyph, /* %R */
177 post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
178 NULL((void *)0), /* %V */
179 NULL((void *)0), /* Ac */
180 NULL((void *)0), /* Ao */
181 post_delim_nb, /* Aq */
182 post_at, /* At */
183 NULL((void *)0), /* Bc */
184 post_bf, /* Bf */
185 NULL((void *)0), /* Bo */
186 NULL((void *)0), /* Bq */
187 post_xx, /* Bsx */
188 post_bx, /* Bx */
189 post_obsolete, /* Db */
190 NULL((void *)0), /* Dc */
191 NULL((void *)0), /* Do */
192 NULL((void *)0), /* Dq */
193 NULL((void *)0), /* Ec */
194 NULL((void *)0), /* Ef */
195 post_em, /* Em */
196 NULL((void *)0), /* Eo */
197 post_xx, /* Fx */
198 post_tag, /* Ms */
199 post_tag, /* No */
200 post_ns, /* Ns */
201 post_xx, /* Nx */
202 post_xx, /* Ox */
203 NULL((void *)0), /* Pc */
204 NULL((void *)0), /* Pf */
205 NULL((void *)0), /* Po */
206 post_delim_nb, /* Pq */
207 NULL((void *)0), /* Qc */
208 post_delim_nb, /* Ql */
209 NULL((void *)0), /* Qo */
210 post_delim_nb, /* Qq */
211 NULL((void *)0), /* Re */
212 post_rs, /* Rs */
213 NULL((void *)0), /* Sc */
214 NULL((void *)0), /* So */
215 post_delim_nb, /* Sq */
216 post_sm, /* Sm */
217 post_sx, /* Sx */
218 post_em, /* Sy */
219 post_useless, /* Tn */
220 post_xx, /* Ux */
221 NULL((void *)0), /* Xc */
222 NULL((void *)0), /* Xo */
223 post_fo, /* Fo */
224 NULL((void *)0), /* Fc */
225 NULL((void *)0), /* Oo */
226 NULL((void *)0), /* Oc */
227 post_bk, /* Bk */
228 NULL((void *)0), /* Ek */
229 post_eoln, /* Bt */
230 post_obsolete, /* Hf */
231 post_obsolete, /* Fr */
232 post_eoln, /* Ud */
233 post_lb, /* Lb */
234 post_abort, /* Lp */
235 post_delim_nb, /* Lk */
236 post_defaults, /* Mt */
237 post_delim_nb, /* Brq */
238 NULL((void *)0), /* Bro */
239 NULL((void *)0), /* Brc */
240 NULL((void *)0), /* %C */
241 post_es, /* Es */
242 post_en, /* En */
243 post_xx, /* Dx */
244 NULL((void *)0), /* %Q */
245 NULL((void *)0), /* %U */
246 NULL((void *)0), /* Ta */
247 post_tg, /* Tg */
248};
249
250#define RSORD_MAX14 14 /* Number of `Rs' blocks. */
251
252static const enum roff_tok rsord[RSORD_MAX14] = {
253 MDOC__A,
254 MDOC__T,
255 MDOC__B,
256 MDOC__I,
257 MDOC__J,
258 MDOC__R,
259 MDOC__N,
260 MDOC__V,
261 MDOC__U,
262 MDOC__P,
263 MDOC__Q,
264 MDOC__C,
265 MDOC__D,
266 MDOC__O
267};
268
269static const char * const secnames[SEC__MAX] = {
270 NULL((void *)0),
271 "NAME",
272 "LIBRARY",
273 "SYNOPSIS",
274 "DESCRIPTION",
275 "CONTEXT",
276 "IMPLEMENTATION NOTES",
277 "RETURN VALUES",
278 "ENVIRONMENT",
279 "FILES",
280 "EXIT STATUS",
281 "EXAMPLES",
282 "DIAGNOSTICS",
283 "COMPATIBILITY",
284 "ERRORS",
285 "SEE ALSO",
286 "STANDARDS",
287 "HISTORY",
288 "AUTHORS",
289 "CAVEATS",
290 "BUGS",
291 "SECURITY CONSIDERATIONS",
292 NULL((void *)0)
293};
294
295static int fn_prio = TAG_STRONG2;
296
297
298/* Validate the subtree rooted at mdoc->last. */
299void
300mdoc_validate(struct roff_man *mdoc)
301{
302 struct roff_node *n, *np;
303 const v_post *p;
304
305 /*
306 * Translate obsolete macros to modern macros first
307 * such that later code does not need to look
308 * for the obsolete versions.
309 */
310
311 n = mdoc->last;
312 switch (n->tok) {
313 case MDOC_Lp:
314 n->tok = MDOC_Pp;
315 break;
316 case MDOC_Ot:
317 post_obsolete(mdoc);
318 n->tok = MDOC_Ft;
319 break;
320 default:
321 break;
322 }
323
324 /*
325 * Iterate over all children, recursing into each one
326 * in turn, depth-first.
327 */
328
329 mdoc->last = mdoc->last->child;
330 while (mdoc->last != NULL((void *)0)) {
331 mdoc_validate(mdoc);
332 if (mdoc->last == n)
333 mdoc->last = mdoc->last->child;
334 else
335 mdoc->last = mdoc->last->next;
336 }
337
338 /* Finally validate the macro itself. */
339
340 mdoc->last = n;
341 mdoc->next = ROFF_NEXT_SIBLING;
342 switch (n->type) {
343 case ROFFT_TEXT:
344 np = n->parent;
345 if (n->sec != SEC_SYNOPSIS ||
346 (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
347 check_text(mdoc, n->line, n->pos, n->string);
348 if ((n->flags & NODE_NOFILL(1 << 8)) == 0 &&
349 (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
350 np->parent->parent->norm->Bl.type != LIST_diag))
351 check_text_em(mdoc, n->line, n->pos, n->string);
352 if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
353 (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
354 check_toptext(mdoc, n->line, n->pos, n->string);
355 break;
356 case ROFFT_COMMENT:
357 case ROFFT_EQN:
358 case ROFFT_TBL:
359 break;
360 case ROFFT_ROOT:
361 post_root(mdoc);
362 break;
363 default:
364 check_args(mdoc, mdoc->last);
365
366 /*
367 * Closing delimiters are not special at the
368 * beginning of a block, opening delimiters
369 * are not special at the end.
370 */
371
372 if (n->child != NULL((void *)0))
373 n->child->flags &= ~NODE_DELIMC(1 << 5);
374 if (n->last != NULL((void *)0))
375 n->last->flags &= ~NODE_DELIMO(1 << 4);
376
377 /* Call the macro's postprocessor. */
378
379 if (n->tok < ROFF_MAX) {
380 roff_validate(mdoc);
381 break;
382 }
383
384 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX)((n->tok >= MDOC_Dd && n->tok < MDOC_MAX)
? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 384, __func__, "n->tok >= MDOC_Dd && n->tok < MDOC_MAX"
))
;
385 p = mdoc_valids + (n->tok - MDOC_Dd);
386 if (*p)
387 (*p)(mdoc);
388 if (mdoc->last == n)
389 mdoc_state(mdoc, n);
390 break;
391 }
392}
393
394static void
395check_args(struct roff_man *mdoc, struct roff_node *n)
396{
397 int i;
398
399 if (NULL((void *)0) == n->args)
400 return;
401
402 assert(n->args->argc)((n->args->argc) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 402, __func__, "n->args->argc"))
;
403 for (i = 0; i < (int)n->args->argc; i++)
404 check_argv(mdoc, n, &n->args->argv[i]);
405}
406
407static void
408check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
409{
410 int i;
411
412 for (i = 0; i < (int)v->sz; i++)
413 check_text(mdoc, v->line, v->pos, v->value[i]);
414}
415
416static void
417check_text(struct roff_man *mdoc, int ln, int pos, char *p)
418{
419 char *cp;
420
421 if (mdoc->last->flags & NODE_NOFILL(1 << 8))
422 return;
423
424 for (cp = p; NULL((void *)0) != (p = strchr(p, '\t')); p++)
425 mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL((void *)0));
426}
427
428static void
429check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
430{
431 const struct roff_node *np, *nn;
432 char *cp;
433
434 np = mdoc->last->prev;
435 nn = mdoc->last->next;
436
437 /* Look for em-dashes wrongly encoded as "--". */
438
439 for (cp = p; *cp != '\0'; cp++) {
440 if (cp[0] != '-' || cp[1] != '-')
441 continue;
442 cp++;
443
444 /* Skip input sequences of more than two '-'. */
445
446 if (cp[1] == '-') {
447 while (cp[1] == '-')
448 cp++;
449 continue;
450 }
451
452 /* Skip "--" directly attached to something else. */
453
454 if ((cp - p > 1 && cp[-2] != ' ') ||
455 (cp[1] != '\0' && cp[1] != ' '))
456 continue;
457
458 /* Require a letter right before or right afterwards. */
459
460 if ((cp - p > 2 ?
461 isalpha((unsigned char)cp[-3]) :
462 np != NULL((void *)0) &&
463 np->type == ROFFT_TEXT &&
464 *np->string != '\0' &&
465 isalpha((unsigned char)np->string[
466 strlen(np->string) - 1])) ||
467 (cp[1] != '\0' && cp[2] != '\0' ?
468 isalpha((unsigned char)cp[2]) :
469 nn != NULL((void *)0) &&
470 nn->type == ROFFT_TEXT &&
471 isalpha((unsigned char)*nn->string))) {
472 mandoc_msg(MANDOCERR_DASHDASH,
473 ln, pos + (int)(cp - p) - 1, NULL((void *)0));
474 break;
475 }
476 }
477}
478
479static void
480check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
481{
482 const char *cp, *cpr;
483
484 if (*p == '\0')
485 return;
486
487 if ((cp = strstr(p, "OpenBSD")) != NULL((void *)0))
488 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
489 if ((cp = strstr(p, "NetBSD")) != NULL((void *)0))
490 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
491 if ((cp = strstr(p, "FreeBSD")) != NULL((void *)0))
492 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
493 if ((cp = strstr(p, "DragonFly")) != NULL((void *)0))
494 mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
495
496 cp = p;
497 while ((cp = strstr(cp + 1, "()")) != NULL((void *)0)) {
498 for (cpr = cp - 1; cpr >= p; cpr--)
499 if (*cpr != '_' && !isalnum((unsigned char)*cpr))
500 break;
501 if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
502 cpr++;
503 mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
504 "%.*s()", (int)(cp - cpr), cpr);
505 }
506 }
507}
508
509static void
510post_abort(POST_ARGSstruct roff_man *mdoc)
511{
512 abort();
513}
514
515static void
516post_delim(POST_ARGSstruct roff_man *mdoc)
517{
518 const struct roff_node *nch;
519 const char *lc;
520 enum mdelim delim;
521 enum roff_tok tok;
522
523 tok = mdoc->last->tok;
524 nch = mdoc->last->last;
525 if (nch == NULL((void *)0) || nch->type != ROFFT_TEXT)
526 return;
527 lc = strchr(nch->string, '\0') - 1;
528 if (lc < nch->string)
529 return;
530 delim = mdoc_isdelim(lc);
531 if (delim == DELIM_NONE || delim == DELIM_OPEN)
532 return;
533 if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
534 tok == MDOC_Ss || tok == MDOC_Fo))
535 return;
536
537 mandoc_msg(MANDOCERR_DELIM, nch->line,
538 nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
539 nch == mdoc->last->child ? "" : " ...", nch->string);
540}
541
542static void
543post_delim_nb(POST_ARGSstruct roff_man *mdoc)
544{
545 const struct roff_node *nch;
546 const char *lc, *cp;
547 int nw;
548 enum mdelim delim;
549 enum roff_tok tok;
550
551 /*
552 * Find candidates: at least two bytes,
553 * the last one a closing or middle delimiter.
554 */
555
556 tok = mdoc->last->tok;
557 nch = mdoc->last->last;
558 if (nch == NULL((void *)0) || nch->type != ROFFT_TEXT)
559 return;
560 lc = strchr(nch->string, '\0') - 1;
561 if (lc <= nch->string)
562 return;
563 delim = mdoc_isdelim(lc);
564 if (delim == DELIM_NONE || delim == DELIM_OPEN)
565 return;
566
567 /*
568 * Reduce false positives by allowing various cases.
569 */
570
571 /* Escaped delimiters. */
572 if (lc > nch->string + 1 && lc[-2] == '\\' &&
573 (lc[-1] == '&' || lc[-1] == 'e'))
574 return;
575
576 /* Specific byte sequences. */
577 switch (*lc) {
578 case ')':
579 for (cp = lc; cp >= nch->string; cp--)
580 if (*cp == '(')
581 return;
582 break;
583 case '.':
584 if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
585 return;
586 if (lc[-1] == '.')
587 return;
588 break;
589 case ';':
590 if (tok == MDOC_Vt)
591 return;
592 break;
593 case '?':
594 if (lc[-1] == '?')
595 return;
596 break;
597 case ']':
598 for (cp = lc; cp >= nch->string; cp--)
599 if (*cp == '[')
600 return;
601 break;
602 case '|':
603 if (lc == nch->string + 1 && lc[-1] == '|')
604 return;
605 default:
606 break;
607 }
608
609 /* Exactly two non-alphanumeric bytes. */
610 if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
611 return;
612
613 /* At least three alphabetic words with a sentence ending. */
614 if (strchr("!.:?", *lc) != NULL((void *)0) && (tok == MDOC_Em ||
615 tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
616 nw = 0;
617 for (cp = lc - 1; cp >= nch->string; cp--) {
618 if (*cp == ' ') {
619 nw++;
620 if (cp > nch->string && cp[-1] == ',')
621 cp--;
622 } else if (isalpha((unsigned int)*cp)) {
623 if (nw > 1)
624 return;
625 } else
626 break;
627 }
628 }
629
630 mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
631 nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
632 nch == mdoc->last->child ? "" : " ...", nch->string);
633}
634
635static void
636post_bl_norm(POST_ARGSstruct roff_man *mdoc)
637{
638 struct roff_node *n;
639 struct mdoc_argv *argv, *wa;
640 int i;
641 enum mdocargt mdoclt;
642 enum mdoc_list lt;
643
644 n = mdoc->last->parent;
645 n->norm->Bl.type = LIST__NONE;
646
647 /*
648 * First figure out which kind of list to use: bind ourselves to
649 * the first mentioned list type and warn about any remaining
650 * ones. If we find no list type, we default to LIST_item.
651 */
652
653 wa = (n->args == NULL((void *)0)) ? NULL((void *)0) : n->args->argv;
4
Assuming field 'args' is equal to NULL
5
'?' condition is true
6
Null pointer value stored to 'wa'
654 mdoclt = MDOC_ARG_MAX;
655 for (i = 0; n->args
6.1
Field 'args' is null
&& i < (int)n->args->argc; i++) {
656 argv = n->args->argv + i;
657 lt = LIST__NONE;
658 switch (argv->arg) {
659 /* Set list types. */
660 case MDOC_Bullet:
661 lt = LIST_bullet;
662 break;
663 case MDOC_Dash:
664 lt = LIST_dash;
665 break;
666 case MDOC_Enum:
667 lt = LIST_enum;
668 break;
669 case MDOC_Hyphen:
670 lt = LIST_hyphen;
671 break;
672 case MDOC_Item:
673 lt = LIST_item;
674 break;
675 case MDOC_Tag:
676 lt = LIST_tag;
677 break;
678 case MDOC_Diag:
679 lt = LIST_diag;
680 break;
681 case MDOC_Hang:
682 lt = LIST_hang;
683 break;
684 case MDOC_Ohang:
685 lt = LIST_ohang;
686 break;
687 case MDOC_Inset:
688 lt = LIST_inset;
689 break;
690 case MDOC_Column:
691 lt = LIST_column;
692 break;
693 /* Set list arguments. */
694 case MDOC_Compact:
695 if (n->norm->Bl.comp)
696 mandoc_msg(MANDOCERR_ARG_REP,
697 argv->line, argv->pos, "Bl -compact");
698 n->norm->Bl.comp = 1;
699 break;
700 case MDOC_Width:
701 wa = argv;
702 if (0 == argv->sz) {
703 mandoc_msg(MANDOCERR_ARG_EMPTY,
704 argv->line, argv->pos, "Bl -width");
705 n->norm->Bl.width = "0n";
706 break;
707 }
708 if (NULL((void *)0) != n->norm->Bl.width)
709 mandoc_msg(MANDOCERR_ARG_REP,
710 argv->line, argv->pos,
711 "Bl -width %s", argv->value[0]);
712 rewrite_macro2len(mdoc, argv->value);
713 n->norm->Bl.width = argv->value[0];
714 break;
715 case MDOC_Offset:
716 if (0 == argv->sz) {
717 mandoc_msg(MANDOCERR_ARG_EMPTY,
718 argv->line, argv->pos, "Bl -offset");
719 break;
720 }
721 if (NULL((void *)0) != n->norm->Bl.offs)
722 mandoc_msg(MANDOCERR_ARG_REP,
723 argv->line, argv->pos,
724 "Bl -offset %s", argv->value[0]);
725 rewrite_macro2len(mdoc, argv->value);
726 n->norm->Bl.offs = argv->value[0];
727 break;
728 default:
729 continue;
730 }
731 if (LIST__NONE == lt)
732 continue;
733 mdoclt = argv->arg;
734
735 /* Check: multiple list types. */
736
737 if (LIST__NONE != n->norm->Bl.type) {
738 mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
739 "Bl -%s", mdoc_argnames[argv->arg]);
740 continue;
741 }
742
743 /* The list type should come first. */
744
745 if (n->norm->Bl.width ||
746 n->norm->Bl.offs ||
747 n->norm->Bl.comp)
748 mandoc_msg(MANDOCERR_BL_LATETYPE,
749 n->line, n->pos, "Bl -%s",
750 mdoc_argnames[n->args->argv[0].arg]);
751
752 n->norm->Bl.type = lt;
753 if (LIST_column == lt) {
754 n->norm->Bl.ncols = argv->sz;
755 n->norm->Bl.cols = (void *)argv->value;
756 }
757 }
758
759 /* Allow lists to default to LIST_item. */
760
761 if (LIST__NONE
6.2
LIST__NONE is equal to field 'type'
== n->norm->Bl.type) {
7
Taking true branch
762 mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
763 n->norm->Bl.type = LIST_item;
764 mdoclt = MDOC_Item;
765 }
766
767 /*
768 * Validate the width field. Some list types don't need width
769 * types and should be warned about them. Others should have it
770 * and must also be warned. Yet others have a default and need
771 * no warning.
772 */
773
774 switch (n->norm->Bl.type) {
8
Control jumps to 'case LIST_item:' at line 784
775 case LIST_tag:
776 if (n->norm->Bl.width == NULL((void *)0))
777 mandoc_msg(MANDOCERR_BL_NOWIDTH,
778 n->line, n->pos, "Bl -tag");
779 break;
780 case LIST_column:
781 case LIST_diag:
782 case LIST_ohang:
783 case LIST_inset:
784 case LIST_item:
785 if (n->norm->Bl.width != NULL((void *)0))
9
Assuming field 'width' is not equal to NULL
10
Taking true branch
786 mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
11
Access to field 'line' results in a dereference of a null pointer (loaded from variable 'wa')
787 "Bl -%s", mdoc_argnames[mdoclt]);
788 n->norm->Bl.width = NULL((void *)0);
789 break;
790 case LIST_bullet:
791 case LIST_dash:
792 case LIST_hyphen:
793 if (n->norm->Bl.width == NULL((void *)0))
794 n->norm->Bl.width = "2n";
795 break;
796 case LIST_enum:
797 if (n->norm->Bl.width == NULL((void *)0))
798 n->norm->Bl.width = "3n";
799 break;
800 default:
801 break;
802 }
803}
804
805static void
806post_bd(POST_ARGSstruct roff_man *mdoc)
807{
808 struct roff_node *n;
809 struct mdoc_argv *argv;
810 int i;
811 enum mdoc_disp dt;
812
813 n = mdoc->last;
814 for (i = 0; n->args && i < (int)n->args->argc; i++) {
815 argv = n->args->argv + i;
816 dt = DISP__NONE;
817
818 switch (argv->arg) {
819 case MDOC_Centred:
820 dt = DISP_centered;
821 break;
822 case MDOC_Ragged:
823 dt = DISP_ragged;
824 break;
825 case MDOC_Unfilled:
826 dt = DISP_unfilled;
827 break;
828 case MDOC_Filled:
829 dt = DISP_filled;
830 break;
831 case MDOC_Literal:
832 dt = DISP_literal;
833 break;
834 case MDOC_File:
835 mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL((void *)0));
836 break;
837 case MDOC_Offset:
838 if (0 == argv->sz) {
839 mandoc_msg(MANDOCERR_ARG_EMPTY,
840 argv->line, argv->pos, "Bd -offset");
841 break;
842 }
843 if (NULL((void *)0) != n->norm->Bd.offs)
844 mandoc_msg(MANDOCERR_ARG_REP,
845 argv->line, argv->pos,
846 "Bd -offset %s", argv->value[0]);
847 rewrite_macro2len(mdoc, argv->value);
848 n->norm->Bd.offs = argv->value[0];
849 break;
850 case MDOC_Compact:
851 if (n->norm->Bd.comp)
852 mandoc_msg(MANDOCERR_ARG_REP,
853 argv->line, argv->pos, "Bd -compact");
854 n->norm->Bd.comp = 1;
855 break;
856 default:
857 abort();
858 }
859 if (DISP__NONE == dt)
860 continue;
861
862 if (DISP__NONE == n->norm->Bd.type)
863 n->norm->Bd.type = dt;
864 else
865 mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
866 "Bd -%s", mdoc_argnames[argv->arg]);
867 }
868
869 if (DISP__NONE == n->norm->Bd.type) {
870 mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
871 n->norm->Bd.type = DISP_ragged;
872 }
873}
874
875/*
876 * Stand-alone line macros.
877 */
878
879static void
880post_an_norm(POST_ARGSstruct roff_man *mdoc)
881{
882 struct roff_node *n;
883 struct mdoc_argv *argv;
884 size_t i;
885
886 n = mdoc->last;
887 if (n->args == NULL((void *)0))
888 return;
889
890 for (i = 1; i < n->args->argc; i++) {
891 argv = n->args->argv + i;
892 mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
893 "An -%s", mdoc_argnames[argv->arg]);
894 }
895
896 argv = n->args->argv;
897 if (argv->arg == MDOC_Split)
898 n->norm->An.auth = AUTH_split;
899 else if (argv->arg == MDOC_Nosplit)
900 n->norm->An.auth = AUTH_nosplit;
901 else
902 abort();
903}
904
905static void
906post_eoln(POST_ARGSstruct roff_man *mdoc)
907{
908 struct roff_node *n;
909
910 post_useless(mdoc);
911 n = mdoc->last;
912 if (n->child != NULL((void *)0))
913 mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
914 n->pos, "%s %s", roff_name[n->tok], n->child->string);
915
916 while (n->child != NULL((void *)0))
917 roff_node_delete(mdoc, n->child);
918
919 roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
920 "is currently in beta test." : "currently under development.");
921 mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9);
922 mdoc->last = n;
923}
924
925static int
926build_list(struct roff_man *mdoc, int tok)
927{
928 struct roff_node *n;
929 int ic;
930
931 n = mdoc->last->next;
932 for (ic = 1;; ic++) {
933 roff_elem_alloc(mdoc, n->line, n->pos, tok);
934 mdoc->last->flags |= NODE_NOSRC(1 << 9);
935 roff_node_relink(mdoc, n);
936 n = mdoc->last = mdoc->last->parent;
937 mdoc->next = ROFF_NEXT_SIBLING;
938 if (n->next == NULL((void *)0))
939 return ic;
940 if (ic > 1 || n->next->next != NULL((void *)0)) {
941 roff_word_alloc(mdoc, n->line, n->pos, ",");
942 mdoc->last->flags |= NODE_DELIMC(1 << 5) | NODE_NOSRC(1 << 9);
943 }
944 n = mdoc->last->next;
945 if (n->next == NULL((void *)0)) {
946 roff_word_alloc(mdoc, n->line, n->pos, "and");
947 mdoc->last->flags |= NODE_NOSRC(1 << 9);
948 }
949 }
950}
951
952static void
953post_ex(POST_ARGSstruct roff_man *mdoc)
954{
955 struct roff_node *n;
956 int ic;
957
958 post_std(mdoc);
959
960 n = mdoc->last;
961 mdoc->next = ROFF_NEXT_CHILD;
962 roff_word_alloc(mdoc, n->line, n->pos, "The");
963 mdoc->last->flags |= NODE_NOSRC(1 << 9);
964
965 if (mdoc->last->next != NULL((void *)0))
966 ic = build_list(mdoc, MDOC_Nm);
967 else if (mdoc->meta.name != NULL((void *)0)) {
968 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
969 mdoc->last->flags |= NODE_NOSRC(1 << 9);
970 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
971 mdoc->last->flags |= NODE_NOSRC(1 << 9);
972 mdoc->last = mdoc->last->parent;
973 mdoc->next = ROFF_NEXT_SIBLING;
974 ic = 1;
975 } else {
976 mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
977 ic = 0;
978 }
979
980 roff_word_alloc(mdoc, n->line, n->pos,
981 ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
982 mdoc->last->flags |= NODE_NOSRC(1 << 9);
983 roff_word_alloc(mdoc, n->line, n->pos,
984 "on success, and\\~>0 if an error occurs.");
985 mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9);
986 mdoc->last = n;
987}
988
989static void
990post_lb(POST_ARGSstruct roff_man *mdoc)
991{
992 struct roff_node *n;
993
994 post_delim_nb(mdoc);
995
996 n = mdoc->last;
997 assert(n->child->type == ROFFT_TEXT)((n->child->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 997, __func__, "n->child->type == ROFFT_TEXT"))
;
998 mdoc->next = ROFF_NEXT_CHILD;
999 roff_word_alloc(mdoc, n->line, n->pos, "library");
1000 mdoc->last->flags = NODE_NOSRC(1 << 9);
1001 roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
1002 mdoc->last->flags = NODE_DELIMO(1 << 4) | NODE_NOSRC(1 << 9);
1003 mdoc->last = mdoc->last->next;
1004 roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
1005 mdoc->last->flags = NODE_DELIMC(1 << 5) | NODE_NOSRC(1 << 9);
1006 mdoc->last = n;
1007}
1008
1009static void
1010post_rv(POST_ARGSstruct roff_man *mdoc)
1011{
1012 struct roff_node *n;
1013 int ic;
1014
1015 post_std(mdoc);
1016
1017 n = mdoc->last;
1018 mdoc->next = ROFF_NEXT_CHILD;
1019 if (n->child != NULL((void *)0)) {
1020 roff_word_alloc(mdoc, n->line, n->pos, "The");
1021 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1022 ic = build_list(mdoc, MDOC_Fn);
1023 roff_word_alloc(mdoc, n->line, n->pos,
1024 ic > 1 ? "functions return" : "function returns");
1025 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1026 roff_word_alloc(mdoc, n->line, n->pos,
1027 "the value\\~0 if successful;");
1028 } else
1029 roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
1030 "completion, the value\\~0 is returned;");
1031 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1032
1033 roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
1034 "the value\\~\\-1 is returned and the global variable");
1035 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1036 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
1037 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1038 roff_word_alloc(mdoc, n->line, n->pos, "errno");
1039 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1040 mdoc->last = mdoc->last->parent;
1041 mdoc->next = ROFF_NEXT_SIBLING;
1042 roff_word_alloc(mdoc, n->line, n->pos,
1043 "is set to indicate the error.");
1044 mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9);
1045 mdoc->last = n;
1046}
1047
1048static void
1049post_std(POST_ARGSstruct roff_man *mdoc)
1050{
1051 struct roff_node *n;
1052
1053 post_delim(mdoc);
1054
1055 n = mdoc->last;
1056 if (n->args && n->args->argc == 1)
1057 if (n->args->argv[0].arg == MDOC_Std)
1058 return;
1059
1060 mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
1061 "%s", roff_name[n->tok]);
1062}
1063
1064static void
1065post_st(POST_ARGSstruct roff_man *mdoc)
1066{
1067 struct roff_node *n, *nch;
1068 const char *p;
1069
1070 n = mdoc->last;
1071 nch = n->child;
1072 assert(nch->type == ROFFT_TEXT)((nch->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 1072, __func__, "nch->type == ROFFT_TEXT"))
;
1073
1074 if ((p = mdoc_a2st(nch->string)) == NULL((void *)0)) {
1075 mandoc_msg(MANDOCERR_ST_BAD,
1076 nch->line, nch->pos, "St %s", nch->string);
1077 roff_node_delete(mdoc, n);
1078 return;
1079 }
1080
1081 nch->flags |= NODE_NOPRT(1 << 10);
1082 mdoc->next = ROFF_NEXT_CHILD;
1083 roff_word_alloc(mdoc, nch->line, nch->pos, p);
1084 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1085 mdoc->last= n;
1086}
1087
1088static void
1089post_tg(POST_ARGSstruct roff_man *mdoc)
1090{
1091 struct roff_node *n; /* The .Tg node. */
1092 struct roff_node *nch; /* The first child of the .Tg node. */
1093 struct roff_node *nn; /* The next node after the .Tg node. */
1094 struct roff_node *np; /* The parent of the next node. */
1095 struct roff_node *nt; /* The TEXT node containing the tag. */
1096 size_t len; /* The number of bytes in the tag. */
1097
1098 /* Find the next node. */
1099 n = mdoc->last;
1100 for (nn = n; nn != NULL((void *)0); nn = nn->parent) {
1101 if (nn->next != NULL((void *)0)) {
1102 nn = nn->next;
1103 break;
1104 }
1105 }
1106
1107 /* Find the tag. */
1108 nt = nch = n->child;
1109 if (nch == NULL((void *)0) && nn != NULL((void *)0) && nn->child != NULL((void *)0) &&
1110 nn->child->type == ROFFT_TEXT)
1111 nt = nn->child;
1112
1113 /* Validate the tag. */
1114 if (nt == NULL((void *)0) || *nt->string == '\0')
1115 mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
1116 if (nt == NULL((void *)0)) {
1117 roff_node_delete(mdoc, n);
1118 return;
1119 }
1120 len = strcspn(nt->string, " \t\\");
1121 if (nt->string[len] != '\0')
1122 mandoc_msg(MANDOCERR_TG_SPC, nt->line,
1123 nt->pos + len, "Tg %s", nt->string);
1124
1125 /* Keep only the first argument. */
1126 if (nch != NULL((void *)0) && nch->next != NULL((void *)0)) {
1127 mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
1128 nch->next->pos, "Tg ... %s", nch->next->string);
1129 while (nch->next != NULL((void *)0))
1130 roff_node_delete(mdoc, nch->next);
1131 }
1132
1133 /* Drop the macro if the first argument is invalid. */
1134 if (len == 0 || nt->string[len] != '\0') {
1135 roff_node_delete(mdoc, n);
1136 return;
1137 }
1138
1139 /* By default, tag the .Tg node itself. */
1140 if (nn == NULL((void *)0) || nn->flags & NODE_ID(1 << 11))
1141 nn = n;
1142
1143 /* Explicit tagging of specific macros. */
1144 switch (nn->tok) {
1145 case MDOC_Sh:
1146 case MDOC_Ss:
1147 case MDOC_Fo:
1148 nn = nn->head->child == NULL((void *)0) ? n : nn->head;
1149 break;
1150 case MDOC_It:
1151 np = nn->parent;
1152 while (np->tok != MDOC_Bl)
1153 np = np->parent;
1154 switch (np->norm->Bl.type) {
1155 case LIST_column:
1156 break;
1157 case LIST_diag:
1158 case LIST_hang:
1159 case LIST_inset:
1160 case LIST_ohang:
1161 case LIST_tag:
1162 nn = nn->head;
1163 break;
1164 case LIST_bullet:
1165 case LIST_dash:
1166 case LIST_enum:
1167 case LIST_hyphen:
1168 case LIST_item:
1169 nn = nn->body->child == NULL((void *)0) ? n : nn->body;
1170 break;
1171 default:
1172 abort();
1173 }
1174 break;
1175 case MDOC_Bd:
1176 case MDOC_Bl:
1177 case MDOC_D1:
1178 case MDOC_Dl:
1179 nn = nn->body->child == NULL((void *)0) ? n : nn->body;
1180 break;
1181 case MDOC_Pp:
1182 break;
1183 case MDOC_Cm:
1184 case MDOC_Dv:
1185 case MDOC_Em:
1186 case MDOC_Er:
1187 case MDOC_Ev:
1188 case MDOC_Fl:
1189 case MDOC_Fn:
1190 case MDOC_Ic:
1191 case MDOC_Li:
1192 case MDOC_Ms:
1193 case MDOC_No:
1194 case MDOC_Sy:
1195 if (nn->child == NULL((void *)0))
1196 nn = n;
1197 break;
1198 default:
1199 nn = n;
1200 break;
1201 }
1202 tag_put(nt->string, TAG_MANUAL1, nn);
1203 if (nn != n)
1204 n->flags |= NODE_NOPRT(1 << 10);
1205}
1206
1207static void
1208post_obsolete(POST_ARGSstruct roff_man *mdoc)
1209{
1210 struct roff_node *n;
1211
1212 n = mdoc->last;
1213 if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1214 mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
1215 "%s", roff_name[n->tok]);
1216}
1217
1218static void
1219post_useless(POST_ARGSstruct roff_man *mdoc)
1220{
1221 struct roff_node *n;
1222
1223 n = mdoc->last;
1224 mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
1225 "%s", roff_name[n->tok]);
1226}
1227
1228/*
1229 * Block macros.
1230 */
1231
1232static void
1233post_bf(POST_ARGSstruct roff_man *mdoc)
1234{
1235 struct roff_node *np, *nch;
1236
1237 /*
1238 * Unlike other data pointers, these are "housed" by the HEAD
1239 * element, which contains the goods.
1240 */
1241
1242 np = mdoc->last;
1243 if (np->type != ROFFT_HEAD)
1244 return;
1245
1246 assert(np->parent->type == ROFFT_BLOCK)((np->parent->type == ROFFT_BLOCK) ? (void)0 : __assert2
("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1246, __func__, "np->parent->type == ROFFT_BLOCK"
))
;
1247 assert(np->parent->tok == MDOC_Bf)((np->parent->tok == MDOC_Bf) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 1247, __func__, "np->parent->tok == MDOC_Bf"))
;
1248
1249 /* Check the number of arguments. */
1250
1251 nch = np->child;
1252 if (np->parent->args == NULL((void *)0)) {
1253 if (nch == NULL((void *)0)) {
1254 mandoc_msg(MANDOCERR_BF_NOFONT,
1255 np->line, np->pos, "Bf");
1256 return;
1257 }
1258 nch = nch->next;
1259 }
1260 if (nch != NULL((void *)0))
1261 mandoc_msg(MANDOCERR_ARG_EXCESS,
1262 nch->line, nch->pos, "Bf ... %s", nch->string);
1263
1264 /* Extract argument into data. */
1265
1266 if (np->parent->args != NULL((void *)0)) {
1267 switch (np->parent->args->argv[0].arg) {
1268 case MDOC_Emphasis:
1269 np->norm->Bf.font = FONT_Em;
1270 break;
1271 case MDOC_Literal:
1272 np->norm->Bf.font = FONT_Li;
1273 break;
1274 case MDOC_Symbolic:
1275 np->norm->Bf.font = FONT_Sy;
1276 break;
1277 default:
1278 abort();
1279 }
1280 return;
1281 }
1282
1283 /* Extract parameter into data. */
1284
1285 if ( ! strcmp(np->child->string, "Em"))
1286 np->norm->Bf.font = FONT_Em;
1287 else if ( ! strcmp(np->child->string, "Li"))
1288 np->norm->Bf.font = FONT_Li;
1289 else if ( ! strcmp(np->child->string, "Sy"))
1290 np->norm->Bf.font = FONT_Sy;
1291 else
1292 mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
1293 np->child->pos, "Bf %s", np->child->string);
1294}
1295
1296static void
1297post_fname(POST_ARGSstruct roff_man *mdoc)
1298{
1299 struct roff_node *n, *nch;
1300 const char *cp;
1301 size_t pos;
1302
1303 n = mdoc->last;
1304 nch = n->child;
1305 cp = nch->string;
1306 if (*cp == '(') {
1307 if (cp[strlen(cp + 1)] == ')')
1308 return;
1309 pos = 0;
1310 } else {
1311 pos = strcspn(cp, "()");
1312 if (cp[pos] == '\0') {
1313 if (n->sec == SEC_DESCRIPTION ||
1314 n->sec == SEC_CUSTOM)
1315 tag_put(NULL((void *)0), fn_prio++, n);
1316 return;
1317 }
1318 }
1319 mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
1320}
1321
1322static void
1323post_fn(POST_ARGSstruct roff_man *mdoc)
1324{
1325 post_fname(mdoc);
1326 post_fa(mdoc);
1327}
1328
1329static void
1330post_fo(POST_ARGSstruct roff_man *mdoc)
1331{
1332 const struct roff_node *n;
1333
1334 n = mdoc->last;
1335
1336 if (n->type != ROFFT_HEAD)
1337 return;
1338
1339 if (n->child == NULL((void *)0)) {
1340 mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
1341 return;
1342 }
1343 if (n->child != n->last) {
1344 mandoc_msg(MANDOCERR_ARG_EXCESS,
1345 n->child->next->line, n->child->next->pos,
1346 "Fo ... %s", n->child->next->string);
1347 while (n->child != n->last)
1348 roff_node_delete(mdoc, n->last);
1349 } else
1350 post_delim(mdoc);
1351
1352 post_fname(mdoc);
1353}
1354
1355static void
1356post_fa(POST_ARGSstruct roff_man *mdoc)
1357{
1358 const struct roff_node *n;
1359 const char *cp;
1360
1361 for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) {
1362 for (cp = n->string; *cp != '\0'; cp++) {
1363 /* Ignore callbacks and alterations. */
1364 if (*cp == '(' || *cp == '{')
1365 break;
1366 if (*cp != ',')
1367 continue;
1368 mandoc_msg(MANDOCERR_FA_COMMA, n->line,
1369 n->pos + (int)(cp - n->string), "%s", n->string);
1370 break;
1371 }
1372 }
1373 post_delim_nb(mdoc);
1374}
1375
1376static void
1377post_nm(POST_ARGSstruct roff_man *mdoc)
1378{
1379 struct roff_node *n;
1380
1381 n = mdoc->last;
1382
1383 if (n->sec == SEC_NAME && n->child != NULL((void *)0) &&
1384 n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL((void *)0))
1385 mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
1386
1387 if (n->last != NULL((void *)0) && n->last->tok == MDOC_Pp)
1388 roff_node_relink(mdoc, n->last);
1389
1390 if (mdoc->meta.name == NULL((void *)0))
1391 deroff(&mdoc->meta.name, n);
1392
1393 if (mdoc->meta.name == NULL((void *)0) ||
1394 (mdoc->lastsec == SEC_NAME && n->child == NULL((void *)0)))
1395 mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
1396
1397 switch (n->type) {
1398 case ROFFT_ELEM:
1399 post_delim_nb(mdoc);
1400 break;
1401 case ROFFT_HEAD:
1402 post_delim(mdoc);
1403 break;
1404 default:
1405 return;
1406 }
1407
1408 if ((n->child != NULL((void *)0) && n->child->type == ROFFT_TEXT) ||
1409 mdoc->meta.name == NULL((void *)0))
1410 return;
1411
1412 mdoc->next = ROFF_NEXT_CHILD;
1413 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1414 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1415 mdoc->last = n;
1416}
1417
1418static void
1419post_nd(POST_ARGSstruct roff_man *mdoc)
1420{
1421 struct roff_node *n;
1422
1423 n = mdoc->last;
1424
1425 if (n->type != ROFFT_BODY)
1426 return;
1427
1428 if (n->sec != SEC_NAME)
1429 mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
1430
1431 if (n->child == NULL((void *)0))
1432 mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
1433 else
1434 post_delim(mdoc);
1435
1436 post_hyph(mdoc);
1437}
1438
1439static void
1440post_display(POST_ARGSstruct roff_man *mdoc)
1441{
1442 struct roff_node *n, *np;
1443
1444 n = mdoc->last;
1445 switch (n->type) {
1446 case ROFFT_BODY:
1447 if (n->end != ENDBODY_NOT) {
1448 if (n->tok == MDOC_Bd &&
1449 n->body->parent->args == NULL((void *)0))
1450 roff_node_delete(mdoc, n);
1451 } else if (n->child == NULL((void *)0))
1452 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
1453 "%s", roff_name[n->tok]);
1454 else if (n->tok == MDOC_D1)
1455 post_hyph(mdoc);
1456 break;
1457 case ROFFT_BLOCK:
1458 if (n->tok == MDOC_Bd) {
1459 if (n->args == NULL((void *)0)) {
1460 mandoc_msg(MANDOCERR_BD_NOARG,
1461 n->line, n->pos, "Bd");
1462 mdoc->next = ROFF_NEXT_SIBLING;
1463 while (n->body->child != NULL((void *)0))
1464 roff_node_relink(mdoc,
1465 n->body->child);
1466 roff_node_delete(mdoc, n);
1467 break;
1468 }
1469 post_bd(mdoc);
1470 post_prevpar(mdoc);
1471 }
1472 for (np = n->parent; np != NULL((void *)0); np = np->parent) {
1473 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1474 mandoc_msg(MANDOCERR_BD_NEST, n->line,
1475 n->pos, "%s in Bd", roff_name[n->tok]);
1476 break;
1477 }
1478 }
1479 break;
1480 default:
1481 break;
1482 }
1483}
1484
1485static void
1486post_defaults(POST_ARGSstruct roff_man *mdoc)
1487{
1488 struct roff_node *n;
1489
1490 n = mdoc->last;
1491 if (n->child != NULL((void *)0)) {
1492 post_delim_nb(mdoc);
1493 return;
1494 }
1495 mdoc->next = ROFF_NEXT_CHILD;
1496 switch (n->tok) {
1497 case MDOC_Ar:
1498 roff_word_alloc(mdoc, n->line, n->pos, "file");
1499 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1500 roff_word_alloc(mdoc, n->line, n->pos, "...");
1501 break;
1502 case MDOC_Pa:
1503 case MDOC_Mt:
1504 roff_word_alloc(mdoc, n->line, n->pos, "~");
1505 break;
1506 default:
1507 abort();
1508 }
1509 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1510 mdoc->last = n;
1511}
1512
1513static void
1514post_at(POST_ARGSstruct roff_man *mdoc)
1515{
1516 struct roff_node *n, *nch;
1517 const char *att;
1518
1519 n = mdoc->last;
1520 nch = n->child;
1521
1522 /*
1523 * If we have a child, look it up in the standard keys. If a
1524 * key exist, use that instead of the child; if it doesn't,
1525 * prefix "AT&T UNIX " to the existing data.
1526 */
1527
1528 att = NULL((void *)0);
1529 if (nch != NULL((void *)0) && ((att = mdoc_a2att(nch->string)) == NULL((void *)0)))
1530 mandoc_msg(MANDOCERR_AT_BAD,
1531 nch->line, nch->pos, "At %s", nch->string);
1532
1533 mdoc->next = ROFF_NEXT_CHILD;
1534 if (att != NULL((void *)0)) {
1535 roff_word_alloc(mdoc, nch->line, nch->pos, att);
1536 nch->flags |= NODE_NOPRT(1 << 10);
1537 } else
1538 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1539 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1540 mdoc->last = n;
1541}
1542
1543static void
1544post_an(POST_ARGSstruct roff_man *mdoc)
1545{
1546 struct roff_node *np, *nch;
1547
1548 post_an_norm(mdoc);
1549
1550 np = mdoc->last;
1551 nch = np->child;
1552 if (np->norm->An.auth == AUTH__NONE) {
1553 if (nch == NULL((void *)0))
1554 mandoc_msg(MANDOCERR_MACRO_EMPTY,
1555 np->line, np->pos, "An");
1556 else
1557 post_delim_nb(mdoc);
1558 } else if (nch != NULL((void *)0))
1559 mandoc_msg(MANDOCERR_ARG_EXCESS,
1560 nch->line, nch->pos, "An ... %s", nch->string);
1561}
1562
1563static void
1564post_em(POST_ARGSstruct roff_man *mdoc)
1565{
1566 post_tag(mdoc);
1567 tag_put(NULL((void *)0), TAG_FALLBACK(2147483647 - 1), mdoc->last);
1568}
1569
1570static void
1571post_en(POST_ARGSstruct roff_man *mdoc)
1572{
1573 post_obsolete(mdoc);
1574 if (mdoc->last->type == ROFFT_BLOCK)
1575 mdoc->last->norm->Es = mdoc->last_es;
1576}
1577
1578static void
1579post_er(POST_ARGSstruct roff_man *mdoc)
1580{
1581 struct roff_node *n;
1582
1583 n = mdoc->last;
1584 if (n->sec == SEC_ERRORS &&
1585 (n->parent->tok == MDOC_It ||
1586 (n->parent->tok == MDOC_Bq &&
1587 n->parent->parent->parent->tok == MDOC_It)))
1588 tag_put(NULL((void *)0), TAG_STRONG2, n);
1589 post_delim_nb(mdoc);
1590}
1591
1592static void
1593post_tag(POST_ARGSstruct roff_man *mdoc)
1594{
1595 struct roff_node *n;
1596
1597 n = mdoc->last;
1598 if ((n->prev == NULL((void *)0) ||
1599 (n->prev->type == ROFFT_TEXT &&
1600 strcmp(n->prev->string, "|") == 0)) &&
1601 (n->parent->tok == MDOC_It ||
1602 (n->parent->tok == MDOC_Xo &&
1603 n->parent->parent->prev == NULL((void *)0) &&
1604 n->parent->parent->parent->tok == MDOC_It)))
1605 tag_put(NULL((void *)0), TAG_STRONG2, n);
1606 post_delim_nb(mdoc);
1607}
1608
1609static void
1610post_es(POST_ARGSstruct roff_man *mdoc)
1611{
1612 post_obsolete(mdoc);
1613 mdoc->last_es = mdoc->last;
1614}
1615
1616static void
1617post_fl(POST_ARGSstruct roff_man *mdoc)
1618{
1619 struct roff_node *n;
1620 char *cp;
1621
1622 /*
1623 * Transform ".Fl Fl long" to ".Fl \-long",
1624 * resulting for example in better HTML output.
1625 */
1626
1627 n = mdoc->last;
1628 if (n->prev != NULL((void *)0) && n->prev->tok == MDOC_Fl &&
1629 n->prev->child == NULL((void *)0) && n->child != NULL((void *)0) &&
1630 (n->flags & NODE_LINE(1 << 3)) == 0) {
1631 mandoc_asprintf(&cp, "\\-%s", n->child->string);
1632 free(n->child->string);
1633 n->child->string = cp;
1634 roff_node_delete(mdoc, n->prev);
1635 }
1636 post_tag(mdoc);
1637}
1638
1639static void
1640post_xx(POST_ARGSstruct roff_man *mdoc)
1641{
1642 struct roff_node *n;
1643 const char *os;
1644 char *v;
1645
1646 post_delim_nb(mdoc);
1647
1648 n = mdoc->last;
1649 switch (n->tok) {
1650 case MDOC_Bsx:
1651 os = "BSD/OS";
1652 break;
1653 case MDOC_Dx:
1654 os = "DragonFly";
1655 break;
1656 case MDOC_Fx:
1657 os = "FreeBSD";
1658 break;
1659 case MDOC_Nx:
1660 os = "NetBSD";
1661 if (n->child == NULL((void *)0))
1662 break;
1663 v = n->child->string;
1664 if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
1665 v[2] < '0' || v[2] > '9' ||
1666 v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1667 break;
1668 n->child->flags |= NODE_NOPRT(1 << 10);
1669 mdoc->next = ROFF_NEXT_CHILD;
1670 roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1671 v = mdoc->last->string;
1672 v[3] = toupper((unsigned char)v[3]);
1673 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1674 mdoc->last = n;
1675 break;
1676 case MDOC_Ox:
1677 os = "OpenBSD";
1678 break;
1679 case MDOC_Ux:
1680 os = "UNIX";
1681 break;
1682 default:
1683 abort();
1684 }
1685 mdoc->next = ROFF_NEXT_CHILD;
1686 roff_word_alloc(mdoc, n->line, n->pos, os);
1687 mdoc->last->flags |= NODE_NOSRC(1 << 9);
1688 mdoc->last = n;
1689}
1690
1691static void
1692post_it(POST_ARGSstruct roff_man *mdoc)
1693{
1694 struct roff_node *nbl, *nit, *nch;
1695 int i, cols;
1696 enum mdoc_list lt;
1697
1698 post_prevpar(mdoc);
1699
1700 nit = mdoc->last;
1701 if (nit->type != ROFFT_BLOCK)
1702 return;
1703
1704 nbl = nit->parent->parent;
1705 lt = nbl->norm->Bl.type;
1706
1707 switch (lt) {
1708 case LIST_tag:
1709 case LIST_hang:
1710 case LIST_ohang:
1711 case LIST_inset:
1712 case LIST_diag:
1713 if (nit->head->child == NULL((void *)0))
1714 mandoc_msg(MANDOCERR_IT_NOHEAD,
1715 nit->line, nit->pos, "Bl -%s It",
1716 mdoc_argnames[nbl->args->argv[0].arg]);
1717 break;
1718 case LIST_bullet:
1719 case LIST_dash:
1720 case LIST_enum:
1721 case LIST_hyphen:
1722 if (nit->body == NULL((void *)0) || nit->body->child == NULL((void *)0))
1723 mandoc_msg(MANDOCERR_IT_NOBODY,
1724 nit->line, nit->pos, "Bl -%s It",
1725 mdoc_argnames[nbl->args->argv[0].arg]);
1726 /* FALLTHROUGH */
1727 case LIST_item:
1728 if ((nch = nit->head->child) != NULL((void *)0))
1729 mandoc_msg(MANDOCERR_ARG_SKIP,
1730 nit->line, nit->pos, "It %s",
1731 nch->type == ROFFT_TEXT ? nch->string :
1732 roff_name[nch->tok]);
1733 break;
1734 case LIST_column:
1735 cols = (int)nbl->norm->Bl.ncols;
1736
1737 assert(nit->head->child == NULL)((nit->head->child == ((void *)0)) ? (void)0 : __assert2
("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1737, __func__, "nit->head->child == NULL"
))
;
1738
1739 if (nit->head->next->child == NULL((void *)0) &&
1740 nit->head->next->next == NULL((void *)0)) {
1741 mandoc_msg(MANDOCERR_MACRO_EMPTY,
1742 nit->line, nit->pos, "It");
1743 roff_node_delete(mdoc, nit);
1744 break;
1745 }
1746
1747 i = 0;
1748 for (nch = nit->child; nch != NULL((void *)0); nch = nch->next) {
1749 if (nch->type != ROFFT_BODY)
1750 continue;
1751 if (i++ && nch->flags & NODE_LINE(1 << 3))
1752 mandoc_msg(MANDOCERR_TA_LINE,
1753 nch->line, nch->pos, "Ta");
1754 }
1755 if (i < cols || i > cols + 1)
1756 mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
1757 "%d columns, %d cells", cols, i);
1758 else if (nit->head->next->child != NULL((void *)0) &&
1759 nit->head->next->child->flags & NODE_LINE(1 << 3))
1760 mandoc_msg(MANDOCERR_IT_NOARG,
1761 nit->line, nit->pos, "Bl -column It");
1762 break;
1763 default:
1764 abort();
1765 }
1766}
1767
1768static void
1769post_bl_block(POST_ARGSstruct roff_man *mdoc)
1770{
1771 struct roff_node *n, *ni, *nc;
1772
1773 post_prevpar(mdoc);
1774
1775 n = mdoc->last;
1776 for (ni = n->body->child; ni != NULL((void *)0); ni = ni->next) {
1777 if (ni->body == NULL((void *)0))
1778 continue;
1779 nc = ni->body->last;
1780 while (nc != NULL((void *)0)) {
1781 switch (nc->tok) {
1782 case MDOC_Pp:
1783 case ROFF_br:
1784 break;
1785 default:
1786 nc = NULL((void *)0);
1787 continue;
1788 }
1789 if (ni->next == NULL((void *)0)) {
1790 mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
1791 nc->pos, "%s", roff_name[nc->tok]);
1792 roff_node_relink(mdoc, nc);
1793 } else if (n->norm->Bl.comp == 0 &&
1794 n->norm->Bl.type != LIST_column) {
1795 mandoc_msg(MANDOCERR_PAR_SKIP,
1796 nc->line, nc->pos,
1797 "%s before It", roff_name[nc->tok]);
1798 roff_node_delete(mdoc, nc);
1799 } else
1800 break;
1801 nc = ni->body->last;
1802 }
1803 }
1804}
1805
1806/*
1807 * If the argument of -offset or -width is a macro,
1808 * replace it with the associated default width.
1809 */
1810static void
1811rewrite_macro2len(struct roff_man *mdoc, char **arg)
1812{
1813 size_t width;
1814 enum roff_tok tok;
1815
1816 if (*arg == NULL((void *)0))
1817 return;
1818 else if ( ! strcmp(*arg, "Ds"))
1819 width = 6;
1820 else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1821 return;
1822 else
1823 width = macro2len(tok);
1824
1825 free(*arg);
1826 mandoc_asprintf(arg, "%zun", width);
1827}
1828
1829static void
1830post_bl_head(POST_ARGSstruct roff_man *mdoc)
1831{
1832 struct roff_node *nbl, *nh, *nch, *nnext;
1833 struct mdoc_argv *argv;
1834 int i, j;
1835
1836 post_bl_norm(mdoc);
3
Calling 'post_bl_norm'
1837
1838 nh = mdoc->last;
1839 if (nh->norm->Bl.type != LIST_column) {
1840 if ((nch = nh->child) == NULL((void *)0))
1841 return;
1842 mandoc_msg(MANDOCERR_ARG_EXCESS,
1843 nch->line, nch->pos, "Bl ... %s", nch->string);
1844 while (nch != NULL((void *)0)) {
1845 roff_node_delete(mdoc, nch);
1846 nch = nh->child;
1847 }
1848 return;
1849 }
1850
1851 /*
1852 * Append old-style lists, where the column width specifiers
1853 * trail as macro parameters, to the new-style ("normal-form")
1854 * lists where they're argument values following -column.
1855 */
1856
1857 if (nh->child == NULL((void *)0))
1858 return;
1859
1860 nbl = nh->parent;
1861 for (j = 0; j < (int)nbl->args->argc; j++)
1862 if (nbl->args->argv[j].arg == MDOC_Column)
1863 break;
1864
1865 assert(j < (int)nbl->args->argc)((j < (int)nbl->args->argc) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 1865, __func__, "j < (int)nbl->args->argc"))
;
1866
1867 /*
1868 * Accommodate for new-style groff column syntax. Shuffle the
1869 * child nodes, all of which must be TEXT, as arguments for the
1870 * column field. Then, delete the head children.
1871 */
1872
1873 argv = nbl->args->argv + j;
1874 i = argv->sz;
1875 for (nch = nh->child; nch != NULL((void *)0); nch = nch->next)
1876 argv->sz++;
1877 argv->value = mandoc_reallocarray(argv->value,
1878 argv->sz, sizeof(char *));
1879
1880 nh->norm->Bl.ncols = argv->sz;
1881 nh->norm->Bl.cols = (void *)argv->value;
1882
1883 for (nch = nh->child; nch != NULL((void *)0); nch = nnext) {
1884 argv->value[i++] = nch->string;
1885 nch->string = NULL((void *)0);
1886 nnext = nch->next;
1887 roff_node_delete(NULL((void *)0), nch);
1888 }
1889 nh->child = NULL((void *)0);
1890}
1891
1892static void
1893post_bl(POST_ARGSstruct roff_man *mdoc)
1894{
1895 struct roff_node *nbody; /* of the Bl */
1896 struct roff_node *nchild, *nnext; /* of the Bl body */
1897 const char *prev_Er;
1898 int order;
1899
1900 nbody = mdoc->last;
1901 switch (nbody->type) {
1
Control jumps to 'case ROFFT_HEAD:' at line 1905
1902 case ROFFT_BLOCK:
1903 post_bl_block(mdoc);
1904 return;
1905 case ROFFT_HEAD:
1906 post_bl_head(mdoc);
2
Calling 'post_bl_head'
1907 return;
1908 case ROFFT_BODY:
1909 break;
1910 default:
1911 return;
1912 }
1913 if (nbody->end != ENDBODY_NOT)
1914 return;
1915
1916 /*
1917 * Up to the first item, move nodes before the list,
1918 * but leave transparent nodes where they are
1919 * if they precede an item.
1920 * The next non-transparent node is kept in nchild.
1921 * It only needs to be updated after a non-transparent
1922 * node was moved out, and at the very beginning
1923 * when no node at all was moved yet.
1924 */
1925
1926 nchild = mdoc->last;
1927 for (;;) {
1928 if (nchild == mdoc->last)
1929 nchild = roff_node_child(nbody);
1930 if (nchild == NULL((void *)0)) {
1931 mdoc->last = nbody;
1932 mandoc_msg(MANDOCERR_BLK_EMPTY,
1933 nbody->line, nbody->pos, "Bl");
1934 return;
1935 }
1936 if (nchild->tok == MDOC_It) {
1937 mdoc->last = nbody;
1938 break;
1939 }
1940 mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line,
1941 nbody->child->pos, "%s", roff_name[nbody->child->tok]);
1942 if (nbody->parent->prev == NULL((void *)0)) {
1943 mdoc->last = nbody->parent->parent;
1944 mdoc->next = ROFF_NEXT_CHILD;
1945 } else {
1946 mdoc->last = nbody->parent->prev;
1947 mdoc->next = ROFF_NEXT_SIBLING;
1948 }
1949 roff_node_relink(mdoc, nbody->child);
1950 }
1951
1952 /*
1953 * We have reached the first item,
1954 * so moving nodes out is no longer possible.
1955 * But in .Bl -column, the first rows may be implicit,
1956 * that is, they may not start with .It macros.
1957 * Such rows may be followed by nodes generated on the
1958 * roff level, for example .TS.
1959 * Wrap such roff nodes into an implicit row.
1960 */
1961
1962 while (nchild != NULL((void *)0)) {
1963 if (nchild->tok == MDOC_It) {
1964 nchild = roff_node_next(nchild);
1965 continue;
1966 }
1967 nnext = nchild->next;
1968 mdoc->last = nchild->prev;
1969 mdoc->next = ROFF_NEXT_SIBLING;
1970 roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1971 roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1972 mdoc->next = ROFF_NEXT_SIBLING;
1973 roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1974 while (nchild->tok != MDOC_It) {
1975 roff_node_relink(mdoc, nchild);
1976 if (nnext == NULL((void *)0))
1977 break;
1978 nchild = nnext;
1979 nnext = nchild->next;
1980 mdoc->next = ROFF_NEXT_SIBLING;
1981 }
1982 mdoc->last = nbody;
1983 }
1984
1985 if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1986 return;
1987
1988 prev_Er = NULL((void *)0);
1989 for (nchild = nbody->child; nchild != NULL((void *)0); nchild = nchild->next) {
1990 if (nchild->tok != MDOC_It)
1991 continue;
1992 if ((nnext = nchild->head->child) == NULL((void *)0))
1993 continue;
1994 if (nnext->type == ROFFT_BLOCK)
1995 nnext = nnext->body->child;
1996 if (nnext == NULL((void *)0) || nnext->tok != MDOC_Er)
1997 continue;
1998 nnext = nnext->child;
1999 if (prev_Er != NULL((void *)0)) {
2000 order = strcmp(prev_Er, nnext->string);
2001 if (order > 0)
2002 mandoc_msg(MANDOCERR_ER_ORDER,
2003 nnext->line, nnext->pos,
2004 "Er %s %s (NetBSD)",
2005 prev_Er, nnext->string);
2006 else if (order == 0)
2007 mandoc_msg(MANDOCERR_ER_REP,
2008 nnext->line, nnext->pos,
2009 "Er %s (NetBSD)", prev_Er);
2010 }
2011 prev_Er = nnext->string;
2012 }
2013}
2014
2015static void
2016post_bk(POST_ARGSstruct roff_man *mdoc)
2017{
2018 struct roff_node *n;
2019
2020 n = mdoc->last;
2021
2022 if (n->type == ROFFT_BLOCK && n->body->child == NULL((void *)0)) {
2023 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
2024 roff_node_delete(mdoc, n);
2025 }
2026}
2027
2028static void
2029post_sm(POST_ARGSstruct roff_man *mdoc)
2030{
2031 struct roff_node *nch;
2032
2033 nch = mdoc->last->child;
2034
2035 if (nch == NULL((void *)0)) {
2036 mdoc->flags ^= MDOC_SMOFF(1 << 9);
2037 return;
2038 }
2039
2040 assert(nch->type == ROFFT_TEXT)((nch->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 2040, __func__, "nch->type == ROFFT_TEXT"))
;
2041
2042 if ( ! strcmp(nch->string, "on")) {
2043 mdoc->flags &= ~MDOC_SMOFF(1 << 9);
2044 return;
2045 }
2046 if ( ! strcmp(nch->string, "off")) {
2047 mdoc->flags |= MDOC_SMOFF(1 << 9);
2048 return;
2049 }
2050
2051 mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
2052 "%s %s", roff_name[mdoc->last->tok], nch->string);
2053 roff_node_relink(mdoc, nch);
2054 return;
2055}
2056
2057static void
2058post_root(POST_ARGSstruct roff_man *mdoc)
2059{
2060 struct roff_node *n;
2061
2062 /* Add missing prologue data. */
2063
2064 if (mdoc->meta.date == NULL((void *)0))
2065 mdoc->meta.date = mandoc_normdate(NULL((void *)0), NULL((void *)0));
2066
2067 if (mdoc->meta.title == NULL((void *)0)) {
2068 mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
2069 mdoc->meta.title = mandoc_strdup("UNTITLED");
2070 }
2071
2072 if (mdoc->meta.vol == NULL((void *)0))
2073 mdoc->meta.vol = mandoc_strdup("LOCAL");
2074
2075 if (mdoc->meta.os == NULL((void *)0)) {
2076 mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL((void *)0));
2077 mdoc->meta.os = mandoc_strdup("");
2078 } else if (mdoc->meta.os_e &&
2079 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
2080 mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
2081 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2082 "(OpenBSD)" : "(NetBSD)");
2083
2084 if (mdoc->meta.arch != NULL((void *)0) &&
2085 arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
2086 n = mdoc->meta.first->child;
2087 while (n->tok != MDOC_Dt ||
2088 n->child == NULL((void *)0) ||
2089 n->child->next == NULL((void *)0) ||
2090 n->child->next->next == NULL((void *)0))
2091 n = n->next;
2092 n = n->child->next->next;
2093 mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
2094 "Dt ... %s %s", mdoc->meta.arch,
2095 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2096 "(OpenBSD)" : "(NetBSD)");
2097 }
2098
2099 /* Check that we begin with a proper `Sh'. */
2100
2101 n = mdoc->meta.first->child;
2102 while (n != NULL((void *)0) &&
2103 (n->type == ROFFT_COMMENT ||
2104 (n->tok >= MDOC_Dd &&
2105 mdoc_macro(n->tok)->flags & MDOC_PROLOGUE(1 << 3))))
2106 n = n->next;
2107
2108 if (n == NULL((void *)0))
2109 mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL((void *)0));
2110 else if (n->tok != MDOC_Sh)
2111 mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
2112 "%s", roff_name[n->tok]);
2113}
2114
2115static void
2116post_rs(POST_ARGSstruct roff_man *mdoc)
2117{
2118 struct roff_node *np, *nch, *next, *prev;
2119 int i, j;
2120
2121 np = mdoc->last;
2122
2123 if (np->type != ROFFT_BODY)
2124 return;
2125
2126 if (np->child == NULL((void *)0)) {
2127 mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
2128 return;
2129 }
2130
2131 /*
2132 * The full `Rs' block needs special handling to order the
2133 * sub-elements according to `rsord'. Pick through each element
2134 * and correctly order it. This is an insertion sort.
2135 */
2136
2137 next = NULL((void *)0);
2138 for (nch = np->child->next; nch != NULL((void *)0); nch = next) {
2139 /* Determine order number of this child. */
2140 for (i = 0; i < RSORD_MAX14; i++)
2141 if (rsord[i] == nch->tok)
2142 break;
2143
2144 if (i == RSORD_MAX14) {
2145 mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
2146 "%s", roff_name[nch->tok]);
2147 i = -1;
2148 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
2149 np->norm->Rs.quote_T++;
2150
2151 /*
2152 * Remove this child from the chain. This somewhat
2153 * repeats roff_node_unlink(), but since we're
2154 * just re-ordering, there's no need for the
2155 * full unlink process.
2156 */
2157
2158 if ((next = nch->next) != NULL((void *)0))
2159 next->prev = nch->prev;
2160
2161 if ((prev = nch->prev) != NULL((void *)0))
2162 prev->next = nch->next;
2163
2164 nch->prev = nch->next = NULL((void *)0);
2165
2166 /*
2167 * Scan back until we reach a node that's
2168 * to be ordered before this child.
2169 */
2170
2171 for ( ; prev ; prev = prev->prev) {
2172 /* Determine order of `prev'. */
2173 for (j = 0; j < RSORD_MAX14; j++)
2174 if (rsord[j] == prev->tok)
2175 break;
2176 if (j == RSORD_MAX14)
2177 j = -1;
2178
2179 if (j <= i)
2180 break;
2181 }
2182
2183 /*
2184 * Set this child back into its correct place
2185 * in front of the `prev' node.
2186 */
2187
2188 nch->prev = prev;
2189
2190 if (prev == NULL((void *)0)) {
2191 np->child->prev = nch;
2192 nch->next = np->child;
2193 np->child = nch;
2194 } else {
2195 if (prev->next)
2196 prev->next->prev = nch;
2197 nch->next = prev->next;
2198 prev->next = nch;
2199 }
2200 }
2201}
2202
2203/*
2204 * For some arguments of some macros,
2205 * convert all breakable hyphens into ASCII_HYPH.
2206 */
2207static void
2208post_hyph(POST_ARGSstruct roff_man *mdoc)
2209{
2210 struct roff_node *n, *nch;
2211 char *cp;
2212
2213 n = mdoc->last;
2214 for (nch = n->child; nch != NULL((void *)0); nch = nch->next) {
2215 if (nch->type != ROFFT_TEXT)
2216 continue;
2217 cp = nch->string;
2218 if (*cp == '\0')
2219 continue;
2220 while (*(++cp) != '\0')
2221 if (*cp == '-' &&
2222 isalpha((unsigned char)cp[-1]) &&
2223 isalpha((unsigned char)cp[1])) {
2224 if (n->tag == NULL((void *)0) && n->flags & NODE_ID(1 << 11))
2225 n->tag = mandoc_strdup(nch->string);
2226 *cp = ASCII_HYPH30;
2227 }
2228 }
2229}
2230
2231static void
2232post_ns(POST_ARGSstruct roff_man *mdoc)
2233{
2234 struct roff_node *n;
2235
2236 n = mdoc->last;
2237 if (n->flags & NODE_LINE(1 << 3) ||
2238 (n->next != NULL((void *)0) && n->next->flags & NODE_DELIMC(1 << 5)))
2239 mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL((void *)0));
2240}
2241
2242static void
2243post_sx(POST_ARGSstruct roff_man *mdoc)
2244{
2245 post_delim(mdoc);
2246 post_hyph(mdoc);
2247}
2248
2249static void
2250post_sh(POST_ARGSstruct roff_man *mdoc)
2251{
2252 post_section(mdoc);
2253
2254 switch (mdoc->last->type) {
2255 case ROFFT_HEAD:
2256 post_sh_head(mdoc);
2257 break;
2258 case ROFFT_BODY:
2259 switch (mdoc->lastsec) {
2260 case SEC_NAME:
2261 post_sh_name(mdoc);
2262 break;
2263 case SEC_SEE_ALSO:
2264 post_sh_see_also(mdoc);
2265 break;
2266 case SEC_AUTHORS:
2267 post_sh_authors(mdoc);
2268 break;
2269 default:
2270 break;
2271 }
2272 break;
2273 default:
2274 break;
2275 }
2276}
2277
2278static void
2279post_sh_name(POST_ARGSstruct roff_man *mdoc)
2280{
2281 struct roff_node *n;
2282 int hasnm, hasnd;
2283
2284 hasnm = hasnd = 0;
2285
2286 for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) {
2287 switch (n->tok) {
2288 case MDOC_Nm:
2289 if (hasnm && n->child != NULL((void *)0))
2290 mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
2291 n->line, n->pos,
2292 "Nm %s", n->child->string);
2293 hasnm = 1;
2294 continue;
2295 case MDOC_Nd:
2296 hasnd = 1;
2297 if (n->next != NULL((void *)0))
2298 mandoc_msg(MANDOCERR_NAMESEC_ND,
2299 n->line, n->pos, NULL((void *)0));
2300 break;
2301 case TOKEN_NONE:
2302 if (n->type == ROFFT_TEXT &&
2303 n->string[0] == ',' && n->string[1] == '\0' &&
2304 n->next != NULL((void *)0) && n->next->tok == MDOC_Nm) {
2305 n = n->next;
2306 continue;
2307 }
2308 /* FALLTHROUGH */
2309 default:
2310 mandoc_msg(MANDOCERR_NAMESEC_BAD,
2311 n->line, n->pos, "%s", roff_name[n->tok]);
2312 continue;
2313 }
2314 break;
2315 }
2316
2317 if ( ! hasnm)
2318 mandoc_msg(MANDOCERR_NAMESEC_NONM,
2319 mdoc->last->line, mdoc->last->pos, NULL((void *)0));
2320 if ( ! hasnd)
2321 mandoc_msg(MANDOCERR_NAMESEC_NOND,
2322 mdoc->last->line, mdoc->last->pos, NULL((void *)0));
2323}
2324
2325static void
2326post_sh_see_also(POST_ARGSstruct roff_man *mdoc)
2327{
2328 const struct roff_node *n;
2329 const char *name, *sec;
2330 const char *lastname, *lastsec, *lastpunct;
2331 int cmp;
2332
2333 n = mdoc->last->child;
2334 lastname = lastsec = lastpunct = NULL((void *)0);
2335 while (n != NULL((void *)0)) {
2336 if (n->tok != MDOC_Xr ||
2337 n->child == NULL((void *)0) ||
2338 n->child->next == NULL((void *)0))
2339 break;
2340
2341 /* Process one .Xr node. */
2342
2343 name = n->child->string;
2344 sec = n->child->next->string;
2345 if (lastsec != NULL((void *)0)) {
2346 if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2347 mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2348 n->pos, "%s before %s(%s)",
2349 lastpunct, name, sec);
2350 cmp = strcmp(lastsec, sec);
2351 if (cmp > 0)
2352 mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2353 n->pos, "%s(%s) after %s(%s)",
2354 name, sec, lastname, lastsec);
2355 else if (cmp == 0 &&
2356 strcasecmp(lastname, name) > 0)
2357 mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2358 n->pos, "%s after %s", name, lastname);
2359 }
2360 lastname = name;
2361 lastsec = sec;
2362
2363 /* Process the following node. */
2364
2365 n = n->next;
2366 if (n == NULL((void *)0))
2367 break;
2368 if (n->tok == MDOC_Xr) {
2369 lastpunct = "none";
2370 continue;
2371 }
2372 if (n->type != ROFFT_TEXT)
2373 break;
2374 for (name = n->string; *name != '\0'; name++)
2375 if (isalpha((const unsigned char)*name))
2376 return;
2377 lastpunct = n->string;
2378 if (n->next == NULL((void *)0) || n->next->tok == MDOC_Rs)
2379 mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2380 n->pos, "%s after %s(%s)",
2381 lastpunct, lastname, lastsec);
2382 n = n->next;
2383 }
2384}
2385
2386static int
2387child_an(const struct roff_node *n)
2388{
2389
2390 for (n = n->child; n != NULL((void *)0); n = n->next)
2391 if ((n->tok == MDOC_An && n->child != NULL((void *)0)) || child_an(n))
2392 return 1;
2393 return 0;
2394}
2395
2396static void
2397post_sh_authors(POST_ARGSstruct roff_man *mdoc)
2398{
2399
2400 if ( ! child_an(mdoc->last))
2401 mandoc_msg(MANDOCERR_AN_MISSING,
2402 mdoc->last->line, mdoc->last->pos, NULL((void *)0));
2403}
2404
2405/*
2406 * Return an upper bound for the string distance (allowing
2407 * transpositions). Not a full Levenshtein implementation
2408 * because Levenshtein is quadratic in the string length
2409 * and this function is called for every standard name,
2410 * so the check for each custom name would be cubic.
2411 * The following crude heuristics is linear, resulting
2412 * in quadratic behaviour for checking one custom name,
2413 * which does not cause measurable slowdown.
2414 */
2415static int
2416similar(const char *s1, const char *s2)
2417{
2418 const int maxdist = 3;
2419 int dist = 0;
2420
2421 while (s1[0] != '\0' && s2[0] != '\0') {
2422 if (s1[0] == s2[0]) {
2423 s1++;
2424 s2++;
2425 continue;
2426 }
2427 if (++dist > maxdist)
2428 return INT_MAX2147483647;
2429 if (s1[1] == s2[1]) { /* replacement */
2430 s1++;
2431 s2++;
2432 } else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2433 s1 += 2; /* transposition */
2434 s2 += 2;
2435 } else if (s1[0] == s2[1]) /* insertion */
2436 s2++;
2437 else if (s1[1] == s2[0]) /* deletion */
2438 s1++;
2439 else
2440 return INT_MAX2147483647;
2441 }
2442 dist += strlen(s1) + strlen(s2);
2443 return dist > maxdist ? INT_MAX2147483647 : dist;
2444}
2445
2446static void
2447post_sh_head(POST_ARGSstruct roff_man *mdoc)
2448{
2449 struct roff_node *nch;
2450 const char *goodsec;
2451 const char *const *testsec;
2452 int dist, mindist;
2453 enum roff_sec sec;
2454
2455 /*
2456 * Process a new section. Sections are either "named" or
2457 * "custom". Custom sections are user-defined, while named ones
2458 * follow a conventional order and may only appear in certain
2459 * manual sections.
2460 */
2461
2462 sec = mdoc->last->sec;
2463
2464 /* The NAME should be first. */
2465
2466 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2467 mandoc_msg(MANDOCERR_NAMESEC_FIRST,
2468 mdoc->last->line, mdoc->last->pos, "Sh %s",
2469 sec != SEC_CUSTOM ? secnames[sec] :
2470 (nch = mdoc->last->child) == NULL((void *)0) ? "" :
2471 nch->type == ROFFT_TEXT ? nch->string :
2472 roff_name[nch->tok]);
2473
2474 /* The SYNOPSIS gets special attention in other areas. */
2475
2476 if (sec == SEC_SYNOPSIS) {
2477 roff_setreg(mdoc->roff, "nS", 1, '=');
2478 mdoc->flags |= MDOC_SYNOPSIS(1 << 7);
2479 } else {
2480 roff_setreg(mdoc->roff, "nS", 0, '=');
2481 mdoc->flags &= ~MDOC_SYNOPSIS(1 << 7);
2482 }
2483 if (sec == SEC_DESCRIPTION)
2484 fn_prio = TAG_STRONG2;
2485
2486 /* Mark our last section. */
2487
2488 mdoc->lastsec = sec;
2489
2490 /* We don't care about custom sections after this. */
2491
2492 if (sec == SEC_CUSTOM) {
2493 if ((nch = mdoc->last->child) == NULL((void *)0) ||
2494 nch->type != ROFFT_TEXT || nch->next != NULL((void *)0))
2495 return;
2496 goodsec = NULL((void *)0);
2497 mindist = INT_MAX2147483647;
2498 for (testsec = secnames + 1; *testsec != NULL((void *)0); testsec++) {
2499 dist = similar(nch->string, *testsec);
2500 if (dist < mindist) {
2501 goodsec = *testsec;
2502 mindist = dist;
2503 }
2504 }
2505 if (goodsec != NULL((void *)0))
2506 mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
2507 "Sh %s instead of %s", nch->string, goodsec);
2508 return;
2509 }
2510
2511 /*
2512 * Check whether our non-custom section is being repeated or is
2513 * out of order.
2514 */
2515
2516 if (sec == mdoc->lastnamed)
2517 mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
2518 mdoc->last->pos, "Sh %s", secnames[sec]);
2519
2520 if (sec < mdoc->lastnamed)
2521 mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
2522 mdoc->last->pos, "Sh %s", secnames[sec]);
2523
2524 /* Mark the last named section. */
2525
2526 mdoc->lastnamed = sec;
2527
2528 /* Check particular section/manual conventions. */
2529
2530 if (mdoc->meta.msec == NULL((void *)0))
2531 return;
2532
2533 goodsec = NULL((void *)0);
2534 switch (sec) {
2535 case SEC_ERRORS:
2536 if (*mdoc->meta.msec == '4')
2537 break;
2538 goodsec = "2, 3, 4, 9";
2539 /* FALLTHROUGH */
2540 case SEC_RETURN_VALUES:
2541 case SEC_LIBRARY:
2542 if (*mdoc->meta.msec == '2')
2543 break;
2544 if (*mdoc->meta.msec == '3')
2545 break;
2546 if (NULL((void *)0) == goodsec)
2547 goodsec = "2, 3, 9";
2548 /* FALLTHROUGH */
2549 case SEC_CONTEXT:
2550 if (*mdoc->meta.msec == '9')
2551 break;
2552 if (NULL((void *)0) == goodsec)
2553 goodsec = "9";
2554 mandoc_msg(MANDOCERR_SEC_MSEC,
2555 mdoc->last->line, mdoc->last->pos,
2556 "Sh %s for %s only", secnames[sec], goodsec);
2557 break;
2558 default:
2559 break;
2560 }
2561}
2562
2563static void
2564post_xr(POST_ARGSstruct roff_man *mdoc)
2565{
2566 struct roff_node *n, *nch;
2567
2568 n = mdoc->last;
2569 nch = n->child;
2570 if (nch->next == NULL((void *)0)) {
2571 mandoc_msg(MANDOCERR_XR_NOSEC,
2572 n->line, n->pos, "Xr %s", nch->string);
2573 } else {
2574 assert(nch->next == n->last)((nch->next == n->last) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c"
, 2574, __func__, "nch->next == n->last"))
;
2575 if(mandoc_xr_add(nch->next->string, nch->string,
2576 nch->line, nch->pos))
2577 mandoc_msg(MANDOCERR_XR_SELF,
2578 nch->line, nch->pos, "Xr %s %s",
2579 nch->string, nch->next->string);
2580 }
2581 post_delim_nb(mdoc);
2582}
2583
2584static void
2585post_section(POST_ARGSstruct roff_man *mdoc)
2586{
2587 struct roff_node *n, *nch;
2588 char *cp, *tag;
2589
2590 n = mdoc->last;
2591 switch (n->type) {
2592 case ROFFT_BLOCK:
2593 post_prevpar(mdoc);
2594 return;
2595 case ROFFT_HEAD:
2596 tag = NULL((void *)0);
2597 deroff(&tag, n);
2598 if (tag != NULL((void *)0)) {
2599 for (cp = tag; *cp != '\0'; cp++)
2600 if (*cp == ' ')
2601 *cp = '_';
2602 if ((nch = n->child) != NULL((void *)0) &&
2603 nch->type == ROFFT_TEXT &&
2604 strcmp(nch->string, tag) == 0)
2605 tag_put(NULL((void *)0), TAG_STRONG2, n);
2606 else
2607 tag_put(tag, TAG_FALLBACK(2147483647 - 1), n);
2608 free(tag);
2609 }
2610 post_delim(mdoc);
2611 post_hyph(mdoc);
2612 return;
2613 case ROFFT_BODY:
2614 break;
2615 default:
2616 return;
2617 }
2618 if ((nch = n->child) != NULL((void *)0) &&
2619 (nch->tok == MDOC_Pp || nch->tok == ROFF_br ||
2620 nch->tok == ROFF_sp)) {
2621 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2622 "%s after %s", roff_name[nch->tok],
2623 roff_name[n->tok]);
2624 roff_node_delete(mdoc, nch);
2625 }
2626 if ((nch = n->last) != NULL((void *)0) &&
2627 (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) {
2628 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2629 "%s at the end of %s", roff_name[nch->tok],
2630 roff_name[n->tok]);
2631 roff_node_delete(mdoc, nch);
2632 }
2633}
2634
2635static void
2636post_prevpar(POST_ARGSstruct roff_man *mdoc)
2637{
2638 struct roff_node *n, *np;
2639
2640 n = mdoc->last;
2641 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2642 return;
2643 if ((np = roff_node_prev(n)) == NULL((void *)0))
2644 return;
2645
2646 /*
2647 * Don't allow `Pp' prior to a paragraph-type
2648 * block: `Pp' or non-compact `Bd' or `Bl'.
2649 */
2650
2651 if (np->tok != MDOC_Pp && np->tok != ROFF_br)
2652 return;
2653 if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2654 return;
2655 if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2656 return;
2657 if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2658 return;
2659
2660 mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
2661 "%s before %s", roff_name[np->tok], roff_name[n->tok]);
2662 roff_node_delete(mdoc, np);
2663}
2664
2665static void
2666post_par(POST_ARGSstruct roff_man *mdoc)
2667{
2668 struct roff_node *np;
2669
2670 fn_prio = TAG_STRONG2;
2671 post_prevpar(mdoc);
2672
2673 np = mdoc->last;
2674 if (np->child != NULL((void *)0))
2675 mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
2676 "%s %s", roff_name[np->tok], np->child->string);
2677}
2678
2679static void
2680post_dd(POST_ARGSstruct roff_man *mdoc)
2681{
2682 struct roff_node *n;
2683
2684 n = mdoc->last;
2685 n->flags |= NODE_NOPRT(1 << 10);
2686
2687 if (mdoc->meta.date != NULL((void *)0)) {
2688 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
2689 free(mdoc->meta.date);
2690 } else if (mdoc->flags & MDOC_PBODY(1 << 2))
2691 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
2692 else if (mdoc->meta.title != NULL((void *)0))
2693 mandoc_msg(MANDOCERR_PROLOG_ORDER,
2694 n->line, n->pos, "Dd after Dt");
2695 else if (mdoc->meta.os != NULL((void *)0))
2696 mandoc_msg(MANDOCERR_PROLOG_ORDER,
2697 n->line, n->pos, "Dd after Os");
2698
2699 if (mdoc->quick && n != NULL((void *)0))
2700 mdoc->meta.date = mandoc_strdup("");
2701 else
2702 mdoc->meta.date = mandoc_normdate(n->child, n);
2703}
2704
2705static void
2706post_dt(POST_ARGSstruct roff_man *mdoc)
2707{
2708 struct roff_node *nn, *n;
2709 const char *cp;
2710 char *p;
2711
2712 n = mdoc->last;
2713 n->flags |= NODE_NOPRT(1 << 10);
2714
2715 if (mdoc->flags & MDOC_PBODY(1 << 2)) {
2716 mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
2717 return;
2718 }
2719
2720 if (mdoc->meta.title != NULL((void *)0))
2721 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
2722 else if (mdoc->meta.os != NULL((void *)0))
2723 mandoc_msg(MANDOCERR_PROLOG_ORDER,
2724 n->line, n->pos, "Dt after Os");
2725
2726 free(mdoc->meta.title);
2727 free(mdoc->meta.msec);
2728 free(mdoc->meta.vol);
2729 free(mdoc->meta.arch);
2730
2731 mdoc->meta.title = NULL((void *)0);
2732 mdoc->meta.msec = NULL((void *)0);
2733 mdoc->meta.vol = NULL((void *)0);
2734 mdoc->meta.arch = NULL((void *)0);
2735
2736 /* Mandatory first argument: title. */
2737
2738 nn = n->child;
2739 if (nn == NULL((void *)0) || *nn->string == '\0') {
2740 mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
2741 mdoc->meta.title = mandoc_strdup("UNTITLED");
2742 } else {
2743 mdoc->meta.title = mandoc_strdup(nn->string);
2744
2745 /* Check that all characters are uppercase. */
2746
2747 for (p = nn->string; *p != '\0'; p++)
2748 if (islower((unsigned char)*p)) {
2749 mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
2750 nn->pos + (int)(p - nn->string),
2751 "Dt %s", nn->string);
2752 break;
2753 }
2754 }
2755
2756 /* Mandatory second argument: section. */
2757
2758 if (nn != NULL((void *)0))
2759 nn = nn->next;
2760
2761 if (nn == NULL((void *)0)) {
2762 mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
2763 "Dt %s", mdoc->meta.title);
2764 mdoc->meta.vol = mandoc_strdup("LOCAL");
2765 return; /* msec and arch remain NULL. */
2766 }
2767
2768 mdoc->meta.msec = mandoc_strdup(nn->string);
2769
2770 /* Infer volume title from section number. */
2771
2772 cp = mandoc_a2msec(nn->string);
2773 if (cp == NULL((void *)0)) {
2774 mandoc_msg(MANDOCERR_MSEC_BAD,
2775 nn->line, nn->pos, "Dt ... %s", nn->string);
2776 mdoc->meta.vol = mandoc_strdup(nn->string);
2777 } else {
2778 mdoc->meta.vol = mandoc_strdup(cp);
2779 if (mdoc->filesec != '\0' &&
2780 mdoc->filesec != *nn->string &&
2781 *nn->string >= '1' && *nn->string <= '9')
2782 mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos,
2783 "*.%c vs Dt ... %c", mdoc->filesec, *nn->string);
2784 }
2785
2786 /* Optional third argument: architecture. */
2787
2788 if ((nn = nn->next) == NULL((void *)0))
2789 return;
2790
2791 for (p = nn->string; *p != '\0'; p++)
2792 *p = tolower((unsigned char)*p);
2793 mdoc->meta.arch = mandoc_strdup(nn->string);
2794
2795 /* Ignore fourth and later arguments. */
2796
2797 if ((nn = nn->next) != NULL((void *)0))
2798 mandoc_msg(MANDOCERR_ARG_EXCESS,
2799 nn->line, nn->pos, "Dt ... %s", nn->string);
2800}
2801
2802static void
2803post_bx(POST_ARGSstruct roff_man *mdoc)
2804{
2805 struct roff_node *n, *nch;
2806 const char *macro;
2807
2808 post_delim_nb(mdoc);
2809
2810 n = mdoc->last;
2811 nch = n->child;
2812
2813 if (nch != NULL((void *)0)) {
2814 macro = !strcmp(nch->string, "Open") ? "Ox" :
2815 !strcmp(nch->string, "Net") ? "Nx" :
2816 !strcmp(nch->string, "Free") ? "Fx" :
2817 !strcmp(nch->string, "DragonFly") ? "Dx" : NULL((void *)0);
2818 if (macro != NULL((void *)0))
2819 mandoc_msg(MANDOCERR_BX,
2820 n->line, n->pos, "%s", macro);
2821 mdoc->last = nch;
2822 nch = nch->next;
2823 mdoc->next = ROFF_NEXT_SIBLING;
2824 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2825 mdoc->last->flags |= NODE_NOSRC(1 << 9);
2826 mdoc->next = ROFF_NEXT_SIBLING;
2827 } else
2828 mdoc->next = ROFF_NEXT_CHILD;
2829 roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2830 mdoc->last->flags |= NODE_NOSRC(1 << 9);
2831
2832 if (nch == NULL((void *)0)) {
2833 mdoc->last = n;
2834 return;
2835 }
2836
2837 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2838 mdoc->last->flags |= NODE_NOSRC(1 << 9);
2839 mdoc->next = ROFF_NEXT_SIBLING;
2840 roff_word_alloc(mdoc, n->line, n->pos, "-");
2841 mdoc->last->flags |= NODE_NOSRC(1 << 9);
2842 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2843 mdoc->last->flags |= NODE_NOSRC(1 << 9);
2844 mdoc->last = n;
2845
2846 /*
2847 * Make `Bx's second argument always start with an uppercase
2848 * letter. Groff checks if it's an "accepted" term, but we just
2849 * uppercase blindly.
2850 */
2851
2852 *nch->string = (char)toupper((unsigned char)*nch->string);
2853}
2854
2855static void
2856post_os(POST_ARGSstruct roff_man *mdoc)
2857{
2858#ifndef OSNAME
2859 struct utsname utsname;
2860#endif
2861 struct roff_node *n;
2862
2863 n = mdoc->last;
2864 n->flags |= NODE_NOPRT(1 << 10);
2865
2866 if (mdoc->meta.os != NULL((void *)0))
2867 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
2868 else if (mdoc->flags & MDOC_PBODY(1 << 2))
2869 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
2870
2871 post_delim(mdoc);
2872
2873 /*
2874 * Set the operating system by way of the `Os' macro.
2875 * The order of precedence is:
2876 * 1. the argument of the `Os' macro, unless empty
2877 * 2. the -Ios=foo command line argument, if provided
2878 * 3. -DOSNAME="\"foo\"", if provided during compilation
2879 * 4. "sysname release" from uname(3)
2880 */
2881
2882 free(mdoc->meta.os);
2883 mdoc->meta.os = NULL((void *)0);
2884 deroff(&mdoc->meta.os, n);
2885 if (mdoc->meta.os)
2886 goto out;
2887
2888 if (mdoc->os_s != NULL((void *)0)) {
2889 mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2890 goto out;
2891 }
2892
2893#ifdef OSNAME
2894 mdoc->meta.os = mandoc_strdup(OSNAME);
2895#else /*!OSNAME */
2896 if (mdoc->os_r == NULL((void *)0)) {
2897 if (uname(&utsname) == -1) {
2898 mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
2899 mdoc->os_r = mandoc_strdup("UNKNOWN");
2900 } else
2901 mandoc_asprintf(&mdoc->os_r, "%s %s",
2902 utsname.sysname, utsname.release);
2903 }
2904 mdoc->meta.os = mandoc_strdup(mdoc->os_r);
2905#endif /*!OSNAME*/
2906
2907out:
2908 if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2909 if (strstr(mdoc->meta.os, "OpenBSD") != NULL((void *)0))
2910 mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2911 else if (strstr(mdoc->meta.os, "NetBSD") != NULL((void *)0))
2912 mdoc->meta.os_e = MANDOC_OS_NETBSD;
2913 }
2914
2915 /*
2916 * This is the earliest point where we can check
2917 * Mdocdate conventions because we don't know
2918 * the operating system earlier.
2919 */
2920
2921 if (n->child != NULL((void *)0))
2922 mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
2923 "Os %s (%s)", n->child->string,
2924 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2925 "OpenBSD" : "NetBSD");
2926
2927 while (n->tok != MDOC_Dd)
2928 if ((n = n->prev) == NULL((void *)0))
2929 return;
2930 if ((n = n->child) == NULL((void *)0))
2931 return;
2932 if (strncmp(n->string, "$" "Mdocdate", 9)) {
2933 if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2934 mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
2935 n->pos, "Dd %s (OpenBSD)", n->string);
2936 } else {
2937 if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2938 mandoc_msg(MANDOCERR_MDOCDATE, n->line,
2939 n->pos, "Dd %s (NetBSD)", n->string);
2940 }
2941}
2942
2943enum roff_sec
2944mdoc_a2sec(const char *p)
2945{
2946 int i;
2947
2948 for (i = 0; i < (int)SEC__MAX; i++)
2949 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2950 return (enum roff_sec)i;
2951
2952 return SEC_CUSTOM;
2953}
2954
2955static size_t
2956macro2len(enum roff_tok macro)
2957{
2958
2959 switch (macro) {
2960 case MDOC_Ad:
2961 return 12;
2962 case MDOC_Ao:
2963 return 12;
2964 case MDOC_An:
2965 return 12;
2966 case MDOC_Aq:
2967 return 12;
2968 case MDOC_Ar:
2969 return 12;
2970 case MDOC_Bo:
2971 return 12;
2972 case MDOC_Bq:
2973 return 12;
2974 case MDOC_Cd:
2975 return 12;
2976 case MDOC_Cm:
2977 return 10;
2978 case MDOC_Do:
2979 return 10;
2980 case MDOC_Dq:
2981 return 12;
2982 case MDOC_Dv:
2983 return 12;
2984 case MDOC_Eo:
2985 return 12;
2986 case MDOC_Em:
2987 return 10;
2988 case MDOC_Er:
2989 return 17;
2990 case MDOC_Ev:
2991 return 15;
2992 case MDOC_Fa:
2993 return 12;
2994 case MDOC_Fl:
2995 return 10;
2996 case MDOC_Fo:
2997 return 16;
2998 case MDOC_Fn:
2999 return 16;
3000 case MDOC_Ic:
3001 return 10;
3002 case MDOC_Li:
3003 return 16;
3004 case MDOC_Ms:
3005 return 6;
3006 case MDOC_Nm:
3007 return 10;
3008 case MDOC_No:
3009 return 12;
3010 case MDOC_Oo:
3011 return 10;
3012 case MDOC_Op:
3013 return 14;
3014 case MDOC_Pa:
3015 return 32;
3016 case MDOC_Pf:
3017 return 12;
3018 case MDOC_Po:
3019 return 12;
3020 case MDOC_Pq:
3021 return 12;
3022 case MDOC_Ql:
3023 return 16;
3024 case MDOC_Qo:
3025 return 12;
3026 case MDOC_So:
3027 return 12;
3028 case MDOC_Sq:
3029 return 12;
3030 case MDOC_Sy:
3031 return 6;
3032 case MDOC_Sx:
3033 return 16;
3034 case MDOC_Tn:
3035 return 10;
3036 case MDOC_Va:
3037 return 12;
3038 case MDOC_Vt:
3039 return 12;
3040 case MDOC_Xr:
3041 return 10;
3042 default:
3043 break;
3044 };
3045 return 0;
3046}