Print this page
9718 update mandoc to 1.14.4
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/mandoc/man_validate.c
+++ new/usr/src/cmd/mandoc/man_validate.c
1 1 /* $OpenBSD$ */
2 2 /*
3 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 - * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
4 + * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org>
5 5 *
6 6 * Permission to use, copy, modify, and distribute this software for any
7 7 * purpose with or without fee is hereby granted, provided that the above
8 8 * copyright notice and this permission notice appear in all copies.
9 9 *
10 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 17 */
18 18 #include "config.h"
19 19
20 20 #include <sys/types.h>
21 21
22 22 #include <assert.h>
23 23 #include <ctype.h>
24 24 #include <errno.h>
25 25 #include <limits.h>
26 26 #include <stdarg.h>
27 27 #include <stdlib.h>
28 28 #include <string.h>
29 29 #include <time.h>
30 30
31 31 #include "mandoc_aux.h"
32 32 #include "mandoc.h"
33 33 #include "roff.h"
34 34 #include "man.h"
35 35 #include "libmandoc.h"
36 36 #include "roff_int.h"
37 37 #include "libman.h"
38 38
39 39 #define CHKARGS struct roff_man *man, struct roff_node *n
40 40
41 41 typedef void (*v_check)(CHKARGS);
42 42
43 43 static void check_par(CHKARGS);
44 44 static void check_part(CHKARGS);
45 45 static void check_root(CHKARGS);
46 46 static void check_text(CHKARGS);
47 47
48 48 static void post_AT(CHKARGS);
49 49 static void post_IP(CHKARGS);
50 50 static void post_OP(CHKARGS);
51 51 static void post_TH(CHKARGS);
52 52 static void post_UC(CHKARGS);
53 53 static void post_UR(CHKARGS);
54 54 static void post_in(CHKARGS);
55 55 static void post_vs(CHKARGS);
56 56
57 57 static const v_check __man_valids[MAN_MAX - MAN_TH] = {
58 58 post_TH, /* TH */
59 59 NULL, /* SH */
60 60 NULL, /* SS */
61 61 NULL, /* TP */
62 62 check_par, /* LP */
63 63 check_par, /* PP */
64 64 check_par, /* P */
65 65 post_IP, /* IP */
66 66 NULL, /* HP */
67 67 NULL, /* SM */
68 68 NULL, /* SB */
69 69 NULL, /* BI */
70 70 NULL, /* IB */
71 71 NULL, /* BR */
72 72 NULL, /* RB */
73 73 NULL, /* R */
74 74 NULL, /* B */
75 75 NULL, /* I */
76 76 NULL, /* IR */
77 77 NULL, /* RI */
78 78 NULL, /* nf */
79 79 NULL, /* fi */
80 80 NULL, /* RE */
81 81 check_part, /* RS */
82 82 NULL, /* DT */
83 83 post_UC, /* UC */
84 84 NULL, /* PD */
85 85 post_AT, /* AT */
86 86 post_in, /* in */
87 87 post_OP, /* OP */
88 88 NULL, /* EX */
89 89 NULL, /* EE */
90 90 post_UR, /* UR */
91 91 NULL, /* UE */
92 92 post_UR, /* MT */
93 93 NULL, /* ME */
94 94 };
95 95 static const v_check *man_valids = __man_valids - MAN_TH;
96 96
97 97
98 98 void
99 99 man_node_validate(struct roff_man *man)
100 100 {
101 101 struct roff_node *n;
102 102 const v_check *cp;
103 103
104 104 n = man->last;
105 105 man->last = man->last->child;
106 106 while (man->last != NULL) {
107 107 man_node_validate(man);
108 108 if (man->last == n)
109 109 man->last = man->last->child;
110 110 else
111 111 man->last = man->last->next;
112 112 }
↓ open down ↓ |
98 lines elided |
↑ open up ↑ |
113 113
114 114 man->last = n;
115 115 man->next = ROFF_NEXT_SIBLING;
116 116 switch (n->type) {
117 117 case ROFFT_TEXT:
118 118 check_text(man, n);
119 119 break;
120 120 case ROFFT_ROOT:
121 121 check_root(man, n);
122 122 break;
123 + case ROFFT_COMMENT:
123 124 case ROFFT_EQN:
124 125 case ROFFT_TBL:
125 126 break;
126 127 default:
127 128 if (n->tok < ROFF_MAX) {
128 129 switch (n->tok) {
129 130 case ROFF_br:
130 131 case ROFF_sp:
131 132 post_vs(man, n);
132 133 break;
133 134 default:
134 135 roff_validate(man);
135 136 break;
136 137 }
137 138 break;
138 139 }
139 140 assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
140 141 cp = man_valids + n->tok;
141 142 if (*cp)
↓ open down ↓ |
9 lines elided |
↑ open up ↑ |
142 143 (*cp)(man, n);
143 144 if (man->last == n)
144 145 man_state(man, n);
145 146 break;
146 147 }
147 148 }
148 149
149 150 static void
150 151 check_root(CHKARGS)
151 152 {
152 -
153 153 assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
154 154
155 - if (NULL == man->first->child)
155 + if (n->last == NULL || n->last->type == ROFFT_COMMENT)
156 156 mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
157 157 n->line, n->pos, NULL);
158 158 else
159 159 man->meta.hasbody = 1;
160 160
161 161 if (NULL == man->meta.title) {
162 162 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
163 163 n->line, n->pos, NULL);
164 164
165 165 /*
166 166 * If a title hasn't been set, do so now (by
167 167 * implication, date and section also aren't set).
168 168 */
169 169
170 170 man->meta.title = mandoc_strdup("");
171 171 man->meta.msec = mandoc_strdup("");
172 172 man->meta.date = man->quick ? mandoc_strdup("") :
173 173 mandoc_normdate(man, NULL, n->line, n->pos);
174 174 }
175 175
176 176 if (man->meta.os_e &&
177 177 (man->meta.rcsids & (1 << man->meta.os_e)) == 0)
178 178 mandoc_msg(MANDOCERR_RCS_MISSING, man->parse, 0, 0,
179 179 man->meta.os_e == MANDOC_OS_OPENBSD ?
180 180 "(OpenBSD)" : "(NetBSD)");
181 181 }
182 182
183 183 static void
184 184 check_text(CHKARGS)
185 185 {
186 186 char *cp, *p;
187 187
188 188 if (MAN_LITERAL & man->flags)
189 189 return;
190 190
191 191 cp = n->string;
192 192 for (p = cp; NULL != (p = strchr(p, '\t')); p++)
193 193 mandoc_msg(MANDOCERR_FI_TAB, man->parse,
194 194 n->line, n->pos + (p - cp), NULL);
195 195 }
196 196
197 197 static void
198 198 post_OP(CHKARGS)
199 199 {
200 200
201 201 if (n->child == NULL)
202 202 mandoc_msg(MANDOCERR_OP_EMPTY, man->parse,
203 203 n->line, n->pos, "OP");
204 204 else if (n->child->next != NULL && n->child->next->next != NULL) {
205 205 n = n->child->next->next;
206 206 mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
207 207 n->line, n->pos, "OP ... %s", n->string);
208 208 }
209 209 }
210 210
211 211 static void
212 212 post_UR(CHKARGS)
213 213 {
214 214 if (n->type == ROFFT_HEAD && n->child == NULL)
215 215 mandoc_msg(MANDOCERR_UR_NOHEAD, man->parse,
216 216 n->line, n->pos, roff_name[n->tok]);
217 217 check_part(man, n);
218 218 }
219 219
220 220 static void
221 221 check_part(CHKARGS)
222 222 {
223 223
224 224 if (n->type == ROFFT_BODY && n->child == NULL)
225 225 mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse,
226 226 n->line, n->pos, roff_name[n->tok]);
227 227 }
228 228
229 229 static void
230 230 check_par(CHKARGS)
231 231 {
232 232
233 233 switch (n->type) {
234 234 case ROFFT_BLOCK:
235 235 if (n->body->child == NULL)
236 236 roff_node_delete(man, n);
237 237 break;
238 238 case ROFFT_BODY:
239 239 if (n->child == NULL)
240 240 mandoc_vmsg(MANDOCERR_PAR_SKIP,
241 241 man->parse, n->line, n->pos,
242 242 "%s empty", roff_name[n->tok]);
243 243 break;
244 244 case ROFFT_HEAD:
245 245 if (n->child != NULL)
246 246 mandoc_vmsg(MANDOCERR_ARG_SKIP,
247 247 man->parse, n->line, n->pos, "%s %s%s",
248 248 roff_name[n->tok], n->child->string,
249 249 n->child->next != NULL ? " ..." : "");
250 250 break;
251 251 default:
252 252 break;
253 253 }
254 254 }
255 255
256 256 static void
257 257 post_IP(CHKARGS)
258 258 {
259 259
260 260 switch (n->type) {
261 261 case ROFFT_BLOCK:
262 262 if (n->head->child == NULL && n->body->child == NULL)
263 263 roff_node_delete(man, n);
264 264 break;
265 265 case ROFFT_BODY:
266 266 if (n->parent->head->child == NULL && n->child == NULL)
267 267 mandoc_vmsg(MANDOCERR_PAR_SKIP,
268 268 man->parse, n->line, n->pos,
269 269 "%s empty", roff_name[n->tok]);
270 270 break;
271 271 default:
272 272 break;
273 273 }
274 274 }
275 275
276 276 static void
277 277 post_TH(CHKARGS)
278 278 {
279 279 struct roff_node *nb;
280 280 const char *p;
281 281
282 282 free(man->meta.title);
283 283 free(man->meta.vol);
284 284 free(man->meta.os);
285 285 free(man->meta.msec);
286 286 free(man->meta.date);
287 287
288 288 man->meta.title = man->meta.vol = man->meta.date =
289 289 man->meta.msec = man->meta.os = NULL;
290 290
291 291 nb = n;
292 292
293 293 /* ->TITLE<- MSEC DATE OS VOL */
294 294
295 295 n = n->child;
296 296 if (n && n->string) {
297 297 for (p = n->string; '\0' != *p; p++) {
298 298 /* Only warn about this once... */
299 299 if (isalpha((unsigned char)*p) &&
300 300 ! isupper((unsigned char)*p)) {
301 301 mandoc_vmsg(MANDOCERR_TITLE_CASE,
302 302 man->parse, n->line,
303 303 n->pos + (p - n->string),
304 304 "TH %s", n->string);
305 305 break;
306 306 }
307 307 }
308 308 man->meta.title = mandoc_strdup(n->string);
309 309 } else {
310 310 man->meta.title = mandoc_strdup("");
311 311 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
312 312 nb->line, nb->pos, "TH");
313 313 }
314 314
315 315 /* TITLE ->MSEC<- DATE OS VOL */
316 316
317 317 if (n)
318 318 n = n->next;
319 319 if (n && n->string)
320 320 man->meta.msec = mandoc_strdup(n->string);
321 321 else {
322 322 man->meta.msec = mandoc_strdup("");
323 323 mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
324 324 nb->line, nb->pos, "TH %s", man->meta.title);
325 325 }
326 326
327 327 /* TITLE MSEC ->DATE<- OS VOL */
328 328
329 329 if (n)
330 330 n = n->next;
331 331 if (n && n->string && '\0' != n->string[0]) {
332 332 man->meta.date = man->quick ?
333 333 mandoc_strdup(n->string) :
334 334 mandoc_normdate(man, n->string, n->line, n->pos);
335 335 } else {
336 336 man->meta.date = mandoc_strdup("");
337 337 mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
338 338 n ? n->line : nb->line,
339 339 n ? n->pos : nb->pos, "TH");
340 340 }
341 341
342 342 /* TITLE MSEC DATE ->OS<- VOL */
343 343
344 344 if (n && (n = n->next))
345 345 man->meta.os = mandoc_strdup(n->string);
346 346 else if (man->os_s != NULL)
347 347 man->meta.os = mandoc_strdup(man->os_s);
348 348 if (man->meta.os_e == MANDOC_OS_OTHER && man->meta.os != NULL) {
349 349 if (strstr(man->meta.os, "OpenBSD") != NULL)
350 350 man->meta.os_e = MANDOC_OS_OPENBSD;
351 351 else if (strstr(man->meta.os, "NetBSD") != NULL)
352 352 man->meta.os_e = MANDOC_OS_NETBSD;
353 353 }
354 354
355 355 /* TITLE MSEC DATE OS ->VOL<- */
356 356 /* If missing, use the default VOL name for MSEC. */
357 357
358 358 if (n && (n = n->next))
359 359 man->meta.vol = mandoc_strdup(n->string);
360 360 else if ('\0' != man->meta.msec[0] &&
361 361 (NULL != (p = mandoc_a2msec(man->meta.msec))))
362 362 man->meta.vol = mandoc_strdup(p);
363 363
364 364 if (n != NULL && (n = n->next) != NULL)
365 365 mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
366 366 n->line, n->pos, "TH ... %s", n->string);
367 367
368 368 /*
369 369 * Remove the `TH' node after we've processed it for our
370 370 * meta-data.
371 371 */
372 372 roff_node_delete(man, man->last);
373 373 }
374 374
375 375 static void
376 376 post_UC(CHKARGS)
377 377 {
378 378 static const char * const bsd_versions[] = {
379 379 "3rd Berkeley Distribution",
380 380 "4th Berkeley Distribution",
381 381 "4.2 Berkeley Distribution",
382 382 "4.3 Berkeley Distribution",
383 383 "4.4 Berkeley Distribution",
384 384 };
385 385
386 386 const char *p, *s;
387 387
388 388 n = n->child;
389 389
390 390 if (n == NULL || n->type != ROFFT_TEXT)
391 391 p = bsd_versions[0];
392 392 else {
393 393 s = n->string;
394 394 if (0 == strcmp(s, "3"))
395 395 p = bsd_versions[0];
396 396 else if (0 == strcmp(s, "4"))
397 397 p = bsd_versions[1];
398 398 else if (0 == strcmp(s, "5"))
399 399 p = bsd_versions[2];
400 400 else if (0 == strcmp(s, "6"))
401 401 p = bsd_versions[3];
402 402 else if (0 == strcmp(s, "7"))
403 403 p = bsd_versions[4];
404 404 else
405 405 p = bsd_versions[0];
406 406 }
407 407
408 408 free(man->meta.os);
409 409 man->meta.os = mandoc_strdup(p);
410 410 }
411 411
412 412 static void
413 413 post_AT(CHKARGS)
414 414 {
415 415 static const char * const unix_versions[] = {
416 416 "7th Edition",
417 417 "System III",
418 418 "System V",
419 419 "System V Release 2",
420 420 };
421 421
422 422 struct roff_node *nn;
423 423 const char *p, *s;
424 424
425 425 n = n->child;
426 426
427 427 if (n == NULL || n->type != ROFFT_TEXT)
428 428 p = unix_versions[0];
429 429 else {
430 430 s = n->string;
431 431 if (0 == strcmp(s, "3"))
432 432 p = unix_versions[0];
433 433 else if (0 == strcmp(s, "4"))
434 434 p = unix_versions[1];
435 435 else if (0 == strcmp(s, "5")) {
436 436 nn = n->next;
437 437 if (nn != NULL &&
438 438 nn->type == ROFFT_TEXT &&
439 439 nn->string[0] != '\0')
440 440 p = unix_versions[3];
441 441 else
442 442 p = unix_versions[2];
443 443 } else
444 444 p = unix_versions[0];
445 445 }
446 446
447 447 free(man->meta.os);
448 448 man->meta.os = mandoc_strdup(p);
449 449 }
450 450
451 451 static void
452 452 post_in(CHKARGS)
453 453 {
454 454 char *s;
455 455
456 456 if (n->parent->tok != MAN_TP ||
457 457 n->parent->type != ROFFT_HEAD ||
458 458 n->child == NULL ||
459 459 *n->child->string == '+' ||
460 460 *n->child->string == '-')
461 461 return;
462 462 mandoc_asprintf(&s, "+%s", n->child->string);
463 463 free(n->child->string);
464 464 n->child->string = s;
465 465 }
466 466
467 467 static void
468 468 post_vs(CHKARGS)
469 469 {
470 470
471 471 if (NULL != n->prev)
472 472 return;
473 473
474 474 switch (n->parent->tok) {
475 475 case MAN_SH:
476 476 case MAN_SS:
477 477 case MAN_PP:
478 478 case MAN_LP:
479 479 case MAN_P:
480 480 mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
481 481 "%s after %s", roff_name[n->tok],
482 482 roff_name[n->parent->tok]);
483 483 /* FALLTHROUGH */
484 484 case TOKEN_NONE:
485 485 /*
486 486 * Don't warn about this because it occurs in pod2man
487 487 * and would cause considerable (unfixable) warnage.
488 488 */
489 489 roff_node_delete(man, n);
490 490 break;
491 491 default:
492 492 break;
493 493 }
494 494 }
↓ open down ↓ |
329 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX