![]() ![]() | ![]() |
version 1.128, 2017/06/11 19:37:01 | version 1.154, 2020/04/24 12:02:33 | ||
---|---|---|---|
|
|
||
/* $OpenBSD$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> | |||
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> | |||
* | * | ||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||
|
|
||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
* | |||
* Validation module for man(7) syntax trees used by mandoc(1). | |||
*/ | */ | ||
#include "config.h" | #include "config.h" | ||
|
|
||
#include <errno.h> | #include <errno.h> | ||
#include <limits.h> | #include <limits.h> | ||
#include <stdarg.h> | #include <stdarg.h> | ||
#include <stdio.h> | |||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include <time.h> | #include <time.h> | ||
|
|
||
#include "libmandoc.h" | #include "libmandoc.h" | ||
#include "roff_int.h" | #include "roff_int.h" | ||
#include "libman.h" | #include "libman.h" | ||
#include "tag.h" | |||
#define CHKARGS struct roff_man *man, struct roff_node *n | #define CHKARGS struct roff_man *man, struct roff_node *n | ||
typedef void (*v_check)(CHKARGS); | typedef void (*v_check)(CHKARGS); | ||
static void check_abort(CHKARGS) __attribute__((__noreturn__)); | |||
static void check_par(CHKARGS); | static void check_par(CHKARGS); | ||
static void check_part(CHKARGS); | static void check_part(CHKARGS); | ||
static void check_root(CHKARGS); | static void check_root(CHKARGS); | ||
static void check_tag(struct roff_node *, struct roff_node *); | |||
static void check_text(CHKARGS); | static void check_text(CHKARGS); | ||
static void post_AT(CHKARGS); | static void post_AT(CHKARGS); | ||
static void post_EE(CHKARGS); | |||
static void post_EX(CHKARGS); | |||
static void post_IP(CHKARGS); | static void post_IP(CHKARGS); | ||
static void post_vs(CHKARGS); | |||
static void post_OP(CHKARGS); | static void post_OP(CHKARGS); | ||
static void post_SH(CHKARGS); | |||
static void post_TH(CHKARGS); | static void post_TH(CHKARGS); | ||
static void post_TP(CHKARGS); | |||
static void post_UC(CHKARGS); | static void post_UC(CHKARGS); | ||
static void post_UR(CHKARGS); | static void post_UR(CHKARGS); | ||
static void post_in(CHKARGS); | |||
static const v_check __man_valids[MAN_MAX - MAN_TH] = { | static const v_check man_valids[MAN_MAX - MAN_TH] = { | ||
post_TH, /* TH */ | post_TH, /* TH */ | ||
NULL, /* SH */ | post_SH, /* SH */ | ||
NULL, /* SS */ | post_SH, /* SS */ | ||
NULL, /* TP */ | post_TP, /* TP */ | ||
check_par, /* LP */ | post_TP, /* TQ */ | ||
check_abort,/* LP */ | |||
check_par, /* PP */ | check_par, /* PP */ | ||
check_par, /* P */ | check_abort,/* P */ | ||
post_IP, /* IP */ | post_IP, /* IP */ | ||
NULL, /* HP */ | NULL, /* HP */ | ||
NULL, /* SM */ | NULL, /* SM */ | ||
|
|
||
NULL, /* I */ | NULL, /* I */ | ||
NULL, /* IR */ | NULL, /* IR */ | ||
NULL, /* RI */ | NULL, /* RI */ | ||
NULL, /* nf */ | |||
NULL, /* fi */ | |||
NULL, /* RE */ | NULL, /* RE */ | ||
check_part, /* RS */ | check_part, /* RS */ | ||
NULL, /* DT */ | NULL, /* DT */ | ||
post_UC, /* UC */ | post_UC, /* UC */ | ||
NULL, /* PD */ | NULL, /* PD */ | ||
post_AT, /* AT */ | post_AT, /* AT */ | ||
NULL, /* in */ | post_in, /* in */ | ||
NULL, /* SY */ | |||
NULL, /* YS */ | |||
post_OP, /* OP */ | post_OP, /* OP */ | ||
NULL, /* EX */ | post_EX, /* EX */ | ||
NULL, /* EE */ | post_EE, /* EE */ | ||
post_UR, /* UR */ | post_UR, /* UR */ | ||
NULL, /* UE */ | NULL, /* UE */ | ||
post_UR, /* MT */ | |||
NULL, /* ME */ | |||
}; | }; | ||
static const v_check *man_valids = __man_valids - MAN_TH; | |||
/* Validate the subtree rooted at man->last. */ | |||
void | void | ||
man_node_validate(struct roff_man *man) | man_validate(struct roff_man *man) | ||
{ | { | ||
struct roff_node *n; | struct roff_node *n; | ||
const v_check *cp; | const v_check *cp; | ||
/* | |||
* Translate obsolete macros such that later code | |||
* does not need to look for them. | |||
*/ | |||
n = man->last; | n = man->last; | ||
switch (n->tok) { | |||
case MAN_LP: | |||
case MAN_P: | |||
n->tok = MAN_PP; | |||
break; | |||
default: | |||
break; | |||
} | |||
/* | |||
* Iterate over all children, recursing into each one | |||
* in turn, depth-first. | |||
*/ | |||
man->last = man->last->child; | man->last = man->last->child; | ||
while (man->last != NULL) { | while (man->last != NULL) { | ||
man_node_validate(man); | man_validate(man); | ||
if (man->last == n) | if (man->last == n) | ||
man->last = man->last->child; | man->last = man->last->child; | ||
else | else | ||
man->last = man->last->next; | man->last = man->last->next; | ||
} | } | ||
/* Finally validate the macro itself. */ | |||
man->last = n; | man->last = n; | ||
man->next = ROFF_NEXT_SIBLING; | man->next = ROFF_NEXT_SIBLING; | ||
switch (n->type) { | switch (n->type) { | ||
|
|
||
case ROFFT_ROOT: | case ROFFT_ROOT: | ||
check_root(man, n); | check_root(man, n); | ||
break; | break; | ||
case ROFFT_COMMENT: | |||
case ROFFT_EQN: | case ROFFT_EQN: | ||
case ROFFT_TBL: | case ROFFT_TBL: | ||
break; | break; | ||
default: | default: | ||
if (n->tok < ROFF_MAX) { | if (n->tok < ROFF_MAX) { | ||
switch (n->tok) { | roff_validate(man); | ||
case ROFF_br: | |||
case ROFF_sp: | |||
post_vs(man, n); | |||
break; | |||
default: | |||
roff_validate(man); | |||
break; | |||
} | |||
break; | break; | ||
} | } | ||
assert(n->tok >= MAN_TH && n->tok < MAN_MAX); | assert(n->tok >= MAN_TH && n->tok < MAN_MAX); | ||
cp = man_valids + n->tok; | cp = man_valids + (n->tok - MAN_TH); | ||
if (*cp) | if (*cp) | ||
(*cp)(man, n); | (*cp)(man, n); | ||
if (man->last == n) | if (man->last == n) | ||
man_state(man, n); | n->flags |= NODE_VALID; | ||
break; | break; | ||
} | } | ||
} | } | ||
|
|
||
static void | static void | ||
check_root(CHKARGS) | check_root(CHKARGS) | ||
{ | { | ||
assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0); | assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0); | ||
if (NULL == man->first->child) | if (n->last == NULL || n->last->type == ROFFT_COMMENT) | ||
mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse, | mandoc_msg(MANDOCERR_DOC_EMPTY, n->line, n->pos, NULL); | ||
n->line, n->pos, NULL); | |||
else | else | ||
man->meta.hasbody = 1; | man->meta.hasbody = 1; | ||
if (NULL == man->meta.title) { | if (NULL == man->meta.title) { | ||
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse, | mandoc_msg(MANDOCERR_TH_NOTITLE, n->line, n->pos, NULL); | ||
n->line, n->pos, NULL); | |||
/* | /* | ||
* If a title hasn't been set, do so now (by | * If a title hasn't been set, do so now (by | ||
|
|
||
man->meta.title = mandoc_strdup(""); | man->meta.title = mandoc_strdup(""); | ||
man->meta.msec = mandoc_strdup(""); | man->meta.msec = mandoc_strdup(""); | ||
man->meta.date = man->quick ? mandoc_strdup("") : | man->meta.date = mandoc_normdate(NULL, NULL); | ||
mandoc_normdate(man, NULL, n->line, n->pos); | |||
} | } | ||
if (man->meta.os_e && | |||
(man->meta.rcsids & (1 << man->meta.os_e)) == 0) | |||
mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, | |||
man->meta.os_e == MANDOC_OS_OPENBSD ? | |||
"(OpenBSD)" : "(NetBSD)"); | |||
} | } | ||
static void | static void | ||
check_abort(CHKARGS) | |||
{ | |||
abort(); | |||
} | |||
/* | |||
* Skip leading whitespace, dashes, backslashes, and font escapes, | |||
* then create a tag if the first following byte is a letter. | |||
* Priority is high unless whitespace is present. | |||
*/ | |||
static void | |||
check_tag(struct roff_node *n, struct roff_node *nt) | |||
{ | |||
const char *cp, *arg; | |||
int prio, sz; | |||
if (nt == NULL || nt->type != ROFFT_TEXT) | |||
return; | |||
cp = nt->string; | |||
prio = TAG_STRONG; | |||
for (;;) { | |||
switch (*cp) { | |||
case ' ': | |||
case '\t': | |||
prio = TAG_WEAK; | |||
/* FALLTHROUGH */ | |||
case '-': | |||
cp++; | |||
break; | |||
case '\\': | |||
cp++; | |||
switch (mandoc_escape(&cp, &arg, &sz)) { | |||
case ESCAPE_FONT: | |||
case ESCAPE_FONTBOLD: | |||
case ESCAPE_FONTITALIC: | |||
case ESCAPE_FONTBI: | |||
case ESCAPE_FONTROMAN: | |||
case ESCAPE_FONTCW: | |||
case ESCAPE_FONTPREV: | |||
case ESCAPE_IGNORE: | |||
break; | |||
case ESCAPE_SPECIAL: | |||
if (sz != 1) | |||
return; | |||
switch (*arg) { | |||
case '-': | |||
case 'e': | |||
break; | |||
default: | |||
return; | |||
} | |||
break; | |||
default: | |||
return; | |||
} | |||
break; | |||
default: | |||
if (isalpha((unsigned char)*cp)) | |||
tag_put(cp, prio, n); | |||
return; | |||
} | |||
} | |||
} | |||
static void | |||
check_text(CHKARGS) | check_text(CHKARGS) | ||
{ | { | ||
char *cp, *p; | char *cp, *p; | ||
if (MAN_LITERAL & man->flags) | if (n->flags & NODE_NOFILL) | ||
return; | return; | ||
cp = n->string; | cp = n->string; | ||
for (p = cp; NULL != (p = strchr(p, '\t')); p++) | for (p = cp; NULL != (p = strchr(p, '\t')); p++) | ||
mandoc_msg(MANDOCERR_FI_TAB, man->parse, | mandoc_msg(MANDOCERR_FI_TAB, | ||
n->line, n->pos + (p - cp), NULL); | n->line, n->pos + (int)(p - cp), NULL); | ||
} | } | ||
static void | static void | ||
post_EE(CHKARGS) | |||
{ | |||
if ((n->flags & NODE_NOFILL) == 0) | |||
mandoc_msg(MANDOCERR_FI_SKIP, n->line, n->pos, "EE"); | |||
} | |||
static void | |||
post_EX(CHKARGS) | |||
{ | |||
if (n->flags & NODE_NOFILL) | |||
mandoc_msg(MANDOCERR_NF_SKIP, n->line, n->pos, "EX"); | |||
} | |||
static void | |||
post_OP(CHKARGS) | post_OP(CHKARGS) | ||
{ | { | ||
if (n->child == NULL) | if (n->child == NULL) | ||
mandoc_msg(MANDOCERR_OP_EMPTY, man->parse, | mandoc_msg(MANDOCERR_OP_EMPTY, n->line, n->pos, "OP"); | ||
n->line, n->pos, "OP"); | |||
else if (n->child->next != NULL && n->child->next->next != NULL) { | else if (n->child->next != NULL && n->child->next->next != NULL) { | ||
n = n->child->next->next; | n = n->child->next->next; | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, | mandoc_msg(MANDOCERR_ARG_EXCESS, | ||
n->line, n->pos, "OP ... %s", n->string); | n->line, n->pos, "OP ... %s", n->string); | ||
} | } | ||
} | } | ||
static void | static void | ||
post_UR(CHKARGS) | post_SH(CHKARGS) | ||
{ | { | ||
struct roff_node *nc; | |||
char *cp, *tag; | |||
nc = n->child; | |||
switch (n->type) { | |||
case ROFFT_HEAD: | |||
tag = NULL; | |||
deroff(&tag, n); | |||
if (tag != NULL) { | |||
for (cp = tag; *cp != '\0'; cp++) | |||
if (*cp == ' ') | |||
*cp = '_'; | |||
if (nc != NULL && nc->type == ROFFT_TEXT && | |||
strcmp(nc->string, tag) == 0) | |||
tag_put(NULL, TAG_WEAK, n); | |||
else | |||
tag_put(tag, TAG_FALLBACK, n); | |||
free(tag); | |||
} | |||
return; | |||
case ROFFT_BODY: | |||
if (nc != NULL) | |||
break; | |||
return; | |||
default: | |||
return; | |||
} | |||
if (nc->tok == MAN_PP && nc->body->child != NULL) { | |||
while (nc->body->last != NULL) { | |||
man->next = ROFF_NEXT_CHILD; | |||
roff_node_relink(man, nc->body->last); | |||
man->last = n; | |||
} | |||
} | |||
if (nc->tok == MAN_PP || nc->tok == ROFF_sp || nc->tok == ROFF_br) { | |||
mandoc_msg(MANDOCERR_PAR_SKIP, nc->line, nc->pos, | |||
"%s after %s", roff_name[nc->tok], roff_name[n->tok]); | |||
roff_node_delete(man, nc); | |||
} | |||
/* | |||
* Trailing PP is empty, so it is deleted by check_par(). | |||
* Trailing sp is significant. | |||
*/ | |||
if ((nc = n->last) != NULL && nc->tok == ROFF_br) { | |||
mandoc_msg(MANDOCERR_PAR_SKIP, | |||
nc->line, nc->pos, "%s at the end of %s", | |||
roff_name[nc->tok], roff_name[n->tok]); | |||
roff_node_delete(man, nc); | |||
} | |||
} | |||
static void | |||
post_UR(CHKARGS) | |||
{ | |||
if (n->type == ROFFT_HEAD && n->child == NULL) | if (n->type == ROFFT_HEAD && n->child == NULL) | ||
mandoc_vmsg(MANDOCERR_UR_NOHEAD, man->parse, | mandoc_msg(MANDOCERR_UR_NOHEAD, n->line, n->pos, | ||
n->line, n->pos, "UR"); | "%s", roff_name[n->tok]); | ||
check_part(man, n); | check_part(man, n); | ||
} | } | ||
|
|
||
{ | { | ||
if (n->type == ROFFT_BODY && n->child == NULL) | if (n->type == ROFFT_BODY && n->child == NULL) | ||
mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse, | mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, | ||
n->line, n->pos, roff_name[n->tok]); | "%s", roff_name[n->tok]); | ||
} | } | ||
static void | static void | ||
|
|
||
roff_node_delete(man, n); | roff_node_delete(man, n); | ||
break; | break; | ||
case ROFFT_BODY: | case ROFFT_BODY: | ||
if (n->child != NULL && | |||
(n->child->tok == ROFF_sp || n->child->tok == ROFF_br)) { | |||
mandoc_msg(MANDOCERR_PAR_SKIP, | |||
n->child->line, n->child->pos, | |||
"%s after %s", roff_name[n->child->tok], | |||
roff_name[n->tok]); | |||
roff_node_delete(man, n->child); | |||
} | |||
if (n->child == NULL) | if (n->child == NULL) | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, | mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, | ||
man->parse, n->line, n->pos, | |||
"%s empty", roff_name[n->tok]); | "%s empty", roff_name[n->tok]); | ||
break; | break; | ||
case ROFFT_HEAD: | case ROFFT_HEAD: | ||
if (n->child != NULL) | if (n->child != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_SKIP, | mandoc_msg(MANDOCERR_ARG_SKIP, | ||
man->parse, n->line, n->pos, "%s %s%s", | n->line, n->pos, "%s %s%s", | ||
roff_name[n->tok], n->child->string, | roff_name[n->tok], n->child->string, | ||
n->child->next != NULL ? " ..." : ""); | n->child->next != NULL ? " ..." : ""); | ||
break; | break; | ||
|
|
||
static void | static void | ||
post_IP(CHKARGS) | post_IP(CHKARGS) | ||
{ | { | ||
switch (n->type) { | switch (n->type) { | ||
case ROFFT_BLOCK: | case ROFFT_BLOCK: | ||
if (n->head->child == NULL && n->body->child == NULL) | if (n->head->child == NULL && n->body->child == NULL) | ||
roff_node_delete(man, n); | roff_node_delete(man, n); | ||
break; | break; | ||
case ROFFT_HEAD: | |||
check_tag(n, n->child); | |||
break; | |||
case ROFFT_BODY: | case ROFFT_BODY: | ||
if (n->parent->head->child == NULL && n->child == NULL) | if (n->parent->head->child == NULL && n->child == NULL) | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, | mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, | ||
man->parse, n->line, n->pos, | |||
"%s empty", roff_name[n->tok]); | "%s empty", roff_name[n->tok]); | ||
break; | break; | ||
default: | default: | ||
|
|
||
} | } | ||
} | } | ||
/* | |||
* The first next-line element in the head is the tag. | |||
* If that's a font macro, use its first child instead. | |||
*/ | |||
static void | static void | ||
post_TP(CHKARGS) | |||
{ | |||
struct roff_node *nt; | |||
if (n->type != ROFFT_HEAD || (nt = n->child) == NULL) | |||
return; | |||
while ((nt->flags & NODE_LINE) == 0) | |||
if ((nt = nt->next) == NULL) | |||
return; | |||
switch (nt->tok) { | |||
case MAN_B: | |||
case MAN_BI: | |||
case MAN_BR: | |||
case MAN_I: | |||
case MAN_IB: | |||
case MAN_IR: | |||
nt = nt->child; | |||
break; | |||
default: | |||
break; | |||
} | |||
check_tag(n, nt); | |||
} | |||
static void | |||
post_TH(CHKARGS) | post_TH(CHKARGS) | ||
{ | { | ||
struct roff_node *nb; | struct roff_node *nb; | ||
|
|
||
/* ->TITLE<- MSEC DATE OS VOL */ | /* ->TITLE<- MSEC DATE OS VOL */ | ||
n = n->child; | n = n->child; | ||
if (n && n->string) { | if (n != NULL && n->string != NULL) { | ||
for (p = n->string; '\0' != *p; p++) { | for (p = n->string; *p != '\0'; p++) { | ||
/* Only warn about this once... */ | /* Only warn about this once... */ | ||
if (isalpha((unsigned char)*p) && | if (isalpha((unsigned char)*p) && | ||
! isupper((unsigned char)*p)) { | ! isupper((unsigned char)*p)) { | ||
mandoc_vmsg(MANDOCERR_TITLE_CASE, | mandoc_msg(MANDOCERR_TITLE_CASE, n->line, | ||
man->parse, n->line, | n->pos + (int)(p - n->string), | ||
n->pos + (p - n->string), | |||
"TH %s", n->string); | "TH %s", n->string); | ||
break; | break; | ||
} | } | ||
|
|
||
man->meta.title = mandoc_strdup(n->string); | man->meta.title = mandoc_strdup(n->string); | ||
} else { | } else { | ||
man->meta.title = mandoc_strdup(""); | man->meta.title = mandoc_strdup(""); | ||
mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse, | mandoc_msg(MANDOCERR_TH_NOTITLE, nb->line, nb->pos, "TH"); | ||
nb->line, nb->pos, "TH"); | |||
} | } | ||
/* TITLE ->MSEC<- DATE OS VOL */ | /* TITLE ->MSEC<- DATE OS VOL */ | ||
if (n) | if (n != NULL) | ||
n = n->next; | n = n->next; | ||
if (n && n->string) | if (n != NULL && n->string != NULL) { | ||
man->meta.msec = mandoc_strdup(n->string); | man->meta.msec = mandoc_strdup(n->string); | ||
else { | if (man->filesec != '\0' && | ||
man->filesec != *n->string && | |||
*n->string >= '1' && *n->string <= '9') | |||
mandoc_msg(MANDOCERR_MSEC_FILE, n->line, n->pos, | |||
"*.%c vs TH ... %c", man->filesec, *n->string); | |||
} else { | |||
man->meta.msec = mandoc_strdup(""); | man->meta.msec = mandoc_strdup(""); | ||
mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse, | mandoc_msg(MANDOCERR_MSEC_MISSING, | ||
nb->line, nb->pos, "TH %s", man->meta.title); | nb->line, nb->pos, "TH %s", man->meta.title); | ||
} | } | ||
/* TITLE MSEC ->DATE<- OS VOL */ | /* TITLE MSEC ->DATE<- OS VOL */ | ||
if (n) | if (n != NULL) | ||
n = n->next; | n = n->next; | ||
if (n && n->string && '\0' != n->string[0]) { | if (man->quick && n != NULL) | ||
man->meta.date = man->quick ? | |||
mandoc_strdup(n->string) : | |||
mandoc_normdate(man, n->string, n->line, n->pos); | |||
} else { | |||
man->meta.date = mandoc_strdup(""); | man->meta.date = mandoc_strdup(""); | ||
mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, | else | ||
n ? n->line : nb->line, | man->meta.date = mandoc_normdate(n, nb); | ||
n ? n->pos : nb->pos, "TH"); | |||
} | |||
/* TITLE MSEC DATE ->OS<- VOL */ | /* TITLE MSEC DATE ->OS<- VOL */ | ||
if (n && (n = n->next)) | if (n && (n = n->next)) | ||
man->meta.os = mandoc_strdup(n->string); | man->meta.os = mandoc_strdup(n->string); | ||
else if (man->defos != NULL) | else if (man->os_s != NULL) | ||
man->meta.os = mandoc_strdup(man->defos); | man->meta.os = mandoc_strdup(man->os_s); | ||
if (man->meta.os_e == MANDOC_OS_OTHER && man->meta.os != NULL) { | |||
if (strstr(man->meta.os, "OpenBSD") != NULL) | |||
man->meta.os_e = MANDOC_OS_OPENBSD; | |||
else if (strstr(man->meta.os, "NetBSD") != NULL) | |||
man->meta.os_e = MANDOC_OS_NETBSD; | |||
} | |||
/* TITLE MSEC DATE OS ->VOL<- */ | /* TITLE MSEC DATE OS ->VOL<- */ | ||
/* If missing, use the default VOL name for MSEC. */ | /* If missing, use the default VOL name for MSEC. */ | ||
|
|
||
man->meta.vol = mandoc_strdup(p); | man->meta.vol = mandoc_strdup(p); | ||
if (n != NULL && (n = n->next) != NULL) | if (n != NULL && (n = n->next) != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, | mandoc_msg(MANDOCERR_ARG_EXCESS, | ||
n->line, n->pos, "TH ... %s", n->string); | n->line, n->pos, "TH ... %s", n->string); | ||
/* | /* | ||
|
|
||
} | } | ||
static void | static void | ||
post_vs(CHKARGS) | post_in(CHKARGS) | ||
{ | { | ||
char *s; | |||
if (NULL != n->prev) | if (n->parent->tok != MAN_TP || | ||
n->parent->type != ROFFT_HEAD || | |||
n->child == NULL || | |||
*n->child->string == '+' || | |||
*n->child->string == '-') | |||
return; | return; | ||
mandoc_asprintf(&s, "+%s", n->child->string); | |||
switch (n->parent->tok) { | free(n->child->string); | ||
case MAN_SH: | n->child->string = s; | ||
case MAN_SS: | |||
case MAN_PP: | |||
case MAN_LP: | |||
case MAN_P: | |||
mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos, | |||
"%s after %s", roff_name[n->tok], | |||
roff_name[n->parent->tok]); | |||
/* FALLTHROUGH */ | |||
case TOKEN_NONE: | |||
/* | |||
* Don't warn about this because it occurs in pod2man | |||
* and would cause considerable (unfixable) warnage. | |||
*/ | |||
roff_node_delete(man, n); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | } |