3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
31 #include "mandoc_aux.h"
35 #include "libmandoc.h"
39 #define CHKARGS struct roff_man *man, struct roff_node *n
41 typedef void (*v_check)(CHKARGS);
43 static void check_par(CHKARGS);
44 static void check_part(CHKARGS);
45 static void check_root(CHKARGS);
46 static void check_text(CHKARGS);
48 static void post_AT(CHKARGS);
49 static void post_IP(CHKARGS);
50 static void post_vs(CHKARGS);
51 static void post_ft(CHKARGS);
52 static void post_OP(CHKARGS);
53 static void post_TH(CHKARGS);
54 static void post_UC(CHKARGS);
55 static void post_UR(CHKARGS);
57 static v_check man_valids[MAN_MAX] = {
100 man_node_validate(struct roff_man *man)
106 man->last = man->last->child;
107 while (man->last != NULL) {
108 man_node_validate(man);
110 man->last = man->last->child;
112 man->last = man->last->next;
116 man->next = ROFF_NEXT_SIBLING;
128 cp = man_valids + n->tok;
141 assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
143 if (NULL == man->first->child)
144 mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
145 n->line, n->pos, NULL);
147 man->meta.hasbody = 1;
149 if (NULL == man->meta.title) {
150 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
151 n->line, n->pos, NULL);
154 * If a title hasn't been set, do so now (by
155 * implication, date and section also aren't set).
158 man->meta.title = mandoc_strdup("");
159 man->meta.msec = mandoc_strdup("");
160 man->meta.date = man->quick ? mandoc_strdup("") :
161 mandoc_normdate(man->parse, NULL, n->line, n->pos);
170 if (MAN_LITERAL & man->flags)
174 for (p = cp; NULL != (p = strchr(p, '\t')); p++)
175 mandoc_msg(MANDOCERR_FI_TAB, man->parse,
176 n->line, n->pos + (p - cp), NULL);
183 if (n->child == NULL)
184 mandoc_msg(MANDOCERR_OP_EMPTY, man->parse,
185 n->line, n->pos, "OP");
186 else if (n->child->next != NULL && n->child->next->next != NULL) {
187 n = n->child->next->next;
188 mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
189 n->line, n->pos, "OP ... %s", n->string);
197 if (n->type == ROFFT_HEAD && n->child == NULL)
198 mandoc_vmsg(MANDOCERR_UR_NOHEAD, man->parse,
199 n->line, n->pos, "UR");
209 if (n->child == NULL)
213 cp = n->child->string;
226 if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
230 if ('W' == cp[1] && '\0' == cp[2])
238 mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
239 n->line, n->pos, "ft %s", cp);
248 if (n->type == ROFFT_BODY && n->child == NULL)
249 mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse,
250 n->line, n->pos, man_macronames[n->tok]);
259 if (n->body->child == NULL)
260 roff_node_delete(man, n);
263 if (n->child == NULL)
264 mandoc_vmsg(MANDOCERR_PAR_SKIP,
265 man->parse, n->line, n->pos,
266 "%s empty", man_macronames[n->tok]);
269 if (n->child != NULL)
270 mandoc_vmsg(MANDOCERR_ARG_SKIP,
271 man->parse, n->line, n->pos,
272 "%s %s%s", man_macronames[n->tok],
274 n->child->next != NULL ? " ..." : "");
287 if (n->head->child == NULL && n->body->child == NULL)
288 roff_node_delete(man, n);
291 if (n->parent->head->child == NULL && n->child == NULL)
292 mandoc_vmsg(MANDOCERR_PAR_SKIP,
293 man->parse, n->line, n->pos,
294 "%s empty", man_macronames[n->tok]);
304 struct roff_node *nb;
307 free(man->meta.title);
310 free(man->meta.msec);
311 free(man->meta.date);
313 man->meta.title = man->meta.vol = man->meta.date =
314 man->meta.msec = man->meta.os = NULL;
318 /* ->TITLE<- MSEC DATE OS VOL */
321 if (n && n->string) {
322 for (p = n->string; '\0' != *p; p++) {
323 /* Only warn about this once... */
324 if (isalpha((unsigned char)*p) &&
325 ! isupper((unsigned char)*p)) {
326 mandoc_vmsg(MANDOCERR_TITLE_CASE,
328 n->pos + (p - n->string),
333 man->meta.title = mandoc_strdup(n->string);
335 man->meta.title = mandoc_strdup("");
336 mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
337 nb->line, nb->pos, "TH");
340 /* TITLE ->MSEC<- DATE OS VOL */
345 man->meta.msec = mandoc_strdup(n->string);
347 man->meta.msec = mandoc_strdup("");
348 mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
349 nb->line, nb->pos, "TH %s", man->meta.title);
352 /* TITLE MSEC ->DATE<- OS VOL */
356 if (n && n->string && '\0' != n->string[0]) {
357 man->meta.date = man->quick ?
358 mandoc_strdup(n->string) :
359 mandoc_normdate(man->parse, n->string,
362 man->meta.date = mandoc_strdup("");
363 mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
364 n ? n->line : nb->line,
365 n ? n->pos : nb->pos, "TH");
368 /* TITLE MSEC DATE ->OS<- VOL */
370 if (n && (n = n->next))
371 man->meta.os = mandoc_strdup(n->string);
372 else if (man->defos != NULL)
373 man->meta.os = mandoc_strdup(man->defos);
375 /* TITLE MSEC DATE OS ->VOL<- */
376 /* If missing, use the default VOL name for MSEC. */
378 if (n && (n = n->next))
379 man->meta.vol = mandoc_strdup(n->string);
380 else if ('\0' != man->meta.msec[0] &&
381 (NULL != (p = mandoc_a2msec(man->meta.msec))))
382 man->meta.vol = mandoc_strdup(p);
384 if (n != NULL && (n = n->next) != NULL)
385 mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
386 n->line, n->pos, "TH ... %s", n->string);
389 * Remove the `TH' node after we've processed it for our
392 roff_node_delete(man, man->last);
398 static const char * const bsd_versions[] = {
399 "3rd Berkeley Distribution",
400 "4th Berkeley Distribution",
401 "4.2 Berkeley Distribution",
402 "4.3 Berkeley Distribution",
403 "4.4 Berkeley Distribution",
410 if (n == NULL || n->type != ROFFT_TEXT)
414 if (0 == strcmp(s, "3"))
416 else if (0 == strcmp(s, "4"))
418 else if (0 == strcmp(s, "5"))
420 else if (0 == strcmp(s, "6"))
422 else if (0 == strcmp(s, "7"))
429 man->meta.os = mandoc_strdup(p);
435 static const char * const unix_versions[] = {
439 "System V Release 2",
442 struct roff_node *nn;
447 if (n == NULL || n->type != ROFFT_TEXT)
448 p = unix_versions[0];
451 if (0 == strcmp(s, "3"))
452 p = unix_versions[0];
453 else if (0 == strcmp(s, "4"))
454 p = unix_versions[1];
455 else if (0 == strcmp(s, "5")) {
458 nn->type == ROFFT_TEXT &&
459 nn->string[0] != '\0')
460 p = unix_versions[3];
462 p = unix_versions[2];
464 p = unix_versions[0];
468 man->meta.os = mandoc_strdup(p);
478 switch (n->parent->tok) {
481 mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
482 "%s after %s", man_macronames[n->tok],
483 man_macronames[n->parent->tok]);
487 * Don't warn about this because it occurs in pod2man
488 * and would cause considerable (unfixable) warnage.
490 roff_node_delete(man, n);