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') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: mdoc_validate.c,v 1.306 2022/06/08 16:29:12 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 | ||||
48 | enum check_ineq { | |||
49 | CHECK_LT, | |||
50 | CHECK_GT, | |||
51 | CHECK_EQ | |||
52 | }; | |||
53 | ||||
54 | typedef void (*v_post)(POST_ARGSstruct roff_man *mdoc); | |||
55 | ||||
56 | static int build_list(struct roff_man *, int); | |||
57 | static void check_argv(struct roff_man *, | |||
58 | struct roff_node *, struct mdoc_argv *); | |||
59 | static void check_args(struct roff_man *, struct roff_node *); | |||
60 | static void check_text(struct roff_man *, int, int, char *); | |||
61 | static void check_text_em(struct roff_man *, int, int, char *); | |||
62 | static void check_toptext(struct roff_man *, int, int, const char *); | |||
63 | static int child_an(const struct roff_node *); | |||
64 | static size_t macro2len(enum roff_tok); | |||
65 | static void rewrite_macro2len(struct roff_man *, char **); | |||
66 | static int similar(const char *, const char *); | |||
67 | ||||
68 | static void post_abort(POST_ARGSstruct roff_man *mdoc) __attribute__((__noreturn__)); | |||
69 | static void post_an(POST_ARGSstruct roff_man *mdoc); | |||
70 | static void post_an_norm(POST_ARGSstruct roff_man *mdoc); | |||
71 | static void post_at(POST_ARGSstruct roff_man *mdoc); | |||
72 | static void post_bd(POST_ARGSstruct roff_man *mdoc); | |||
73 | static void post_bf(POST_ARGSstruct roff_man *mdoc); | |||
74 | static void post_bk(POST_ARGSstruct roff_man *mdoc); | |||
75 | static void post_bl(POST_ARGSstruct roff_man *mdoc); | |||
76 | static void post_bl_block(POST_ARGSstruct roff_man *mdoc); | |||
77 | static void post_bl_head(POST_ARGSstruct roff_man *mdoc); | |||
78 | static void post_bl_norm(POST_ARGSstruct roff_man *mdoc); | |||
79 | static void post_bx(POST_ARGSstruct roff_man *mdoc); | |||
80 | static void post_defaults(POST_ARGSstruct roff_man *mdoc); | |||
81 | static void post_display(POST_ARGSstruct roff_man *mdoc); | |||
82 | static void post_dd(POST_ARGSstruct roff_man *mdoc); | |||
83 | static void post_delim(POST_ARGSstruct roff_man *mdoc); | |||
84 | static void post_delim_nb(POST_ARGSstruct roff_man *mdoc); | |||
85 | static void post_dt(POST_ARGSstruct roff_man *mdoc); | |||
86 | static void post_em(POST_ARGSstruct roff_man *mdoc); | |||
87 | static void post_en(POST_ARGSstruct roff_man *mdoc); | |||
88 | static void post_er(POST_ARGSstruct roff_man *mdoc); | |||
89 | static void post_es(POST_ARGSstruct roff_man *mdoc); | |||
90 | static void post_eoln(POST_ARGSstruct roff_man *mdoc); | |||
91 | static void post_ex(POST_ARGSstruct roff_man *mdoc); | |||
92 | static void post_fa(POST_ARGSstruct roff_man *mdoc); | |||
93 | static void post_fl(POST_ARGSstruct roff_man *mdoc); | |||
94 | static void post_fn(POST_ARGSstruct roff_man *mdoc); | |||
95 | static void post_fname(POST_ARGSstruct roff_man *mdoc); | |||
96 | static void post_fo(POST_ARGSstruct roff_man *mdoc); | |||
97 | static void post_hyph(POST_ARGSstruct roff_man *mdoc); | |||
98 | static void post_it(POST_ARGSstruct roff_man *mdoc); | |||
99 | static void post_lb(POST_ARGSstruct roff_man *mdoc); | |||
100 | static void post_nd(POST_ARGSstruct roff_man *mdoc); | |||
101 | static void post_nm(POST_ARGSstruct roff_man *mdoc); | |||
102 | static void post_ns(POST_ARGSstruct roff_man *mdoc); | |||
103 | static void post_obsolete(POST_ARGSstruct roff_man *mdoc); | |||
104 | static void post_os(POST_ARGSstruct roff_man *mdoc); | |||
105 | static void post_par(POST_ARGSstruct roff_man *mdoc); | |||
106 | static void post_prevpar(POST_ARGSstruct roff_man *mdoc); | |||
107 | static void post_root(POST_ARGSstruct roff_man *mdoc); | |||
108 | static void post_rs(POST_ARGSstruct roff_man *mdoc); | |||
109 | static void post_rv(POST_ARGSstruct roff_man *mdoc); | |||
110 | static void post_section(POST_ARGSstruct roff_man *mdoc); | |||
111 | static void post_sh(POST_ARGSstruct roff_man *mdoc); | |||
112 | static void post_sh_head(POST_ARGSstruct roff_man *mdoc); | |||
113 | static void post_sh_name(POST_ARGSstruct roff_man *mdoc); | |||
114 | static void post_sh_see_also(POST_ARGSstruct roff_man *mdoc); | |||
115 | static void post_sh_authors(POST_ARGSstruct roff_man *mdoc); | |||
116 | static void post_sm(POST_ARGSstruct roff_man *mdoc); | |||
117 | static void post_st(POST_ARGSstruct roff_man *mdoc); | |||
118 | static void post_std(POST_ARGSstruct roff_man *mdoc); | |||
119 | static void post_sx(POST_ARGSstruct roff_man *mdoc); | |||
120 | static void post_tag(POST_ARGSstruct roff_man *mdoc); | |||
121 | static void post_tg(POST_ARGSstruct roff_man *mdoc); | |||
122 | static void post_useless(POST_ARGSstruct roff_man *mdoc); | |||
123 | static void post_xr(POST_ARGSstruct roff_man *mdoc); | |||
124 | static void post_xx(POST_ARGSstruct roff_man *mdoc); | |||
125 | ||||
126 | static 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 | ||||
252 | static 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 | ||||
269 | static 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 | ||||
295 | static int fn_prio = TAG_STRONG2; | |||
296 | ||||
297 | ||||
298 | /* Validate the subtree rooted at mdoc->last. */ | |||
299 | void | |||
300 | mdoc_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 | ||||
394 | static void | |||
395 | check_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 | ||||
407 | static void | |||
408 | check_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 | ||||
416 | static void | |||
417 | check_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 | ||||
428 | static void | |||
429 | check_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 | ||||
479 | static void | |||
480 | check_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 | ||||
509 | static void | |||
510 | post_abort(POST_ARGSstruct roff_man *mdoc) | |||
511 | { | |||
512 | abort(); | |||
513 | } | |||
514 | ||||
515 | static void | |||
516 | post_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 | ||||
542 | static void | |||
543 | post_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 | ||||
635 | static void | |||
636 | post_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; | |||
654 | mdoclt = MDOC_ARG_MAX; | |||
655 | for (i = 0; n->args
| |||
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
| |||
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) { | |||
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)) | |||
786 | mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos, | |||
| ||||
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 | ||||
805 | static void | |||
806 | post_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 | ||||
879 | static void | |||
880 | post_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 | ||||
905 | static void | |||
906 | post_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 | ||||
925 | static int | |||
926 | build_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 | ||||
952 | static void | |||
953 | post_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 | ||||
989 | static void | |||
990 | post_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 | ||||
1009 | static void | |||
1010 | post_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 | ||||
1048 | static void | |||
1049 | post_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 | ||||
1064 | static void | |||
1065 | post_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 | ||||
1088 | static void | |||
1089 | post_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->type != ROFFT_HEAD && nn->type != ROFFT_BODY && | |||
1102 | nn->type != ROFFT_TAIL && nn->next != NULL((void *)0)) { | |||
1103 | nn = nn->next; | |||
1104 | break; | |||
1105 | } | |||
1106 | } | |||
1107 | ||||
1108 | /* Find the tag. */ | |||
1109 | nt = nch = n->child; | |||
1110 | if (nch == NULL((void *)0) && nn != NULL((void *)0) && nn->child != NULL((void *)0) && | |||
1111 | nn->child->type == ROFFT_TEXT) | |||
1112 | nt = nn->child; | |||
1113 | ||||
1114 | /* Validate the tag. */ | |||
1115 | if (nt == NULL((void *)0) || *nt->string == '\0') | |||
1116 | mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); | |||
1117 | if (nt == NULL((void *)0)) { | |||
1118 | roff_node_delete(mdoc, n); | |||
1119 | return; | |||
1120 | } | |||
1121 | len = strcspn(nt->string, " \t\\"); | |||
1122 | if (nt->string[len] != '\0') | |||
1123 | mandoc_msg(MANDOCERR_TG_SPC, nt->line, | |||
1124 | nt->pos + len, "Tg %s", nt->string); | |||
1125 | ||||
1126 | /* Keep only the first argument. */ | |||
1127 | if (nch != NULL((void *)0) && nch->next != NULL((void *)0)) { | |||
1128 | mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, | |||
1129 | nch->next->pos, "Tg ... %s", nch->next->string); | |||
1130 | while (nch->next != NULL((void *)0)) | |||
1131 | roff_node_delete(mdoc, nch->next); | |||
1132 | } | |||
1133 | ||||
1134 | /* Drop the macro if the first argument is invalid. */ | |||
1135 | if (len == 0 || nt->string[len] != '\0') { | |||
1136 | roff_node_delete(mdoc, n); | |||
1137 | return; | |||
1138 | } | |||
1139 | ||||
1140 | /* By default, tag the .Tg node itself. */ | |||
1141 | if (nn == NULL((void *)0) || nn->flags & NODE_ID(1 << 11)) | |||
1142 | nn = n; | |||
1143 | ||||
1144 | /* Explicit tagging of specific macros. */ | |||
1145 | switch (nn->tok) { | |||
1146 | case MDOC_Sh: | |||
1147 | case MDOC_Ss: | |||
1148 | case MDOC_Fo: | |||
1149 | nn = nn->head->child == NULL((void *)0) ? n : nn->head; | |||
1150 | break; | |||
1151 | case MDOC_It: | |||
1152 | np = nn->parent; | |||
1153 | while (np->tok != MDOC_Bl) | |||
1154 | np = np->parent; | |||
1155 | switch (np->norm->Bl.type) { | |||
1156 | case LIST_column: | |||
1157 | break; | |||
1158 | case LIST_diag: | |||
1159 | case LIST_hang: | |||
1160 | case LIST_inset: | |||
1161 | case LIST_ohang: | |||
1162 | case LIST_tag: | |||
1163 | nn = nn->head; | |||
1164 | break; | |||
1165 | case LIST_bullet: | |||
1166 | case LIST_dash: | |||
1167 | case LIST_enum: | |||
1168 | case LIST_hyphen: | |||
1169 | case LIST_item: | |||
1170 | nn = nn->body->child == NULL((void *)0) ? n : nn->body; | |||
1171 | break; | |||
1172 | default: | |||
1173 | abort(); | |||
1174 | } | |||
1175 | break; | |||
1176 | case MDOC_Bd: | |||
1177 | case MDOC_Bl: | |||
1178 | case MDOC_D1: | |||
1179 | case MDOC_Dl: | |||
1180 | nn = nn->body->child == NULL((void *)0) ? n : nn->body; | |||
1181 | break; | |||
1182 | case MDOC_Pp: | |||
1183 | break; | |||
1184 | case MDOC_Cm: | |||
1185 | case MDOC_Dv: | |||
1186 | case MDOC_Em: | |||
1187 | case MDOC_Er: | |||
1188 | case MDOC_Ev: | |||
1189 | case MDOC_Fl: | |||
1190 | case MDOC_Fn: | |||
1191 | case MDOC_Ic: | |||
1192 | case MDOC_Li: | |||
1193 | case MDOC_Ms: | |||
1194 | case MDOC_No: | |||
1195 | case MDOC_Sy: | |||
1196 | if (nn->child == NULL((void *)0)) | |||
1197 | nn = n; | |||
1198 | break; | |||
1199 | default: | |||
1200 | nn = n; | |||
1201 | break; | |||
1202 | } | |||
1203 | tag_put(nt->string, TAG_MANUAL1, nn); | |||
1204 | if (nn != n) | |||
1205 | n->flags |= NODE_NOPRT(1 << 10); | |||
1206 | } | |||
1207 | ||||
1208 | static void | |||
1209 | post_obsolete(POST_ARGSstruct roff_man *mdoc) | |||
1210 | { | |||
1211 | struct roff_node *n; | |||
1212 | ||||
1213 | n = mdoc->last; | |||
1214 | if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) | |||
1215 | mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, | |||
1216 | "%s", roff_name[n->tok]); | |||
1217 | } | |||
1218 | ||||
1219 | static void | |||
1220 | post_useless(POST_ARGSstruct roff_man *mdoc) | |||
1221 | { | |||
1222 | struct roff_node *n; | |||
1223 | ||||
1224 | n = mdoc->last; | |||
1225 | mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, | |||
1226 | "%s", roff_name[n->tok]); | |||
1227 | } | |||
1228 | ||||
1229 | /* | |||
1230 | * Block macros. | |||
1231 | */ | |||
1232 | ||||
1233 | static void | |||
1234 | post_bf(POST_ARGSstruct roff_man *mdoc) | |||
1235 | { | |||
1236 | struct roff_node *np, *nch; | |||
1237 | ||||
1238 | /* | |||
1239 | * Unlike other data pointers, these are "housed" by the HEAD | |||
1240 | * element, which contains the goods. | |||
1241 | */ | |||
1242 | ||||
1243 | np = mdoc->last; | |||
1244 | if (np->type != ROFFT_HEAD) | |||
1245 | return; | |||
1246 | ||||
1247 | assert(np->parent->type == ROFFT_BLOCK)((np->parent->type == ROFFT_BLOCK) ? (void)0 : __assert2 ("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1247, __func__, "np->parent->type == ROFFT_BLOCK" )); | |||
1248 | assert(np->parent->tok == MDOC_Bf)((np->parent->tok == MDOC_Bf) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 1248, __func__, "np->parent->tok == MDOC_Bf")); | |||
1249 | ||||
1250 | /* Check the number of arguments. */ | |||
1251 | ||||
1252 | nch = np->child; | |||
1253 | if (np->parent->args == NULL((void *)0)) { | |||
1254 | if (nch == NULL((void *)0)) { | |||
1255 | mandoc_msg(MANDOCERR_BF_NOFONT, | |||
1256 | np->line, np->pos, "Bf"); | |||
1257 | return; | |||
1258 | } | |||
1259 | nch = nch->next; | |||
1260 | } | |||
1261 | if (nch != NULL((void *)0)) | |||
1262 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
1263 | nch->line, nch->pos, "Bf ... %s", nch->string); | |||
1264 | ||||
1265 | /* Extract argument into data. */ | |||
1266 | ||||
1267 | if (np->parent->args != NULL((void *)0)) { | |||
1268 | switch (np->parent->args->argv[0].arg) { | |||
1269 | case MDOC_Emphasis: | |||
1270 | np->norm->Bf.font = FONT_Em; | |||
1271 | break; | |||
1272 | case MDOC_Literal: | |||
1273 | np->norm->Bf.font = FONT_Li; | |||
1274 | break; | |||
1275 | case MDOC_Symbolic: | |||
1276 | np->norm->Bf.font = FONT_Sy; | |||
1277 | break; | |||
1278 | default: | |||
1279 | abort(); | |||
1280 | } | |||
1281 | return; | |||
1282 | } | |||
1283 | ||||
1284 | /* Extract parameter into data. */ | |||
1285 | ||||
1286 | if ( ! strcmp(np->child->string, "Em")) | |||
1287 | np->norm->Bf.font = FONT_Em; | |||
1288 | else if ( ! strcmp(np->child->string, "Li")) | |||
1289 | np->norm->Bf.font = FONT_Li; | |||
1290 | else if ( ! strcmp(np->child->string, "Sy")) | |||
1291 | np->norm->Bf.font = FONT_Sy; | |||
1292 | else | |||
1293 | mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, | |||
1294 | np->child->pos, "Bf %s", np->child->string); | |||
1295 | } | |||
1296 | ||||
1297 | static void | |||
1298 | post_fname(POST_ARGSstruct roff_man *mdoc) | |||
1299 | { | |||
1300 | struct roff_node *n, *nch; | |||
1301 | const char *cp; | |||
1302 | size_t pos; | |||
1303 | ||||
1304 | n = mdoc->last; | |||
1305 | nch = n->child; | |||
1306 | cp = nch->string; | |||
1307 | if (*cp == '(') { | |||
1308 | if (cp[strlen(cp + 1)] == ')') | |||
1309 | return; | |||
1310 | pos = 0; | |||
1311 | } else { | |||
1312 | pos = strcspn(cp, "()"); | |||
1313 | if (cp[pos] == '\0') { | |||
1314 | if (n->sec == SEC_DESCRIPTION || | |||
1315 | n->sec == SEC_CUSTOM) | |||
1316 | tag_put(NULL((void *)0), fn_prio++, n); | |||
1317 | return; | |||
1318 | } | |||
1319 | } | |||
1320 | mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); | |||
1321 | } | |||
1322 | ||||
1323 | static void | |||
1324 | post_fn(POST_ARGSstruct roff_man *mdoc) | |||
1325 | { | |||
1326 | post_fname(mdoc); | |||
1327 | post_fa(mdoc); | |||
1328 | } | |||
1329 | ||||
1330 | static void | |||
1331 | post_fo(POST_ARGSstruct roff_man *mdoc) | |||
1332 | { | |||
1333 | const struct roff_node *n; | |||
1334 | ||||
1335 | n = mdoc->last; | |||
1336 | ||||
1337 | if (n->type != ROFFT_HEAD) | |||
1338 | return; | |||
1339 | ||||
1340 | if (n->child == NULL((void *)0)) { | |||
1341 | mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); | |||
1342 | return; | |||
1343 | } | |||
1344 | if (n->child != n->last) { | |||
1345 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
1346 | n->child->next->line, n->child->next->pos, | |||
1347 | "Fo ... %s", n->child->next->string); | |||
1348 | while (n->child != n->last) | |||
1349 | roff_node_delete(mdoc, n->last); | |||
1350 | } else | |||
1351 | post_delim(mdoc); | |||
1352 | ||||
1353 | post_fname(mdoc); | |||
1354 | } | |||
1355 | ||||
1356 | static void | |||
1357 | post_fa(POST_ARGSstruct roff_man *mdoc) | |||
1358 | { | |||
1359 | const struct roff_node *n; | |||
1360 | const char *cp; | |||
1361 | ||||
1362 | for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) { | |||
1363 | for (cp = n->string; *cp != '\0'; cp++) { | |||
1364 | /* Ignore callbacks and alterations. */ | |||
1365 | if (*cp == '(' || *cp == '{') | |||
1366 | break; | |||
1367 | if (*cp != ',') | |||
1368 | continue; | |||
1369 | mandoc_msg(MANDOCERR_FA_COMMA, n->line, | |||
1370 | n->pos + (int)(cp - n->string), "%s", n->string); | |||
1371 | break; | |||
1372 | } | |||
1373 | } | |||
1374 | post_delim_nb(mdoc); | |||
1375 | } | |||
1376 | ||||
1377 | static void | |||
1378 | post_nm(POST_ARGSstruct roff_man *mdoc) | |||
1379 | { | |||
1380 | struct roff_node *n; | |||
1381 | ||||
1382 | n = mdoc->last; | |||
1383 | ||||
1384 | if (n->sec == SEC_NAME && n->child != NULL((void *)0) && | |||
1385 | n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL((void *)0)) | |||
1386 | mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); | |||
1387 | ||||
1388 | if (n->last != NULL((void *)0) && n->last->tok == MDOC_Pp) | |||
1389 | roff_node_relink(mdoc, n->last); | |||
1390 | ||||
1391 | if (mdoc->meta.name == NULL((void *)0)) | |||
1392 | deroff(&mdoc->meta.name, n); | |||
1393 | ||||
1394 | if (mdoc->meta.name == NULL((void *)0) || | |||
1395 | (mdoc->lastsec == SEC_NAME && n->child == NULL((void *)0))) | |||
1396 | mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); | |||
1397 | ||||
1398 | switch (n->type) { | |||
1399 | case ROFFT_ELEM: | |||
1400 | post_delim_nb(mdoc); | |||
1401 | break; | |||
1402 | case ROFFT_HEAD: | |||
1403 | post_delim(mdoc); | |||
1404 | break; | |||
1405 | default: | |||
1406 | return; | |||
1407 | } | |||
1408 | ||||
1409 | if ((n->child != NULL((void *)0) && n->child->type == ROFFT_TEXT) || | |||
1410 | mdoc->meta.name == NULL((void *)0)) | |||
1411 | return; | |||
1412 | ||||
1413 | mdoc->next = ROFF_NEXT_CHILD; | |||
1414 | roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | |||
1415 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1416 | mdoc->last = n; | |||
1417 | } | |||
1418 | ||||
1419 | static void | |||
1420 | post_nd(POST_ARGSstruct roff_man *mdoc) | |||
1421 | { | |||
1422 | struct roff_node *n; | |||
1423 | ||||
1424 | n = mdoc->last; | |||
1425 | ||||
1426 | if (n->type != ROFFT_BODY) | |||
1427 | return; | |||
1428 | ||||
1429 | if (n->sec != SEC_NAME) | |||
1430 | mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); | |||
1431 | ||||
1432 | if (n->child == NULL((void *)0)) | |||
1433 | mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); | |||
1434 | else | |||
1435 | post_delim(mdoc); | |||
1436 | ||||
1437 | post_hyph(mdoc); | |||
1438 | } | |||
1439 | ||||
1440 | static void | |||
1441 | post_display(POST_ARGSstruct roff_man *mdoc) | |||
1442 | { | |||
1443 | struct roff_node *n, *np; | |||
1444 | ||||
1445 | n = mdoc->last; | |||
1446 | switch (n->type) { | |||
1447 | case ROFFT_BODY: | |||
1448 | if (n->end != ENDBODY_NOT) { | |||
1449 | if (n->tok == MDOC_Bd && | |||
1450 | n->body->parent->args == NULL((void *)0)) | |||
1451 | roff_node_delete(mdoc, n); | |||
1452 | } else if (n->child == NULL((void *)0)) | |||
1453 | mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, | |||
1454 | "%s", roff_name[n->tok]); | |||
1455 | else if (n->tok == MDOC_D1) | |||
1456 | post_hyph(mdoc); | |||
1457 | break; | |||
1458 | case ROFFT_BLOCK: | |||
1459 | if (n->tok == MDOC_Bd) { | |||
1460 | if (n->args == NULL((void *)0)) { | |||
1461 | mandoc_msg(MANDOCERR_BD_NOARG, | |||
1462 | n->line, n->pos, "Bd"); | |||
1463 | mdoc->next = ROFF_NEXT_SIBLING; | |||
1464 | while (n->body->child != NULL((void *)0)) | |||
1465 | roff_node_relink(mdoc, | |||
1466 | n->body->child); | |||
1467 | roff_node_delete(mdoc, n); | |||
1468 | break; | |||
1469 | } | |||
1470 | post_bd(mdoc); | |||
1471 | post_prevpar(mdoc); | |||
1472 | } | |||
1473 | for (np = n->parent; np != NULL((void *)0); np = np->parent) { | |||
1474 | if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { | |||
1475 | mandoc_msg(MANDOCERR_BD_NEST, n->line, | |||
1476 | n->pos, "%s in Bd", roff_name[n->tok]); | |||
1477 | break; | |||
1478 | } | |||
1479 | } | |||
1480 | break; | |||
1481 | default: | |||
1482 | break; | |||
1483 | } | |||
1484 | } | |||
1485 | ||||
1486 | static void | |||
1487 | post_defaults(POST_ARGSstruct roff_man *mdoc) | |||
1488 | { | |||
1489 | struct roff_node *n; | |||
1490 | ||||
1491 | n = mdoc->last; | |||
1492 | if (n->child != NULL((void *)0)) { | |||
1493 | post_delim_nb(mdoc); | |||
1494 | return; | |||
1495 | } | |||
1496 | mdoc->next = ROFF_NEXT_CHILD; | |||
1497 | switch (n->tok) { | |||
1498 | case MDOC_Ar: | |||
1499 | roff_word_alloc(mdoc, n->line, n->pos, "file"); | |||
1500 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1501 | roff_word_alloc(mdoc, n->line, n->pos, "..."); | |||
1502 | break; | |||
1503 | case MDOC_Pa: | |||
1504 | case MDOC_Mt: | |||
1505 | roff_word_alloc(mdoc, n->line, n->pos, "~"); | |||
1506 | break; | |||
1507 | default: | |||
1508 | abort(); | |||
1509 | } | |||
1510 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1511 | mdoc->last = n; | |||
1512 | } | |||
1513 | ||||
1514 | static void | |||
1515 | post_at(POST_ARGSstruct roff_man *mdoc) | |||
1516 | { | |||
1517 | struct roff_node *n, *nch; | |||
1518 | const char *att; | |||
1519 | ||||
1520 | n = mdoc->last; | |||
1521 | nch = n->child; | |||
1522 | ||||
1523 | /* | |||
1524 | * If we have a child, look it up in the standard keys. If a | |||
1525 | * key exist, use that instead of the child; if it doesn't, | |||
1526 | * prefix "AT&T UNIX " to the existing data. | |||
1527 | */ | |||
1528 | ||||
1529 | att = NULL((void *)0); | |||
1530 | if (nch != NULL((void *)0) && ((att = mdoc_a2att(nch->string)) == NULL((void *)0))) | |||
1531 | mandoc_msg(MANDOCERR_AT_BAD, | |||
1532 | nch->line, nch->pos, "At %s", nch->string); | |||
1533 | ||||
1534 | mdoc->next = ROFF_NEXT_CHILD; | |||
1535 | if (att != NULL((void *)0)) { | |||
1536 | roff_word_alloc(mdoc, nch->line, nch->pos, att); | |||
1537 | nch->flags |= NODE_NOPRT(1 << 10); | |||
1538 | } else | |||
1539 | roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); | |||
1540 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1541 | mdoc->last = n; | |||
1542 | } | |||
1543 | ||||
1544 | static void | |||
1545 | post_an(POST_ARGSstruct roff_man *mdoc) | |||
1546 | { | |||
1547 | struct roff_node *np, *nch; | |||
1548 | ||||
1549 | post_an_norm(mdoc); | |||
1550 | ||||
1551 | np = mdoc->last; | |||
1552 | nch = np->child; | |||
1553 | if (np->norm->An.auth == AUTH__NONE) { | |||
1554 | if (nch == NULL((void *)0)) | |||
1555 | mandoc_msg(MANDOCERR_MACRO_EMPTY, | |||
1556 | np->line, np->pos, "An"); | |||
1557 | else | |||
1558 | post_delim_nb(mdoc); | |||
1559 | } else if (nch != NULL((void *)0)) | |||
1560 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
1561 | nch->line, nch->pos, "An ... %s", nch->string); | |||
1562 | } | |||
1563 | ||||
1564 | static void | |||
1565 | post_em(POST_ARGSstruct roff_man *mdoc) | |||
1566 | { | |||
1567 | post_tag(mdoc); | |||
1568 | tag_put(NULL((void *)0), TAG_FALLBACK(0x7fffffff - 1), mdoc->last); | |||
1569 | } | |||
1570 | ||||
1571 | static void | |||
1572 | post_en(POST_ARGSstruct roff_man *mdoc) | |||
1573 | { | |||
1574 | post_obsolete(mdoc); | |||
1575 | if (mdoc->last->type == ROFFT_BLOCK) | |||
1576 | mdoc->last->norm->Es = mdoc->last_es; | |||
1577 | } | |||
1578 | ||||
1579 | static void | |||
1580 | post_er(POST_ARGSstruct roff_man *mdoc) | |||
1581 | { | |||
1582 | struct roff_node *n; | |||
1583 | ||||
1584 | n = mdoc->last; | |||
1585 | if (n->sec == SEC_ERRORS && | |||
1586 | (n->parent->tok == MDOC_It || | |||
1587 | (n->parent->tok == MDOC_Bq && | |||
1588 | n->parent->parent->parent->tok == MDOC_It))) | |||
1589 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
1590 | post_delim_nb(mdoc); | |||
1591 | } | |||
1592 | ||||
1593 | static void | |||
1594 | post_tag(POST_ARGSstruct roff_man *mdoc) | |||
1595 | { | |||
1596 | struct roff_node *n; | |||
1597 | ||||
1598 | n = mdoc->last; | |||
1599 | if ((n->prev == NULL((void *)0) || | |||
1600 | (n->prev->type == ROFFT_TEXT && | |||
1601 | strcmp(n->prev->string, "|") == 0)) && | |||
1602 | (n->parent->tok == MDOC_It || | |||
1603 | (n->parent->tok == MDOC_Xo && | |||
1604 | n->parent->parent->prev == NULL((void *)0) && | |||
1605 | n->parent->parent->parent->tok == MDOC_It))) | |||
1606 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
1607 | post_delim_nb(mdoc); | |||
1608 | } | |||
1609 | ||||
1610 | static void | |||
1611 | post_es(POST_ARGSstruct roff_man *mdoc) | |||
1612 | { | |||
1613 | post_obsolete(mdoc); | |||
1614 | mdoc->last_es = mdoc->last; | |||
1615 | } | |||
1616 | ||||
1617 | static void | |||
1618 | post_fl(POST_ARGSstruct roff_man *mdoc) | |||
1619 | { | |||
1620 | struct roff_node *n; | |||
1621 | char *cp; | |||
1622 | ||||
1623 | /* | |||
1624 | * Transform ".Fl Fl long" to ".Fl \-long", | |||
1625 | * resulting for example in better HTML output. | |||
1626 | */ | |||
1627 | ||||
1628 | n = mdoc->last; | |||
1629 | if (n->prev != NULL((void *)0) && n->prev->tok == MDOC_Fl && | |||
1630 | n->prev->child == NULL((void *)0) && n->child != NULL((void *)0) && | |||
1631 | (n->flags & NODE_LINE(1 << 3)) == 0) { | |||
1632 | mandoc_asprintf(&cp, "\\-%s", n->child->string); | |||
1633 | free(n->child->string); | |||
1634 | n->child->string = cp; | |||
1635 | roff_node_delete(mdoc, n->prev); | |||
1636 | } | |||
1637 | post_tag(mdoc); | |||
1638 | } | |||
1639 | ||||
1640 | static void | |||
1641 | post_xx(POST_ARGSstruct roff_man *mdoc) | |||
1642 | { | |||
1643 | struct roff_node *n; | |||
1644 | const char *os; | |||
1645 | char *v; | |||
1646 | ||||
1647 | post_delim_nb(mdoc); | |||
1648 | ||||
1649 | n = mdoc->last; | |||
1650 | switch (n->tok) { | |||
1651 | case MDOC_Bsx: | |||
1652 | os = "BSD/OS"; | |||
1653 | break; | |||
1654 | case MDOC_Dx: | |||
1655 | os = "DragonFly"; | |||
1656 | break; | |||
1657 | case MDOC_Fx: | |||
1658 | os = "FreeBSD"; | |||
1659 | break; | |||
1660 | case MDOC_Nx: | |||
1661 | os = "NetBSD"; | |||
1662 | if (n->child == NULL((void *)0)) | |||
1663 | break; | |||
1664 | v = n->child->string; | |||
1665 | if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || | |||
1666 | v[2] < '0' || v[2] > '9' || | |||
1667 | v[3] < 'a' || v[3] > 'z' || v[4] != '\0') | |||
1668 | break; | |||
1669 | n->child->flags |= NODE_NOPRT(1 << 10); | |||
1670 | mdoc->next = ROFF_NEXT_CHILD; | |||
1671 | roff_word_alloc(mdoc, n->child->line, n->child->pos, v); | |||
1672 | v = mdoc->last->string; | |||
1673 | v[3] = toupper((unsigned char)v[3]); | |||
1674 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1675 | mdoc->last = n; | |||
1676 | break; | |||
1677 | case MDOC_Ox: | |||
1678 | os = "OpenBSD"; | |||
1679 | break; | |||
1680 | case MDOC_Ux: | |||
1681 | os = "UNIX"; | |||
1682 | break; | |||
1683 | default: | |||
1684 | abort(); | |||
1685 | } | |||
1686 | mdoc->next = ROFF_NEXT_CHILD; | |||
1687 | roff_word_alloc(mdoc, n->line, n->pos, os); | |||
1688 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
1689 | mdoc->last = n; | |||
1690 | } | |||
1691 | ||||
1692 | static void | |||
1693 | post_it(POST_ARGSstruct roff_man *mdoc) | |||
1694 | { | |||
1695 | struct roff_node *nbl, *nit, *nch; | |||
1696 | int i, cols; | |||
1697 | enum mdoc_list lt; | |||
1698 | ||||
1699 | post_prevpar(mdoc); | |||
1700 | ||||
1701 | nit = mdoc->last; | |||
1702 | if (nit->type != ROFFT_BLOCK) | |||
1703 | return; | |||
1704 | ||||
1705 | nbl = nit->parent->parent; | |||
1706 | lt = nbl->norm->Bl.type; | |||
1707 | ||||
1708 | switch (lt) { | |||
1709 | case LIST_tag: | |||
1710 | case LIST_hang: | |||
1711 | case LIST_ohang: | |||
1712 | case LIST_inset: | |||
1713 | case LIST_diag: | |||
1714 | if (nit->head->child == NULL((void *)0)) | |||
1715 | mandoc_msg(MANDOCERR_IT_NOHEAD, | |||
1716 | nit->line, nit->pos, "Bl -%s It", | |||
1717 | mdoc_argnames[nbl->args->argv[0].arg]); | |||
1718 | break; | |||
1719 | case LIST_bullet: | |||
1720 | case LIST_dash: | |||
1721 | case LIST_enum: | |||
1722 | case LIST_hyphen: | |||
1723 | if (nit->body == NULL((void *)0) || nit->body->child == NULL((void *)0)) | |||
1724 | mandoc_msg(MANDOCERR_IT_NOBODY, | |||
1725 | nit->line, nit->pos, "Bl -%s It", | |||
1726 | mdoc_argnames[nbl->args->argv[0].arg]); | |||
1727 | /* FALLTHROUGH */ | |||
1728 | case LIST_item: | |||
1729 | if ((nch = nit->head->child) != NULL((void *)0)) | |||
1730 | mandoc_msg(MANDOCERR_ARG_SKIP, | |||
1731 | nit->line, nit->pos, "It %s", | |||
1732 | nch->type == ROFFT_TEXT ? nch->string : | |||
1733 | roff_name[nch->tok]); | |||
1734 | break; | |||
1735 | case LIST_column: | |||
1736 | cols = (int)nbl->norm->Bl.ncols; | |||
1737 | ||||
1738 | assert(nit->head->child == NULL)((nit->head->child == ((void *)0)) ? (void)0 : __assert2 ("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1738, __func__, "nit->head->child == NULL" )); | |||
1739 | ||||
1740 | if (nit->head->next->child == NULL((void *)0) && | |||
1741 | nit->head->next->next == NULL((void *)0)) { | |||
1742 | mandoc_msg(MANDOCERR_MACRO_EMPTY, | |||
1743 | nit->line, nit->pos, "It"); | |||
1744 | roff_node_delete(mdoc, nit); | |||
1745 | break; | |||
1746 | } | |||
1747 | ||||
1748 | i = 0; | |||
1749 | for (nch = nit->child; nch != NULL((void *)0); nch = nch->next) { | |||
1750 | if (nch->type != ROFFT_BODY) | |||
1751 | continue; | |||
1752 | if (i++ && nch->flags & NODE_LINE(1 << 3)) | |||
1753 | mandoc_msg(MANDOCERR_TA_LINE, | |||
1754 | nch->line, nch->pos, "Ta"); | |||
1755 | } | |||
1756 | if (i < cols || i > cols + 1) | |||
1757 | mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, | |||
1758 | "%d columns, %d cells", cols, i); | |||
1759 | else if (nit->head->next->child != NULL((void *)0) && | |||
1760 | nit->head->next->child->flags & NODE_LINE(1 << 3)) | |||
1761 | mandoc_msg(MANDOCERR_IT_NOARG, | |||
1762 | nit->line, nit->pos, "Bl -column It"); | |||
1763 | break; | |||
1764 | default: | |||
1765 | abort(); | |||
1766 | } | |||
1767 | } | |||
1768 | ||||
1769 | static void | |||
1770 | post_bl_block(POST_ARGSstruct roff_man *mdoc) | |||
1771 | { | |||
1772 | struct roff_node *n, *ni, *nc; | |||
1773 | ||||
1774 | post_prevpar(mdoc); | |||
1775 | ||||
1776 | n = mdoc->last; | |||
1777 | for (ni = n->body->child; ni != NULL((void *)0); ni = ni->next) { | |||
1778 | if (ni->body == NULL((void *)0)) | |||
1779 | continue; | |||
1780 | nc = ni->body->last; | |||
1781 | while (nc != NULL((void *)0)) { | |||
1782 | switch (nc->tok) { | |||
1783 | case MDOC_Pp: | |||
1784 | case ROFF_br: | |||
1785 | break; | |||
1786 | default: | |||
1787 | nc = NULL((void *)0); | |||
1788 | continue; | |||
1789 | } | |||
1790 | if (ni->next == NULL((void *)0)) { | |||
1791 | mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, | |||
1792 | nc->pos, "%s", roff_name[nc->tok]); | |||
1793 | roff_node_relink(mdoc, nc); | |||
1794 | } else if (n->norm->Bl.comp == 0 && | |||
1795 | n->norm->Bl.type != LIST_column) { | |||
1796 | mandoc_msg(MANDOCERR_PAR_SKIP, | |||
1797 | nc->line, nc->pos, | |||
1798 | "%s before It", roff_name[nc->tok]); | |||
1799 | roff_node_delete(mdoc, nc); | |||
1800 | } else | |||
1801 | break; | |||
1802 | nc = ni->body->last; | |||
1803 | } | |||
1804 | } | |||
1805 | } | |||
1806 | ||||
1807 | /* | |||
1808 | * If the argument of -offset or -width is a macro, | |||
1809 | * replace it with the associated default width. | |||
1810 | */ | |||
1811 | static void | |||
1812 | rewrite_macro2len(struct roff_man *mdoc, char **arg) | |||
1813 | { | |||
1814 | size_t width; | |||
1815 | enum roff_tok tok; | |||
1816 | ||||
1817 | if (*arg == NULL((void *)0)) | |||
1818 | return; | |||
1819 | else if ( ! strcmp(*arg, "Ds")) | |||
1820 | width = 6; | |||
1821 | else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) | |||
1822 | return; | |||
1823 | else | |||
1824 | width = macro2len(tok); | |||
1825 | ||||
1826 | free(*arg); | |||
1827 | mandoc_asprintf(arg, "%zun", width); | |||
1828 | } | |||
1829 | ||||
1830 | static void | |||
1831 | post_bl_head(POST_ARGSstruct roff_man *mdoc) | |||
1832 | { | |||
1833 | struct roff_node *nbl, *nh, *nch, *nnext; | |||
1834 | struct mdoc_argv *argv; | |||
1835 | int i, j; | |||
1836 | ||||
1837 | post_bl_norm(mdoc); | |||
1838 | ||||
1839 | nh = mdoc->last; | |||
1840 | if (nh->norm->Bl.type != LIST_column) { | |||
1841 | if ((nch = nh->child) == NULL((void *)0)) | |||
1842 | return; | |||
1843 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
1844 | nch->line, nch->pos, "Bl ... %s", nch->string); | |||
1845 | while (nch != NULL((void *)0)) { | |||
1846 | roff_node_delete(mdoc, nch); | |||
1847 | nch = nh->child; | |||
1848 | } | |||
1849 | return; | |||
1850 | } | |||
1851 | ||||
1852 | /* | |||
1853 | * Append old-style lists, where the column width specifiers | |||
1854 | * trail as macro parameters, to the new-style ("normal-form") | |||
1855 | * lists where they're argument values following -column. | |||
1856 | */ | |||
1857 | ||||
1858 | if (nh->child == NULL((void *)0)) | |||
1859 | return; | |||
1860 | ||||
1861 | nbl = nh->parent; | |||
1862 | for (j = 0; j < (int)nbl->args->argc; j++) | |||
1863 | if (nbl->args->argv[j].arg == MDOC_Column) | |||
1864 | break; | |||
1865 | ||||
1866 | assert(j < (int)nbl->args->argc)((j < (int)nbl->args->argc) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 1866, __func__, "j < (int)nbl->args->argc")); | |||
1867 | ||||
1868 | /* | |||
1869 | * Accommodate for new-style groff column syntax. Shuffle the | |||
1870 | * child nodes, all of which must be TEXT, as arguments for the | |||
1871 | * column field. Then, delete the head children. | |||
1872 | */ | |||
1873 | ||||
1874 | argv = nbl->args->argv + j; | |||
1875 | i = argv->sz; | |||
1876 | for (nch = nh->child; nch != NULL((void *)0); nch = nch->next) | |||
1877 | argv->sz++; | |||
1878 | argv->value = mandoc_reallocarray(argv->value, | |||
1879 | argv->sz, sizeof(char *)); | |||
1880 | ||||
1881 | nh->norm->Bl.ncols = argv->sz; | |||
1882 | nh->norm->Bl.cols = (void *)argv->value; | |||
1883 | ||||
1884 | for (nch = nh->child; nch != NULL((void *)0); nch = nnext) { | |||
1885 | argv->value[i++] = nch->string; | |||
1886 | nch->string = NULL((void *)0); | |||
1887 | nnext = nch->next; | |||
1888 | roff_node_delete(NULL((void *)0), nch); | |||
1889 | } | |||
1890 | nh->child = NULL((void *)0); | |||
1891 | } | |||
1892 | ||||
1893 | static void | |||
1894 | post_bl(POST_ARGSstruct roff_man *mdoc) | |||
1895 | { | |||
1896 | struct roff_node *nbody; /* of the Bl */ | |||
1897 | struct roff_node *nchild, *nnext; /* of the Bl body */ | |||
1898 | const char *prev_Er; | |||
1899 | int order; | |||
1900 | ||||
1901 | nbody = mdoc->last; | |||
1902 | switch (nbody->type) { | |||
| ||||
1903 | case ROFFT_BLOCK: | |||
1904 | post_bl_block(mdoc); | |||
1905 | return; | |||
1906 | case ROFFT_HEAD: | |||
1907 | post_bl_head(mdoc); | |||
1908 | return; | |||
1909 | case ROFFT_BODY: | |||
1910 | break; | |||
1911 | default: | |||
1912 | return; | |||
1913 | } | |||
1914 | if (nbody->end != ENDBODY_NOT) | |||
1915 | return; | |||
1916 | ||||
1917 | /* | |||
1918 | * Up to the first item, move nodes before the list, | |||
1919 | * but leave transparent nodes where they are | |||
1920 | * if they precede an item. | |||
1921 | * The next non-transparent node is kept in nchild. | |||
1922 | * It only needs to be updated after a non-transparent | |||
1923 | * node was moved out, and at the very beginning | |||
1924 | * when no node at all was moved yet. | |||
1925 | */ | |||
1926 | ||||
1927 | nchild = mdoc->last; | |||
1928 | for (;;) { | |||
1929 | if (nchild == mdoc->last) | |||
1930 | nchild = roff_node_child(nbody); | |||
1931 | if (nchild == NULL((void *)0)) { | |||
1932 | mdoc->last = nbody; | |||
1933 | mandoc_msg(MANDOCERR_BLK_EMPTY, | |||
1934 | nbody->line, nbody->pos, "Bl"); | |||
1935 | return; | |||
1936 | } | |||
1937 | if (nchild->tok == MDOC_It) { | |||
1938 | mdoc->last = nbody; | |||
1939 | break; | |||
1940 | } | |||
1941 | mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, | |||
1942 | nbody->child->pos, "%s", roff_name[nbody->child->tok]); | |||
1943 | if (nbody->parent->prev == NULL((void *)0)) { | |||
1944 | mdoc->last = nbody->parent->parent; | |||
1945 | mdoc->next = ROFF_NEXT_CHILD; | |||
1946 | } else { | |||
1947 | mdoc->last = nbody->parent->prev; | |||
1948 | mdoc->next = ROFF_NEXT_SIBLING; | |||
1949 | } | |||
1950 | roff_node_relink(mdoc, nbody->child); | |||
1951 | } | |||
1952 | ||||
1953 | /* | |||
1954 | * We have reached the first item, | |||
1955 | * so moving nodes out is no longer possible. | |||
1956 | * But in .Bl -column, the first rows may be implicit, | |||
1957 | * that is, they may not start with .It macros. | |||
1958 | * Such rows may be followed by nodes generated on the | |||
1959 | * roff level, for example .TS. | |||
1960 | * Wrap such roff nodes into an implicit row. | |||
1961 | */ | |||
1962 | ||||
1963 | while (nchild != NULL((void *)0)) { | |||
1964 | if (nchild->tok == MDOC_It) { | |||
1965 | nchild = roff_node_next(nchild); | |||
1966 | continue; | |||
1967 | } | |||
1968 | nnext = nchild->next; | |||
1969 | mdoc->last = nchild->prev; | |||
1970 | mdoc->next = ROFF_NEXT_SIBLING; | |||
1971 | roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
1972 | roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
1973 | mdoc->next = ROFF_NEXT_SIBLING; | |||
1974 | roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
1975 | while (nchild->tok != MDOC_It) { | |||
1976 | roff_node_relink(mdoc, nchild); | |||
1977 | if (nnext == NULL((void *)0)) | |||
1978 | break; | |||
1979 | nchild = nnext; | |||
1980 | nnext = nchild->next; | |||
1981 | mdoc->next = ROFF_NEXT_SIBLING; | |||
1982 | } | |||
1983 | mdoc->last = nbody; | |||
1984 | } | |||
1985 | ||||
1986 | if (mdoc->meta.os_e != MANDOC_OS_NETBSD) | |||
1987 | return; | |||
1988 | ||||
1989 | prev_Er = NULL((void *)0); | |||
1990 | for (nchild = nbody->child; nchild != NULL((void *)0); nchild = nchild->next) { | |||
1991 | if (nchild->tok != MDOC_It) | |||
1992 | continue; | |||
1993 | if ((nnext = nchild->head->child) == NULL((void *)0)) | |||
1994 | continue; | |||
1995 | if (nnext->type == ROFFT_BLOCK) | |||
1996 | nnext = nnext->body->child; | |||
1997 | if (nnext == NULL((void *)0) || nnext->tok != MDOC_Er) | |||
1998 | continue; | |||
1999 | nnext = nnext->child; | |||
2000 | if (prev_Er != NULL((void *)0)) { | |||
2001 | order = strcmp(prev_Er, nnext->string); | |||
2002 | if (order > 0) | |||
2003 | mandoc_msg(MANDOCERR_ER_ORDER, | |||
2004 | nnext->line, nnext->pos, | |||
2005 | "Er %s %s (NetBSD)", | |||
2006 | prev_Er, nnext->string); | |||
2007 | else if (order == 0) | |||
2008 | mandoc_msg(MANDOCERR_ER_REP, | |||
2009 | nnext->line, nnext->pos, | |||
2010 | "Er %s (NetBSD)", prev_Er); | |||
2011 | } | |||
2012 | prev_Er = nnext->string; | |||
2013 | } | |||
2014 | } | |||
2015 | ||||
2016 | static void | |||
2017 | post_bk(POST_ARGSstruct roff_man *mdoc) | |||
2018 | { | |||
2019 | struct roff_node *n; | |||
2020 | ||||
2021 | n = mdoc->last; | |||
2022 | ||||
2023 | if (n->type == ROFFT_BLOCK && n->body->child == NULL((void *)0)) { | |||
2024 | mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); | |||
2025 | roff_node_delete(mdoc, n); | |||
2026 | } | |||
2027 | } | |||
2028 | ||||
2029 | static void | |||
2030 | post_sm(POST_ARGSstruct roff_man *mdoc) | |||
2031 | { | |||
2032 | struct roff_node *nch; | |||
2033 | ||||
2034 | nch = mdoc->last->child; | |||
2035 | ||||
2036 | if (nch == NULL((void *)0)) { | |||
2037 | mdoc->flags ^= MDOC_SMOFF(1 << 9); | |||
2038 | return; | |||
2039 | } | |||
2040 | ||||
2041 | assert(nch->type == ROFFT_TEXT)((nch->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 2041, __func__, "nch->type == ROFFT_TEXT")); | |||
2042 | ||||
2043 | if ( ! strcmp(nch->string, "on")) { | |||
2044 | mdoc->flags &= ~MDOC_SMOFF(1 << 9); | |||
2045 | return; | |||
2046 | } | |||
2047 | if ( ! strcmp(nch->string, "off")) { | |||
2048 | mdoc->flags |= MDOC_SMOFF(1 << 9); | |||
2049 | return; | |||
2050 | } | |||
2051 | ||||
2052 | mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, | |||
2053 | "%s %s", roff_name[mdoc->last->tok], nch->string); | |||
2054 | roff_node_relink(mdoc, nch); | |||
2055 | return; | |||
2056 | } | |||
2057 | ||||
2058 | static void | |||
2059 | post_root(POST_ARGSstruct roff_man *mdoc) | |||
2060 | { | |||
2061 | struct roff_node *n; | |||
2062 | ||||
2063 | /* Add missing prologue data. */ | |||
2064 | ||||
2065 | if (mdoc->meta.date == NULL((void *)0)) | |||
2066 | mdoc->meta.date = mandoc_normdate(NULL((void *)0), NULL((void *)0)); | |||
2067 | ||||
2068 | if (mdoc->meta.title == NULL((void *)0)) { | |||
2069 | mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); | |||
2070 | mdoc->meta.title = mandoc_strdup("UNTITLED"); | |||
2071 | } | |||
2072 | ||||
2073 | if (mdoc->meta.vol == NULL((void *)0)) | |||
2074 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |||
2075 | ||||
2076 | if (mdoc->meta.os == NULL((void *)0)) { | |||
2077 | mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL((void *)0)); | |||
2078 | mdoc->meta.os = mandoc_strdup(""); | |||
2079 | } else if (mdoc->meta.os_e && | |||
2080 | (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) | |||
2081 | mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, | |||
2082 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
2083 | "(OpenBSD)" : "(NetBSD)"); | |||
2084 | ||||
2085 | if (mdoc->meta.arch != NULL((void *)0) && | |||
2086 | arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { | |||
2087 | n = mdoc->meta.first->child; | |||
2088 | while (n->tok != MDOC_Dt || | |||
2089 | n->child == NULL((void *)0) || | |||
2090 | n->child->next == NULL((void *)0) || | |||
2091 | n->child->next->next == NULL((void *)0)) | |||
2092 | n = n->next; | |||
2093 | n = n->child->next->next; | |||
2094 | mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, | |||
2095 | "Dt ... %s %s", mdoc->meta.arch, | |||
2096 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
2097 | "(OpenBSD)" : "(NetBSD)"); | |||
2098 | } | |||
2099 | ||||
2100 | /* Check that we begin with a proper `Sh'. */ | |||
2101 | ||||
2102 | n = mdoc->meta.first->child; | |||
2103 | while (n != NULL((void *)0) && | |||
2104 | (n->type == ROFFT_COMMENT || | |||
2105 | (n->tok >= MDOC_Dd && | |||
2106 | mdoc_macro(n->tok)->flags & MDOC_PROLOGUE(1 << 3)))) | |||
2107 | n = n->next; | |||
2108 | ||||
2109 | if (n == NULL((void *)0)) | |||
2110 | mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL((void *)0)); | |||
2111 | else if (n->tok != MDOC_Sh) | |||
2112 | mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, | |||
2113 | "%s", roff_name[n->tok]); | |||
2114 | } | |||
2115 | ||||
2116 | static void | |||
2117 | post_rs(POST_ARGSstruct roff_man *mdoc) | |||
2118 | { | |||
2119 | struct roff_node *np, *nch, *next, *prev; | |||
2120 | int i, j; | |||
2121 | ||||
2122 | np = mdoc->last; | |||
2123 | ||||
2124 | if (np->type != ROFFT_BODY) | |||
2125 | return; | |||
2126 | ||||
2127 | if (np->child == NULL((void *)0)) { | |||
2128 | mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); | |||
2129 | return; | |||
2130 | } | |||
2131 | ||||
2132 | /* | |||
2133 | * The full `Rs' block needs special handling to order the | |||
2134 | * sub-elements according to `rsord'. Pick through each element | |||
2135 | * and correctly order it. This is an insertion sort. | |||
2136 | */ | |||
2137 | ||||
2138 | next = NULL((void *)0); | |||
2139 | for (nch = np->child->next; nch != NULL((void *)0); nch = next) { | |||
2140 | /* Determine order number of this child. */ | |||
2141 | for (i = 0; i < RSORD_MAX14; i++) | |||
2142 | if (rsord[i] == nch->tok) | |||
2143 | break; | |||
2144 | ||||
2145 | if (i == RSORD_MAX14) { | |||
2146 | mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, | |||
2147 | "%s", roff_name[nch->tok]); | |||
2148 | i = -1; | |||
2149 | } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) | |||
2150 | np->norm->Rs.quote_T++; | |||
2151 | ||||
2152 | /* | |||
2153 | * Remove this child from the chain. This somewhat | |||
2154 | * repeats roff_node_unlink(), but since we're | |||
2155 | * just re-ordering, there's no need for the | |||
2156 | * full unlink process. | |||
2157 | */ | |||
2158 | ||||
2159 | if ((next = nch->next) != NULL((void *)0)) | |||
2160 | next->prev = nch->prev; | |||
2161 | ||||
2162 | if ((prev = nch->prev) != NULL((void *)0)) | |||
2163 | prev->next = nch->next; | |||
2164 | ||||
2165 | nch->prev = nch->next = NULL((void *)0); | |||
2166 | ||||
2167 | /* | |||
2168 | * Scan back until we reach a node that's | |||
2169 | * to be ordered before this child. | |||
2170 | */ | |||
2171 | ||||
2172 | for ( ; prev ; prev = prev->prev) { | |||
2173 | /* Determine order of `prev'. */ | |||
2174 | for (j = 0; j < RSORD_MAX14; j++) | |||
2175 | if (rsord[j] == prev->tok) | |||
2176 | break; | |||
2177 | if (j == RSORD_MAX14) | |||
2178 | j = -1; | |||
2179 | ||||
2180 | if (j <= i) | |||
2181 | break; | |||
2182 | } | |||
2183 | ||||
2184 | /* | |||
2185 | * Set this child back into its correct place | |||
2186 | * in front of the `prev' node. | |||
2187 | */ | |||
2188 | ||||
2189 | nch->prev = prev; | |||
2190 | ||||
2191 | if (prev == NULL((void *)0)) { | |||
2192 | np->child->prev = nch; | |||
2193 | nch->next = np->child; | |||
2194 | np->child = nch; | |||
2195 | } else { | |||
2196 | if (prev->next) | |||
2197 | prev->next->prev = nch; | |||
2198 | nch->next = prev->next; | |||
2199 | prev->next = nch; | |||
2200 | } | |||
2201 | } | |||
2202 | } | |||
2203 | ||||
2204 | /* | |||
2205 | * For some arguments of some macros, | |||
2206 | * convert all breakable hyphens into ASCII_HYPH. | |||
2207 | */ | |||
2208 | static void | |||
2209 | post_hyph(POST_ARGSstruct roff_man *mdoc) | |||
2210 | { | |||
2211 | struct roff_node *n, *nch; | |||
2212 | char *cp; | |||
2213 | ||||
2214 | n = mdoc->last; | |||
2215 | for (nch = n->child; nch != NULL((void *)0); nch = nch->next) { | |||
2216 | if (nch->type != ROFFT_TEXT) | |||
2217 | continue; | |||
2218 | cp = nch->string; | |||
2219 | if (*cp == '\0') | |||
2220 | continue; | |||
2221 | while (*(++cp) != '\0') | |||
2222 | if (*cp == '-' && | |||
2223 | isalpha((unsigned char)cp[-1]) && | |||
2224 | isalpha((unsigned char)cp[1])) { | |||
2225 | if (n->tag == NULL((void *)0) && n->flags & NODE_ID(1 << 11)) | |||
2226 | n->tag = mandoc_strdup(nch->string); | |||
2227 | *cp = ASCII_HYPH28; | |||
2228 | } | |||
2229 | } | |||
2230 | } | |||
2231 | ||||
2232 | static void | |||
2233 | post_ns(POST_ARGSstruct roff_man *mdoc) | |||
2234 | { | |||
2235 | struct roff_node *n; | |||
2236 | ||||
2237 | n = mdoc->last; | |||
2238 | if (n->flags & NODE_LINE(1 << 3) || | |||
2239 | (n->next != NULL((void *)0) && n->next->flags & NODE_DELIMC(1 << 5))) | |||
2240 | mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL((void *)0)); | |||
2241 | } | |||
2242 | ||||
2243 | static void | |||
2244 | post_sx(POST_ARGSstruct roff_man *mdoc) | |||
2245 | { | |||
2246 | post_delim(mdoc); | |||
2247 | post_hyph(mdoc); | |||
2248 | } | |||
2249 | ||||
2250 | static void | |||
2251 | post_sh(POST_ARGSstruct roff_man *mdoc) | |||
2252 | { | |||
2253 | post_section(mdoc); | |||
2254 | ||||
2255 | switch (mdoc->last->type) { | |||
2256 | case ROFFT_HEAD: | |||
2257 | post_sh_head(mdoc); | |||
2258 | break; | |||
2259 | case ROFFT_BODY: | |||
2260 | switch (mdoc->lastsec) { | |||
2261 | case SEC_NAME: | |||
2262 | post_sh_name(mdoc); | |||
2263 | break; | |||
2264 | case SEC_SEE_ALSO: | |||
2265 | post_sh_see_also(mdoc); | |||
2266 | break; | |||
2267 | case SEC_AUTHORS: | |||
2268 | post_sh_authors(mdoc); | |||
2269 | break; | |||
2270 | default: | |||
2271 | break; | |||
2272 | } | |||
2273 | break; | |||
2274 | default: | |||
2275 | break; | |||
2276 | } | |||
2277 | } | |||
2278 | ||||
2279 | static void | |||
2280 | post_sh_name(POST_ARGSstruct roff_man *mdoc) | |||
2281 | { | |||
2282 | struct roff_node *n; | |||
2283 | int hasnm, hasnd; | |||
2284 | ||||
2285 | hasnm = hasnd = 0; | |||
2286 | ||||
2287 | for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) { | |||
2288 | switch (n->tok) { | |||
2289 | case MDOC_Nm: | |||
2290 | if (hasnm && n->child != NULL((void *)0)) | |||
2291 | mandoc_msg(MANDOCERR_NAMESEC_PUNCT, | |||
2292 | n->line, n->pos, | |||
2293 | "Nm %s", n->child->string); | |||
2294 | hasnm = 1; | |||
2295 | continue; | |||
2296 | case MDOC_Nd: | |||
2297 | hasnd = 1; | |||
2298 | if (n->next != NULL((void *)0)) | |||
2299 | mandoc_msg(MANDOCERR_NAMESEC_ND, | |||
2300 | n->line, n->pos, NULL((void *)0)); | |||
2301 | break; | |||
2302 | case TOKEN_NONE: | |||
2303 | if (n->type == ROFFT_TEXT && | |||
2304 | n->string[0] == ',' && n->string[1] == '\0' && | |||
2305 | n->next != NULL((void *)0) && n->next->tok == MDOC_Nm) { | |||
2306 | n = n->next; | |||
2307 | continue; | |||
2308 | } | |||
2309 | /* FALLTHROUGH */ | |||
2310 | default: | |||
2311 | mandoc_msg(MANDOCERR_NAMESEC_BAD, | |||
2312 | n->line, n->pos, "%s", roff_name[n->tok]); | |||
2313 | continue; | |||
2314 | } | |||
2315 | break; | |||
2316 | } | |||
2317 | ||||
2318 | if ( ! hasnm) | |||
2319 | mandoc_msg(MANDOCERR_NAMESEC_NONM, | |||
2320 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
2321 | if ( ! hasnd) | |||
2322 | mandoc_msg(MANDOCERR_NAMESEC_NOND, | |||
2323 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
2324 | } | |||
2325 | ||||
2326 | static void | |||
2327 | post_sh_see_also(POST_ARGSstruct roff_man *mdoc) | |||
2328 | { | |||
2329 | const struct roff_node *n; | |||
2330 | const char *name, *sec; | |||
2331 | const char *lastname, *lastsec, *lastpunct; | |||
2332 | int cmp; | |||
2333 | ||||
2334 | n = mdoc->last->child; | |||
2335 | lastname = lastsec = lastpunct = NULL((void *)0); | |||
2336 | while (n != NULL((void *)0)) { | |||
2337 | if (n->tok != MDOC_Xr || | |||
2338 | n->child == NULL((void *)0) || | |||
2339 | n->child->next == NULL((void *)0)) | |||
2340 | break; | |||
2341 | ||||
2342 | /* Process one .Xr node. */ | |||
2343 | ||||
2344 | name = n->child->string; | |||
2345 | sec = n->child->next->string; | |||
2346 | if (lastsec != NULL((void *)0)) { | |||
2347 | if (lastpunct[0] != ',' || lastpunct[1] != '\0') | |||
2348 | mandoc_msg(MANDOCERR_XR_PUNCT, n->line, | |||
2349 | n->pos, "%s before %s(%s)", | |||
2350 | lastpunct, name, sec); | |||
2351 | cmp = strcmp(lastsec, sec); | |||
2352 | if (cmp > 0) | |||
2353 | mandoc_msg(MANDOCERR_XR_ORDER, n->line, | |||
2354 | n->pos, "%s(%s) after %s(%s)", | |||
2355 | name, sec, lastname, lastsec); | |||
2356 | else if (cmp == 0 && | |||
2357 | strcasecmp(lastname, name) > 0) | |||
2358 | mandoc_msg(MANDOCERR_XR_ORDER, n->line, | |||
2359 | n->pos, "%s after %s", name, lastname); | |||
2360 | } | |||
2361 | lastname = name; | |||
2362 | lastsec = sec; | |||
2363 | ||||
2364 | /* Process the following node. */ | |||
2365 | ||||
2366 | n = n->next; | |||
2367 | if (n == NULL((void *)0)) | |||
2368 | break; | |||
2369 | if (n->tok == MDOC_Xr) { | |||
2370 | lastpunct = "none"; | |||
2371 | continue; | |||
2372 | } | |||
2373 | if (n->type != ROFFT_TEXT) | |||
2374 | break; | |||
2375 | for (name = n->string; *name != '\0'; name++) | |||
2376 | if (isalpha((const unsigned char)*name)) | |||
2377 | return; | |||
2378 | lastpunct = n->string; | |||
2379 | if (n->next == NULL((void *)0) || n->next->tok == MDOC_Rs) | |||
2380 | mandoc_msg(MANDOCERR_XR_PUNCT, n->line, | |||
2381 | n->pos, "%s after %s(%s)", | |||
2382 | lastpunct, lastname, lastsec); | |||
2383 | n = n->next; | |||
2384 | } | |||
2385 | } | |||
2386 | ||||
2387 | static int | |||
2388 | child_an(const struct roff_node *n) | |||
2389 | { | |||
2390 | ||||
2391 | for (n = n->child; n != NULL((void *)0); n = n->next) | |||
2392 | if ((n->tok == MDOC_An && n->child != NULL((void *)0)) || child_an(n)) | |||
2393 | return 1; | |||
2394 | return 0; | |||
2395 | } | |||
2396 | ||||
2397 | static void | |||
2398 | post_sh_authors(POST_ARGSstruct roff_man *mdoc) | |||
2399 | { | |||
2400 | ||||
2401 | if ( ! child_an(mdoc->last)) | |||
2402 | mandoc_msg(MANDOCERR_AN_MISSING, | |||
2403 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
2404 | } | |||
2405 | ||||
2406 | /* | |||
2407 | * Return an upper bound for the string distance (allowing | |||
2408 | * transpositions). Not a full Levenshtein implementation | |||
2409 | * because Levenshtein is quadratic in the string length | |||
2410 | * and this function is called for every standard name, | |||
2411 | * so the check for each custom name would be cubic. | |||
2412 | * The following crude heuristics is linear, resulting | |||
2413 | * in quadratic behaviour for checking one custom name, | |||
2414 | * which does not cause measurable slowdown. | |||
2415 | */ | |||
2416 | static int | |||
2417 | similar(const char *s1, const char *s2) | |||
2418 | { | |||
2419 | const int maxdist = 3; | |||
2420 | int dist = 0; | |||
2421 | ||||
2422 | while (s1[0] != '\0' && s2[0] != '\0') { | |||
2423 | if (s1[0] == s2[0]) { | |||
2424 | s1++; | |||
2425 | s2++; | |||
2426 | continue; | |||
2427 | } | |||
2428 | if (++dist > maxdist) | |||
2429 | return INT_MAX0x7fffffff; | |||
2430 | if (s1[1] == s2[1]) { /* replacement */ | |||
2431 | s1++; | |||
2432 | s2++; | |||
2433 | } else if (s1[0] == s2[1] && s1[1] == s2[0]) { | |||
2434 | s1 += 2; /* transposition */ | |||
2435 | s2 += 2; | |||
2436 | } else if (s1[0] == s2[1]) /* insertion */ | |||
2437 | s2++; | |||
2438 | else if (s1[1] == s2[0]) /* deletion */ | |||
2439 | s1++; | |||
2440 | else | |||
2441 | return INT_MAX0x7fffffff; | |||
2442 | } | |||
2443 | dist += strlen(s1) + strlen(s2); | |||
2444 | return dist > maxdist ? INT_MAX0x7fffffff : dist; | |||
2445 | } | |||
2446 | ||||
2447 | static void | |||
2448 | post_sh_head(POST_ARGSstruct roff_man *mdoc) | |||
2449 | { | |||
2450 | struct roff_node *nch; | |||
2451 | const char *goodsec; | |||
2452 | const char *const *testsec; | |||
2453 | int dist, mindist; | |||
2454 | enum roff_sec sec; | |||
2455 | ||||
2456 | /* | |||
2457 | * Process a new section. Sections are either "named" or | |||
2458 | * "custom". Custom sections are user-defined, while named ones | |||
2459 | * follow a conventional order and may only appear in certain | |||
2460 | * manual sections. | |||
2461 | */ | |||
2462 | ||||
2463 | sec = mdoc->last->sec; | |||
2464 | ||||
2465 | /* The NAME should be first. */ | |||
2466 | ||||
2467 | if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) | |||
2468 | mandoc_msg(MANDOCERR_NAMESEC_FIRST, | |||
2469 | mdoc->last->line, mdoc->last->pos, "Sh %s", | |||
2470 | sec != SEC_CUSTOM ? secnames[sec] : | |||
2471 | (nch = mdoc->last->child) == NULL((void *)0) ? "" : | |||
2472 | nch->type == ROFFT_TEXT ? nch->string : | |||
2473 | roff_name[nch->tok]); | |||
2474 | ||||
2475 | /* The SYNOPSIS gets special attention in other areas. */ | |||
2476 | ||||
2477 | if (sec == SEC_SYNOPSIS) { | |||
2478 | roff_setreg(mdoc->roff, "nS", 1, '='); | |||
2479 | mdoc->flags |= MDOC_SYNOPSIS(1 << 7); | |||
2480 | } else { | |||
2481 | roff_setreg(mdoc->roff, "nS", 0, '='); | |||
2482 | mdoc->flags &= ~MDOC_SYNOPSIS(1 << 7); | |||
2483 | } | |||
2484 | if (sec == SEC_DESCRIPTION) | |||
2485 | fn_prio = TAG_STRONG2; | |||
2486 | ||||
2487 | /* Mark our last section. */ | |||
2488 | ||||
2489 | mdoc->lastsec = sec; | |||
2490 | ||||
2491 | /* We don't care about custom sections after this. */ | |||
2492 | ||||
2493 | if (sec == SEC_CUSTOM) { | |||
2494 | if ((nch = mdoc->last->child) == NULL((void *)0) || | |||
2495 | nch->type != ROFFT_TEXT || nch->next != NULL((void *)0)) | |||
2496 | return; | |||
2497 | goodsec = NULL((void *)0); | |||
2498 | mindist = INT_MAX0x7fffffff; | |||
2499 | for (testsec = secnames + 1; *testsec != NULL((void *)0); testsec++) { | |||
2500 | dist = similar(nch->string, *testsec); | |||
2501 | if (dist < mindist) { | |||
2502 | goodsec = *testsec; | |||
2503 | mindist = dist; | |||
2504 | } | |||
2505 | } | |||
2506 | if (goodsec != NULL((void *)0)) | |||
2507 | mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, | |||
2508 | "Sh %s instead of %s", nch->string, goodsec); | |||
2509 | return; | |||
2510 | } | |||
2511 | ||||
2512 | /* | |||
2513 | * Check whether our non-custom section is being repeated or is | |||
2514 | * out of order. | |||
2515 | */ | |||
2516 | ||||
2517 | if (sec == mdoc->lastnamed) | |||
2518 | mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, | |||
2519 | mdoc->last->pos, "Sh %s", secnames[sec]); | |||
2520 | ||||
2521 | if (sec < mdoc->lastnamed) | |||
2522 | mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, | |||
2523 | mdoc->last->pos, "Sh %s", secnames[sec]); | |||
2524 | ||||
2525 | /* Mark the last named section. */ | |||
2526 | ||||
2527 | mdoc->lastnamed = sec; | |||
2528 | ||||
2529 | /* Check particular section/manual conventions. */ | |||
2530 | ||||
2531 | if (mdoc->meta.msec == NULL((void *)0)) | |||
2532 | return; | |||
2533 | ||||
2534 | goodsec = NULL((void *)0); | |||
2535 | switch (sec) { | |||
2536 | case SEC_ERRORS: | |||
2537 | if (*mdoc->meta.msec == '4') | |||
2538 | break; | |||
2539 | goodsec = "2, 3, 4, 9"; | |||
2540 | /* FALLTHROUGH */ | |||
2541 | case SEC_RETURN_VALUES: | |||
2542 | case SEC_LIBRARY: | |||
2543 | if (*mdoc->meta.msec == '2') | |||
2544 | break; | |||
2545 | if (*mdoc->meta.msec == '3') | |||
2546 | break; | |||
2547 | if (NULL((void *)0) == goodsec) | |||
2548 | goodsec = "2, 3, 9"; | |||
2549 | /* FALLTHROUGH */ | |||
2550 | case SEC_CONTEXT: | |||
2551 | if (*mdoc->meta.msec == '9') | |||
2552 | break; | |||
2553 | if (NULL((void *)0) == goodsec) | |||
2554 | goodsec = "9"; | |||
2555 | mandoc_msg(MANDOCERR_SEC_MSEC, | |||
2556 | mdoc->last->line, mdoc->last->pos, | |||
2557 | "Sh %s for %s only", secnames[sec], goodsec); | |||
2558 | break; | |||
2559 | default: | |||
2560 | break; | |||
2561 | } | |||
2562 | } | |||
2563 | ||||
2564 | static void | |||
2565 | post_xr(POST_ARGSstruct roff_man *mdoc) | |||
2566 | { | |||
2567 | struct roff_node *n, *nch; | |||
2568 | ||||
2569 | n = mdoc->last; | |||
2570 | nch = n->child; | |||
2571 | if (nch->next == NULL((void *)0)) { | |||
2572 | mandoc_msg(MANDOCERR_XR_NOSEC, | |||
2573 | n->line, n->pos, "Xr %s", nch->string); | |||
2574 | } else { | |||
2575 | assert(nch->next == n->last)((nch->next == n->last) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 2575, __func__, "nch->next == n->last")); | |||
2576 | if(mandoc_xr_add(nch->next->string, nch->string, | |||
2577 | nch->line, nch->pos)) | |||
2578 | mandoc_msg(MANDOCERR_XR_SELF, | |||
2579 | nch->line, nch->pos, "Xr %s %s", | |||
2580 | nch->string, nch->next->string); | |||
2581 | } | |||
2582 | post_delim_nb(mdoc); | |||
2583 | } | |||
2584 | ||||
2585 | static void | |||
2586 | post_section(POST_ARGSstruct roff_man *mdoc) | |||
2587 | { | |||
2588 | struct roff_node *n, *nch; | |||
2589 | char *cp, *tag; | |||
2590 | ||||
2591 | n = mdoc->last; | |||
2592 | switch (n->type) { | |||
2593 | case ROFFT_BLOCK: | |||
2594 | post_prevpar(mdoc); | |||
2595 | return; | |||
2596 | case ROFFT_HEAD: | |||
2597 | tag = NULL((void *)0); | |||
2598 | deroff(&tag, n); | |||
2599 | if (tag != NULL((void *)0)) { | |||
2600 | for (cp = tag; *cp != '\0'; cp++) | |||
2601 | if (*cp == ' ') | |||
2602 | *cp = '_'; | |||
2603 | if ((nch = n->child) != NULL((void *)0) && | |||
2604 | nch->type == ROFFT_TEXT && | |||
2605 | strcmp(nch->string, tag) == 0) | |||
2606 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
2607 | else | |||
2608 | tag_put(tag, TAG_FALLBACK(0x7fffffff - 1), n); | |||
2609 | free(tag); | |||
2610 | } | |||
2611 | post_delim(mdoc); | |||
2612 | post_hyph(mdoc); | |||
2613 | return; | |||
2614 | case ROFFT_BODY: | |||
2615 | break; | |||
2616 | default: | |||
2617 | return; | |||
2618 | } | |||
2619 | if ((nch = n->child) != NULL((void *)0) && | |||
2620 | (nch->tok == MDOC_Pp || nch->tok == ROFF_br || | |||
2621 | nch->tok == ROFF_sp)) { | |||
2622 | mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, | |||
2623 | "%s after %s", roff_name[nch->tok], | |||
2624 | roff_name[n->tok]); | |||
2625 | roff_node_delete(mdoc, nch); | |||
2626 | } | |||
2627 | if ((nch = n->last) != NULL((void *)0) && | |||
2628 | (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { | |||
2629 | mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, | |||
2630 | "%s at the end of %s", roff_name[nch->tok], | |||
2631 | roff_name[n->tok]); | |||
2632 | roff_node_delete(mdoc, nch); | |||
2633 | } | |||
2634 | } | |||
2635 | ||||
2636 | static void | |||
2637 | post_prevpar(POST_ARGSstruct roff_man *mdoc) | |||
2638 | { | |||
2639 | struct roff_node *n, *np; | |||
2640 | ||||
2641 | n = mdoc->last; | |||
2642 | if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) | |||
2643 | return; | |||
2644 | if ((np = roff_node_prev(n)) == NULL((void *)0)) | |||
2645 | return; | |||
2646 | ||||
2647 | /* | |||
2648 | * Don't allow `Pp' prior to a paragraph-type | |||
2649 | * block: `Pp' or non-compact `Bd' or `Bl'. | |||
2650 | */ | |||
2651 | ||||
2652 | if (np->tok != MDOC_Pp && np->tok != ROFF_br) | |||
2653 | return; | |||
2654 | if (n->tok == MDOC_Bl && n->norm->Bl.comp) | |||
2655 | return; | |||
2656 | if (n->tok == MDOC_Bd && n->norm->Bd.comp) | |||
2657 | return; | |||
2658 | if (n->tok == MDOC_It && n->parent->norm->Bl.comp) | |||
2659 | return; | |||
2660 | ||||
2661 | mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, | |||
2662 | "%s before %s", roff_name[np->tok], roff_name[n->tok]); | |||
2663 | roff_node_delete(mdoc, np); | |||
2664 | } | |||
2665 | ||||
2666 | static void | |||
2667 | post_par(POST_ARGSstruct roff_man *mdoc) | |||
2668 | { | |||
2669 | struct roff_node *np; | |||
2670 | ||||
2671 | fn_prio = TAG_STRONG2; | |||
2672 | post_prevpar(mdoc); | |||
2673 | ||||
2674 | np = mdoc->last; | |||
2675 | if (np->child != NULL((void *)0)) | |||
2676 | mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, | |||
2677 | "%s %s", roff_name[np->tok], np->child->string); | |||
2678 | } | |||
2679 | ||||
2680 | static void | |||
2681 | post_dd(POST_ARGSstruct roff_man *mdoc) | |||
2682 | { | |||
2683 | struct roff_node *n; | |||
2684 | ||||
2685 | n = mdoc->last; | |||
2686 | n->flags |= NODE_NOPRT(1 << 10); | |||
2687 | ||||
2688 | if (mdoc->meta.date != NULL((void *)0)) { | |||
2689 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); | |||
2690 | free(mdoc->meta.date); | |||
2691 | } else if (mdoc->flags & MDOC_PBODY(1 << 2)) | |||
2692 | mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); | |||
2693 | else if (mdoc->meta.title != NULL((void *)0)) | |||
2694 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
2695 | n->line, n->pos, "Dd after Dt"); | |||
2696 | else if (mdoc->meta.os != NULL((void *)0)) | |||
2697 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
2698 | n->line, n->pos, "Dd after Os"); | |||
2699 | ||||
2700 | if (mdoc->quick && n != NULL((void *)0)) | |||
2701 | mdoc->meta.date = mandoc_strdup(""); | |||
2702 | else | |||
2703 | mdoc->meta.date = mandoc_normdate(n->child, n); | |||
2704 | } | |||
2705 | ||||
2706 | static void | |||
2707 | post_dt(POST_ARGSstruct roff_man *mdoc) | |||
2708 | { | |||
2709 | struct roff_node *nn, *n; | |||
2710 | const char *cp; | |||
2711 | char *p; | |||
2712 | ||||
2713 | n = mdoc->last; | |||
2714 | n->flags |= NODE_NOPRT(1 << 10); | |||
2715 | ||||
2716 | if (mdoc->flags & MDOC_PBODY(1 << 2)) { | |||
2717 | mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); | |||
2718 | return; | |||
2719 | } | |||
2720 | ||||
2721 | if (mdoc->meta.title != NULL((void *)0)) | |||
2722 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); | |||
2723 | else if (mdoc->meta.os != NULL((void *)0)) | |||
2724 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
2725 | n->line, n->pos, "Dt after Os"); | |||
2726 | ||||
2727 | free(mdoc->meta.title); | |||
2728 | free(mdoc->meta.msec); | |||
2729 | free(mdoc->meta.vol); | |||
2730 | free(mdoc->meta.arch); | |||
2731 | ||||
2732 | mdoc->meta.title = NULL((void *)0); | |||
2733 | mdoc->meta.msec = NULL((void *)0); | |||
2734 | mdoc->meta.vol = NULL((void *)0); | |||
2735 | mdoc->meta.arch = NULL((void *)0); | |||
2736 | ||||
2737 | /* Mandatory first argument: title. */ | |||
2738 | ||||
2739 | nn = n->child; | |||
2740 | if (nn == NULL((void *)0) || *nn->string == '\0') { | |||
2741 | mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); | |||
2742 | mdoc->meta.title = mandoc_strdup("UNTITLED"); | |||
2743 | } else { | |||
2744 | mdoc->meta.title = mandoc_strdup(nn->string); | |||
2745 | ||||
2746 | /* Check that all characters are uppercase. */ | |||
2747 | ||||
2748 | for (p = nn->string; *p != '\0'; p++) | |||
2749 | if (islower((unsigned char)*p)) { | |||
2750 | mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, | |||
2751 | nn->pos + (int)(p - nn->string), | |||
2752 | "Dt %s", nn->string); | |||
2753 | break; | |||
2754 | } | |||
2755 | } | |||
2756 | ||||
2757 | /* Mandatory second argument: section. */ | |||
2758 | ||||
2759 | if (nn != NULL((void *)0)) | |||
2760 | nn = nn->next; | |||
2761 | ||||
2762 | if (nn == NULL((void *)0)) { | |||
2763 | mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, | |||
2764 | "Dt %s", mdoc->meta.title); | |||
2765 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |||
2766 | return; /* msec and arch remain NULL. */ | |||
2767 | } | |||
2768 | ||||
2769 | mdoc->meta.msec = mandoc_strdup(nn->string); | |||
2770 | ||||
2771 | /* Infer volume title from section number. */ | |||
2772 | ||||
2773 | cp = mandoc_a2msec(nn->string); | |||
2774 | if (cp == NULL((void *)0)) { | |||
2775 | mandoc_msg(MANDOCERR_MSEC_BAD, | |||
2776 | nn->line, nn->pos, "Dt ... %s", nn->string); | |||
2777 | mdoc->meta.vol = mandoc_strdup(nn->string); | |||
2778 | } else { | |||
2779 | mdoc->meta.vol = mandoc_strdup(cp); | |||
2780 | if (mdoc->filesec != '\0' && | |||
2781 | mdoc->filesec != *nn->string && | |||
2782 | *nn->string >= '1' && *nn->string <= '9') | |||
2783 | mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, | |||
2784 | "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); | |||
2785 | } | |||
2786 | ||||
2787 | /* Optional third argument: architecture. */ | |||
2788 | ||||
2789 | if ((nn = nn->next) == NULL((void *)0)) | |||
2790 | return; | |||
2791 | ||||
2792 | for (p = nn->string; *p != '\0'; p++) | |||
2793 | *p = tolower((unsigned char)*p); | |||
2794 | mdoc->meta.arch = mandoc_strdup(nn->string); | |||
2795 | ||||
2796 | /* Ignore fourth and later arguments. */ | |||
2797 | ||||
2798 | if ((nn = nn->next) != NULL((void *)0)) | |||
2799 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
2800 | nn->line, nn->pos, "Dt ... %s", nn->string); | |||
2801 | } | |||
2802 | ||||
2803 | static void | |||
2804 | post_bx(POST_ARGSstruct roff_man *mdoc) | |||
2805 | { | |||
2806 | struct roff_node *n, *nch; | |||
2807 | const char *macro; | |||
2808 | ||||
2809 | post_delim_nb(mdoc); | |||
2810 | ||||
2811 | n = mdoc->last; | |||
2812 | nch = n->child; | |||
2813 | ||||
2814 | if (nch != NULL((void *)0)) { | |||
2815 | macro = !strcmp(nch->string, "Open") ? "Ox" : | |||
2816 | !strcmp(nch->string, "Net") ? "Nx" : | |||
2817 | !strcmp(nch->string, "Free") ? "Fx" : | |||
2818 | !strcmp(nch->string, "DragonFly") ? "Dx" : NULL((void *)0); | |||
2819 | if (macro != NULL((void *)0)) | |||
2820 | mandoc_msg(MANDOCERR_BX, | |||
2821 | n->line, n->pos, "%s", macro); | |||
2822 | mdoc->last = nch; | |||
2823 | nch = nch->next; | |||
2824 | mdoc->next = ROFF_NEXT_SIBLING; | |||
2825 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
2826 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
2827 | mdoc->next = ROFF_NEXT_SIBLING; | |||
2828 | } else | |||
2829 | mdoc->next = ROFF_NEXT_CHILD; | |||
2830 | roff_word_alloc(mdoc, n->line, n->pos, "BSD"); | |||
2831 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
2832 | ||||
2833 | if (nch == NULL((void *)0)) { | |||
2834 | mdoc->last = n; | |||
2835 | return; | |||
2836 | } | |||
2837 | ||||
2838 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
2839 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
2840 | mdoc->next = ROFF_NEXT_SIBLING; | |||
2841 | roff_word_alloc(mdoc, n->line, n->pos, "-"); | |||
2842 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
2843 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
2844 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
2845 | mdoc->last = n; | |||
2846 | ||||
2847 | /* | |||
2848 | * Make `Bx's second argument always start with an uppercase | |||
2849 | * letter. Groff checks if it's an "accepted" term, but we just | |||
2850 | * uppercase blindly. | |||
2851 | */ | |||
2852 | ||||
2853 | *nch->string = (char)toupper((unsigned char)*nch->string); | |||
2854 | } | |||
2855 | ||||
2856 | static void | |||
2857 | post_os(POST_ARGSstruct roff_man *mdoc) | |||
2858 | { | |||
2859 | #ifndef OSNAME | |||
2860 | struct utsname utsname; | |||
2861 | #endif | |||
2862 | struct roff_node *n; | |||
2863 | ||||
2864 | n = mdoc->last; | |||
2865 | n->flags |= NODE_NOPRT(1 << 10); | |||
2866 | ||||
2867 | if (mdoc->meta.os != NULL((void *)0)) | |||
2868 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); | |||
2869 | else if (mdoc->flags & MDOC_PBODY(1 << 2)) | |||
2870 | mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); | |||
2871 | ||||
2872 | post_delim(mdoc); | |||
2873 | ||||
2874 | /* | |||
2875 | * Set the operating system by way of the `Os' macro. | |||
2876 | * The order of precedence is: | |||
2877 | * 1. the argument of the `Os' macro, unless empty | |||
2878 | * 2. the -Ios=foo command line argument, if provided | |||
2879 | * 3. -DOSNAME="\"foo\"", if provided during compilation | |||
2880 | * 4. "sysname release" from uname(3) | |||
2881 | */ | |||
2882 | ||||
2883 | free(mdoc->meta.os); | |||
2884 | mdoc->meta.os = NULL((void *)0); | |||
2885 | deroff(&mdoc->meta.os, n); | |||
2886 | if (mdoc->meta.os) | |||
2887 | goto out; | |||
2888 | ||||
2889 | if (mdoc->os_s != NULL((void *)0)) { | |||
2890 | mdoc->meta.os = mandoc_strdup(mdoc->os_s); | |||
2891 | goto out; | |||
2892 | } | |||
2893 | ||||
2894 | #ifdef OSNAME | |||
2895 | mdoc->meta.os = mandoc_strdup(OSNAME); | |||
2896 | #else /*!OSNAME */ | |||
2897 | if (mdoc->os_r == NULL((void *)0)) { | |||
2898 | if (uname(&utsname) == -1) { | |||
2899 | mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); | |||
2900 | mdoc->os_r = mandoc_strdup("UNKNOWN"); | |||
2901 | } else | |||
2902 | mandoc_asprintf(&mdoc->os_r, "%s %s", | |||
2903 | utsname.sysname, utsname.release); | |||
2904 | } | |||
2905 | mdoc->meta.os = mandoc_strdup(mdoc->os_r); | |||
2906 | #endif /*!OSNAME*/ | |||
2907 | ||||
2908 | out: | |||
2909 | if (mdoc->meta.os_e == MANDOC_OS_OTHER) { | |||
2910 | if (strstr(mdoc->meta.os, "OpenBSD") != NULL((void *)0)) | |||
2911 | mdoc->meta.os_e = MANDOC_OS_OPENBSD; | |||
2912 | else if (strstr(mdoc->meta.os, "NetBSD") != NULL((void *)0)) | |||
2913 | mdoc->meta.os_e = MANDOC_OS_NETBSD; | |||
2914 | } | |||
2915 | ||||
2916 | /* | |||
2917 | * This is the earliest point where we can check | |||
2918 | * Mdocdate conventions because we don't know | |||
2919 | * the operating system earlier. | |||
2920 | */ | |||
2921 | ||||
2922 | if (n->child != NULL((void *)0)) | |||
2923 | mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, | |||
2924 | "Os %s (%s)", n->child->string, | |||
2925 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
2926 | "OpenBSD" : "NetBSD"); | |||
2927 | ||||
2928 | while (n->tok != MDOC_Dd) | |||
2929 | if ((n = n->prev) == NULL((void *)0)) | |||
2930 | return; | |||
2931 | if ((n = n->child) == NULL((void *)0)) | |||
2932 | return; | |||
2933 | if (strncmp(n->string, "$" "Mdocdate", 9)) { | |||
2934 | if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) | |||
2935 | mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, | |||
2936 | n->pos, "Dd %s (OpenBSD)", n->string); | |||
2937 | } else { | |||
2938 | if (mdoc->meta.os_e == MANDOC_OS_NETBSD) | |||
2939 | mandoc_msg(MANDOCERR_MDOCDATE, n->line, | |||
2940 | n->pos, "Dd %s (NetBSD)", n->string); | |||
2941 | } | |||
2942 | } | |||
2943 | ||||
2944 | enum roff_sec | |||
2945 | mdoc_a2sec(const char *p) | |||
2946 | { | |||
2947 | int i; | |||
2948 | ||||
2949 | for (i = 0; i < (int)SEC__MAX; i++) | |||
2950 | if (secnames[i] && 0 == strcmp(p, secnames[i])) | |||
2951 | return (enum roff_sec)i; | |||
2952 | ||||
2953 | return SEC_CUSTOM; | |||
2954 | } | |||
2955 | ||||
2956 | static size_t | |||
2957 | macro2len(enum roff_tok macro) | |||
2958 | { | |||
2959 | ||||
2960 | switch (macro) { | |||
2961 | case MDOC_Ad: | |||
2962 | return 12; | |||
2963 | case MDOC_Ao: | |||
2964 | return 12; | |||
2965 | case MDOC_An: | |||
2966 | return 12; | |||
2967 | case MDOC_Aq: | |||
2968 | return 12; | |||
2969 | case MDOC_Ar: | |||
2970 | return 12; | |||
2971 | case MDOC_Bo: | |||
2972 | return 12; | |||
2973 | case MDOC_Bq: | |||
2974 | return 12; | |||
2975 | case MDOC_Cd: | |||
2976 | return 12; | |||
2977 | case MDOC_Cm: | |||
2978 | return 10; | |||
2979 | case MDOC_Do: | |||
2980 | return 10; | |||
2981 | case MDOC_Dq: | |||
2982 | return 12; | |||
2983 | case MDOC_Dv: | |||
2984 | return 12; | |||
2985 | case MDOC_Eo: | |||
2986 | return 12; | |||
2987 | case MDOC_Em: | |||
2988 | return 10; | |||
2989 | case MDOC_Er: | |||
2990 | return 17; | |||
2991 | case MDOC_Ev: | |||
2992 | return 15; | |||
2993 | case MDOC_Fa: | |||
2994 | return 12; | |||
2995 | case MDOC_Fl: | |||
2996 | return 10; | |||
2997 | case MDOC_Fo: | |||
2998 | return 16; | |||
2999 | case MDOC_Fn: | |||
3000 | return 16; | |||
3001 | case MDOC_Ic: | |||
3002 | return 10; | |||
3003 | case MDOC_Li: | |||
3004 | return 16; | |||
3005 | case MDOC_Ms: | |||
3006 | return 6; | |||
3007 | case MDOC_Nm: | |||
3008 | return 10; | |||
3009 | case MDOC_No: | |||
3010 | return 12; | |||
3011 | case MDOC_Oo: | |||
3012 | return 10; | |||
3013 | case MDOC_Op: | |||
3014 | return 14; | |||
3015 | case MDOC_Pa: | |||
3016 | return 32; | |||
3017 | case MDOC_Pf: | |||
3018 | return 12; | |||
3019 | case MDOC_Po: | |||
3020 | return 12; | |||
3021 | case MDOC_Pq: | |||
3022 | return 12; | |||
3023 | case MDOC_Ql: | |||
3024 | return 16; | |||
3025 | case MDOC_Qo: | |||
3026 | return 12; | |||
3027 | case MDOC_So: | |||
3028 | return 12; | |||
3029 | case MDOC_Sq: | |||
3030 | return 12; | |||
3031 | case MDOC_Sy: | |||
3032 | return 6; | |||
3033 | case MDOC_Sx: | |||
3034 | return 16; | |||
3035 | case MDOC_Tn: | |||
3036 | return 10; | |||
3037 | case MDOC_Va: | |||
3038 | return 12; | |||
3039 | case MDOC_Vt: | |||
3040 | return 12; | |||
3041 | case MDOC_Xr: | |||
3042 | return 10; | |||
3043 | default: | |||
3044 | break; | |||
3045 | }; | |||
3046 | return 0; | |||
3047 | } |