Return to mdoc_validate.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.273, 2015/02/06 02:04:54 | version 1.337, 2017/06/11 19:37:01 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> | * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org> | * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> | ||
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> | * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.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 | ||
* copyright notice and this permission notice appear in all copies. | * copyright notice and this permission notice appear in all copies. | ||
* | * | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | ||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | ||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* 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 | ||
|
|
||
#include <string.h> | #include <string.h> | ||
#include <time.h> | #include <time.h> | ||
#include "mdoc.h" | |||
#include "mandoc.h" | |||
#include "mandoc_aux.h" | #include "mandoc_aux.h" | ||
#include "libmdoc.h" | #include "mandoc.h" | ||
#include "roff.h" | |||
#include "mdoc.h" | |||
#include "libmandoc.h" | #include "libmandoc.h" | ||
#include "roff_int.h" | |||
#include "libmdoc.h" | |||
/* FIXME: .Bl -diag can't have non-text children in HEAD. */ | /* FIXME: .Bl -diag can't have non-text children in HEAD. */ | ||
#define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n | #define POST_ARGS struct roff_man *mdoc | ||
#define POST_ARGS struct mdoc *mdoc | |||
enum check_ineq { | enum check_ineq { | ||
CHECK_LT, | CHECK_LT, | ||
|
|
||
CHECK_EQ | CHECK_EQ | ||
}; | }; | ||
typedef void (*v_pre)(PRE_ARGS); | |||
typedef void (*v_post)(POST_ARGS); | typedef void (*v_post)(POST_ARGS); | ||
struct valids { | static int build_list(struct roff_man *, int); | ||
v_pre pre; | static void check_text(struct roff_man *, int, int, char *); | ||
v_post post; | static void check_argv(struct roff_man *, | ||
}; | struct roff_node *, struct mdoc_argv *); | ||
static void check_args(struct roff_man *, struct roff_node *); | |||
static void check_toptext(struct roff_man *, int, int, const char *); | |||
static int child_an(const struct roff_node *); | |||
static size_t macro2len(enum roff_tok); | |||
static void rewrite_macro2len(struct roff_man *, char **); | |||
static void check_count(struct mdoc *, enum mdoc_type, | |||
enum check_ineq, int); | |||
static void check_text(struct mdoc *, int, int, char *); | |||
static void check_argv(struct mdoc *, | |||
struct mdoc_node *, struct mdoc_argv *); | |||
static void check_args(struct mdoc *, struct mdoc_node *); | |||
static int child_an(const struct mdoc_node *); | |||
static enum mdoc_sec a2sec(const char *); | |||
static size_t macro2len(enum mdoct); | |||
static void rewrite_macro2len(char **); | |||
static void bwarn_ge1(POST_ARGS); | |||
static void ewarn_eq1(POST_ARGS); | |||
static void ewarn_ge1(POST_ARGS); | |||
static void post_an(POST_ARGS); | static void post_an(POST_ARGS); | ||
static void post_an_norm(POST_ARGS); | |||
static void post_at(POST_ARGS); | static void post_at(POST_ARGS); | ||
static void post_bd(POST_ARGS); | |||
static void post_bf(POST_ARGS); | static void post_bf(POST_ARGS); | ||
static void post_bk(POST_ARGS); | static void post_bk(POST_ARGS); | ||
static void post_bl(POST_ARGS); | static void post_bl(POST_ARGS); | ||
static void post_bl_block(POST_ARGS); | static void post_bl_block(POST_ARGS); | ||
static void post_bl_block_tag(POST_ARGS); | |||
static void post_bl_head(POST_ARGS); | static void post_bl_head(POST_ARGS); | ||
static void post_bl_norm(POST_ARGS); | |||
static void post_bx(POST_ARGS); | static void post_bx(POST_ARGS); | ||
static void post_d1(POST_ARGS); | |||
static void post_defaults(POST_ARGS); | static void post_defaults(POST_ARGS); | ||
static void post_display(POST_ARGS); | |||
static void post_dd(POST_ARGS); | static void post_dd(POST_ARGS); | ||
static void post_delim(POST_ARGS); | |||
static void post_dt(POST_ARGS); | static void post_dt(POST_ARGS); | ||
static void post_en(POST_ARGS); | static void post_en(POST_ARGS); | ||
static void post_es(POST_ARGS); | static void post_es(POST_ARGS); | ||
|
|
||
static void post_fname(POST_ARGS); | static void post_fname(POST_ARGS); | ||
static void post_fo(POST_ARGS); | static void post_fo(POST_ARGS); | ||
static void post_hyph(POST_ARGS); | static void post_hyph(POST_ARGS); | ||
static void post_hyphtext(POST_ARGS); | |||
static void post_ignpar(POST_ARGS); | static void post_ignpar(POST_ARGS); | ||
static void post_it(POST_ARGS); | static void post_it(POST_ARGS); | ||
static void post_lb(POST_ARGS); | static void post_lb(POST_ARGS); | ||
static void post_literal(POST_ARGS); | |||
static void post_nd(POST_ARGS); | static void post_nd(POST_ARGS); | ||
static void post_nm(POST_ARGS); | static void post_nm(POST_ARGS); | ||
static void post_ns(POST_ARGS); | static void post_ns(POST_ARGS); | ||
static void post_obsolete(POST_ARGS); | |||
static void post_os(POST_ARGS); | static void post_os(POST_ARGS); | ||
static void post_par(POST_ARGS); | static void post_par(POST_ARGS); | ||
static void post_prevpar(POST_ARGS); | |||
static void post_root(POST_ARGS); | static void post_root(POST_ARGS); | ||
static void post_rs(POST_ARGS); | static void post_rs(POST_ARGS); | ||
static void post_rv(POST_ARGS); | |||
static void post_sh(POST_ARGS); | static void post_sh(POST_ARGS); | ||
static void post_sh_head(POST_ARGS); | static void post_sh_head(POST_ARGS); | ||
static void post_sh_name(POST_ARGS); | static void post_sh_name(POST_ARGS); | ||
|
|
||
static void post_sh_authors(POST_ARGS); | static void post_sh_authors(POST_ARGS); | ||
static void post_sm(POST_ARGS); | static void post_sm(POST_ARGS); | ||
static void post_st(POST_ARGS); | static void post_st(POST_ARGS); | ||
static void post_vt(POST_ARGS); | static void post_std(POST_ARGS); | ||
static void post_useless(POST_ARGS); | |||
static void post_xr(POST_ARGS); | |||
static void post_xx(POST_ARGS); | |||
static void pre_an(PRE_ARGS); | static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { | ||
static void pre_bd(PRE_ARGS); | post_dd, /* Dd */ | ||
static void pre_bl(PRE_ARGS); | post_dt, /* Dt */ | ||
static void pre_dd(PRE_ARGS); | post_os, /* Os */ | ||
static void pre_display(PRE_ARGS); | post_sh, /* Sh */ | ||
static void pre_dt(PRE_ARGS); | post_ignpar, /* Ss */ | ||
static void pre_literal(PRE_ARGS); | post_par, /* Pp */ | ||
static void pre_obsolete(PRE_ARGS); | post_display, /* D1 */ | ||
static void pre_os(PRE_ARGS); | post_display, /* Dl */ | ||
static void pre_par(PRE_ARGS); | post_display, /* Bd */ | ||
static void pre_std(PRE_ARGS); | NULL, /* Ed */ | ||
post_bl, /* Bl */ | |||
static const struct valids mdoc_valids[MDOC_MAX] = { | NULL, /* El */ | ||
{ NULL, NULL }, /* Ap */ | post_it, /* It */ | ||
{ pre_dd, post_dd }, /* Dd */ | post_delim, /* Ad */ | ||
{ pre_dt, post_dt }, /* Dt */ | post_an, /* An */ | ||
{ pre_os, post_os }, /* Os */ | NULL, /* Ap */ | ||
{ NULL, post_sh }, /* Sh */ | post_defaults, /* Ar */ | ||
{ NULL, post_ignpar }, /* Ss */ | NULL, /* Cd */ | ||
{ pre_par, post_par }, /* Pp */ | post_delim, /* Cm */ | ||
{ pre_display, post_d1 }, /* D1 */ | post_delim, /* Dv */ | ||
{ pre_literal, post_literal }, /* Dl */ | post_delim, /* Er */ | ||
{ pre_bd, post_literal }, /* Bd */ | post_delim, /* Ev */ | ||
{ NULL, NULL }, /* Ed */ | post_ex, /* Ex */ | ||
{ pre_bl, post_bl }, /* Bl */ | post_fa, /* Fa */ | ||
{ NULL, NULL }, /* El */ | NULL, /* Fd */ | ||
{ pre_par, post_it }, /* It */ | post_delim, /* Fl */ | ||
{ NULL, NULL }, /* Ad */ | post_fn, /* Fn */ | ||
{ pre_an, post_an }, /* An */ | post_delim, /* Ft */ | ||
{ NULL, post_defaults }, /* Ar */ | post_delim, /* Ic */ | ||
{ NULL, NULL }, /* Cd */ | post_delim, /* In */ | ||
{ NULL, NULL }, /* Cm */ | post_defaults, /* Li */ | ||
{ NULL, NULL }, /* Dv */ | post_nd, /* Nd */ | ||
{ NULL, NULL }, /* Er */ | post_nm, /* Nm */ | ||
{ NULL, NULL }, /* Ev */ | post_delim, /* Op */ | ||
{ pre_std, post_ex }, /* Ex */ | post_obsolete, /* Ot */ | ||
{ NULL, post_fa }, /* Fa */ | post_defaults, /* Pa */ | ||
{ NULL, ewarn_ge1 }, /* Fd */ | post_rv, /* Rv */ | ||
{ NULL, NULL }, /* Fl */ | post_st, /* St */ | ||
{ NULL, post_fn }, /* Fn */ | post_delim, /* Va */ | ||
{ NULL, NULL }, /* Ft */ | post_delim, /* Vt */ | ||
{ NULL, NULL }, /* Ic */ | post_xr, /* Xr */ | ||
{ NULL, NULL }, /* In */ | NULL, /* %A */ | ||
{ NULL, post_defaults }, /* Li */ | post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ | ||
{ NULL, post_nd }, /* Nd */ | NULL, /* %D */ | ||
{ NULL, post_nm }, /* Nm */ | NULL, /* %I */ | ||
{ NULL, NULL }, /* Op */ | NULL, /* %J */ | ||
{ pre_obsolete, NULL }, /* Ot */ | post_hyph, /* %N */ | ||
{ NULL, post_defaults }, /* Pa */ | post_hyph, /* %O */ | ||
{ pre_std, NULL }, /* Rv */ | NULL, /* %P */ | ||
{ NULL, post_st }, /* St */ | post_hyph, /* %R */ | ||
{ NULL, NULL }, /* Va */ | post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ | ||
{ NULL, post_vt }, /* Vt */ | NULL, /* %V */ | ||
{ NULL, NULL }, /* Xr */ | NULL, /* Ac */ | ||
{ NULL, ewarn_ge1 }, /* %A */ | post_delim, /* Ao */ | ||
{ NULL, post_hyphtext }, /* %B */ /* FIXME: can be used outside Rs/Re. */ | post_delim, /* Aq */ | ||
{ NULL, ewarn_ge1 }, /* %D */ | post_at, /* At */ | ||
{ NULL, ewarn_ge1 }, /* %I */ | NULL, /* Bc */ | ||
{ NULL, ewarn_ge1 }, /* %J */ | post_bf, /* Bf */ | ||
{ NULL, post_hyphtext }, /* %N */ | post_delim, /* Bo */ | ||
{ NULL, post_hyphtext }, /* %O */ | NULL, /* Bq */ | ||
{ NULL, ewarn_ge1 }, /* %P */ | post_xx, /* Bsx */ | ||
{ NULL, post_hyphtext }, /* %R */ | post_bx, /* Bx */ | ||
{ NULL, post_hyphtext }, /* %T */ /* FIXME: can be used outside Rs/Re. */ | post_obsolete, /* Db */ | ||
{ NULL, ewarn_ge1 }, /* %V */ | NULL, /* Dc */ | ||
{ NULL, NULL }, /* Ac */ | NULL, /* Do */ | ||
{ NULL, NULL }, /* Ao */ | NULL, /* Dq */ | ||
{ NULL, NULL }, /* Aq */ | NULL, /* Ec */ | ||
{ NULL, post_at }, /* At */ | NULL, /* Ef */ | ||
{ NULL, NULL }, /* Bc */ | post_delim, /* Em */ | ||
{ NULL, post_bf }, /* Bf */ | NULL, /* Eo */ | ||
{ NULL, NULL }, /* Bo */ | post_xx, /* Fx */ | ||
{ NULL, NULL }, /* Bq */ | post_delim, /* Ms */ | ||
{ NULL, NULL }, /* Bsx */ | NULL, /* No */ | ||
{ NULL, post_bx }, /* Bx */ | post_ns, /* Ns */ | ||
{ pre_obsolete, NULL }, /* Db */ | post_xx, /* Nx */ | ||
{ NULL, NULL }, /* Dc */ | post_xx, /* Ox */ | ||
{ NULL, NULL }, /* Do */ | NULL, /* Pc */ | ||
{ NULL, NULL }, /* Dq */ | NULL, /* Pf */ | ||
{ NULL, NULL }, /* Ec */ | post_delim, /* Po */ | ||
{ NULL, NULL }, /* Ef */ | post_delim, /* Pq */ | ||
{ NULL, NULL }, /* Em */ | NULL, /* Qc */ | ||
{ NULL, NULL }, /* Eo */ | post_delim, /* Ql */ | ||
{ NULL, NULL }, /* Fx */ | post_delim, /* Qo */ | ||
{ NULL, NULL }, /* Ms */ | post_delim, /* Qq */ | ||
{ NULL, NULL }, /* No */ | NULL, /* Re */ | ||
{ NULL, post_ns }, /* Ns */ | post_rs, /* Rs */ | ||
{ NULL, NULL }, /* Nx */ | NULL, /* Sc */ | ||
{ NULL, NULL }, /* Ox */ | post_delim, /* So */ | ||
{ NULL, NULL }, /* Pc */ | post_delim, /* Sq */ | ||
{ NULL, NULL }, /* Pf */ | post_sm, /* Sm */ | ||
{ NULL, NULL }, /* Po */ | post_hyph, /* Sx */ | ||
{ NULL, NULL }, /* Pq */ | post_delim, /* Sy */ | ||
{ NULL, NULL }, /* Qc */ | post_useless, /* Tn */ | ||
{ NULL, NULL }, /* Ql */ | post_xx, /* Ux */ | ||
{ NULL, NULL }, /* Qo */ | NULL, /* Xc */ | ||
{ NULL, NULL }, /* Qq */ | NULL, /* Xo */ | ||
{ NULL, NULL }, /* Re */ | post_fo, /* Fo */ | ||
{ NULL, post_rs }, /* Rs */ | NULL, /* Fc */ | ||
{ NULL, NULL }, /* Sc */ | post_delim, /* Oo */ | ||
{ NULL, NULL }, /* So */ | NULL, /* Oc */ | ||
{ NULL, NULL }, /* Sq */ | post_bk, /* Bk */ | ||
{ NULL, post_sm }, /* Sm */ | NULL, /* Ek */ | ||
{ NULL, post_hyph }, /* Sx */ | post_eoln, /* Bt */ | ||
{ NULL, NULL }, /* Sy */ | post_obsolete, /* Hf */ | ||
{ NULL, NULL }, /* Tn */ | post_obsolete, /* Fr */ | ||
{ NULL, NULL }, /* Ux */ | post_eoln, /* Ud */ | ||
{ NULL, NULL }, /* Xc */ | post_lb, /* Lb */ | ||
{ NULL, NULL }, /* Xo */ | post_par, /* Lp */ | ||
{ NULL, post_fo }, /* Fo */ | post_delim, /* Lk */ | ||
{ NULL, NULL }, /* Fc */ | post_defaults, /* Mt */ | ||
{ NULL, NULL }, /* Oo */ | post_delim, /* Brq */ | ||
{ NULL, NULL }, /* Oc */ | post_delim, /* Bro */ | ||
{ NULL, post_bk }, /* Bk */ | NULL, /* Brc */ | ||
{ NULL, NULL }, /* Ek */ | NULL, /* %C */ | ||
{ NULL, post_eoln }, /* Bt */ | post_es, /* Es */ | ||
{ NULL, NULL }, /* Hf */ | post_en, /* En */ | ||
{ pre_obsolete, NULL }, /* Fr */ | post_xx, /* Dx */ | ||
{ NULL, post_eoln }, /* Ud */ | NULL, /* %Q */ | ||
{ NULL, post_lb }, /* Lb */ | NULL, /* %U */ | ||
{ pre_par, post_par }, /* Lp */ | NULL, /* Ta */ | ||
{ NULL, NULL }, /* Lk */ | |||
{ NULL, post_defaults }, /* Mt */ | |||
{ NULL, NULL }, /* Brq */ | |||
{ NULL, NULL }, /* Bro */ | |||
{ NULL, NULL }, /* Brc */ | |||
{ NULL, ewarn_ge1 }, /* %C */ | |||
{ pre_obsolete, post_es }, /* Es */ | |||
{ pre_obsolete, post_en }, /* En */ | |||
{ NULL, NULL }, /* Dx */ | |||
{ NULL, ewarn_ge1 }, /* %Q */ | |||
{ NULL, post_par }, /* br */ | |||
{ NULL, post_par }, /* sp */ | |||
{ NULL, ewarn_eq1 }, /* %U */ | |||
{ NULL, NULL }, /* Ta */ | |||
{ NULL, NULL }, /* ll */ | |||
}; | }; | ||
static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd; | |||
#define RSORD_MAX 14 /* Number of `Rs' blocks. */ | #define RSORD_MAX 14 /* Number of `Rs' blocks. */ | ||
static const enum mdoct rsord[RSORD_MAX] = { | static const enum roff_tok rsord[RSORD_MAX] = { | ||
MDOC__A, | MDOC__A, | ||
MDOC__T, | MDOC__T, | ||
MDOC__B, | MDOC__B, | ||
|
|
||
void | void | ||
mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) | mdoc_node_validate(struct roff_man *mdoc) | ||
{ | { | ||
v_pre p; | struct roff_node *n; | ||
const v_post *p; | |||
switch (n->type) { | n = mdoc->last; | ||
case MDOC_TEXT: | mdoc->last = mdoc->last->child; | ||
check_text(mdoc, n->line, n->pos, n->string); | while (mdoc->last != NULL) { | ||
/* FALLTHROUGH */ | mdoc_node_validate(mdoc); | ||
case MDOC_TBL: | if (mdoc->last == n) | ||
/* FALLTHROUGH */ | mdoc->last = mdoc->last->child; | ||
case MDOC_EQN: | else | ||
/* FALLTHROUGH */ | mdoc->last = mdoc->last->next; | ||
case MDOC_ROOT: | |||
return; | |||
default: | |||
break; | |||
} | } | ||
check_args(mdoc, n); | mdoc->last = n; | ||
p = mdoc_valids[n->tok].pre; | mdoc->next = ROFF_NEXT_SIBLING; | ||
if (*p) | |||
(*p)(mdoc, n); | |||
} | |||
void | |||
mdoc_valid_post(struct mdoc *mdoc) | |||
{ | |||
struct mdoc_node *n; | |||
v_post p; | |||
n = mdoc->last; | |||
if (n->flags & MDOC_VALID) | |||
return; | |||
n->flags |= MDOC_VALID; | |||
switch (n->type) { | switch (n->type) { | ||
case MDOC_TEXT: | case ROFFT_TEXT: | ||
/* FALLTHROUGH */ | if (n->sec != SEC_SYNOPSIS || | ||
case MDOC_EQN: | (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd)) | ||
/* FALLTHROUGH */ | check_text(mdoc, n->line, n->pos, n->string); | ||
case MDOC_TBL: | if (n->parent->tok == MDOC_It || | ||
(n->parent->type == ROFFT_BODY && | |||
(n->parent->tok == MDOC_Sh || | |||
n->parent->tok == MDOC_Ss))) | |||
check_toptext(mdoc, n->line, n->pos, n->string); | |||
break; | break; | ||
case MDOC_ROOT: | case ROFFT_EQN: | ||
case ROFFT_TBL: | |||
break; | |||
case ROFFT_ROOT: | |||
post_root(mdoc); | post_root(mdoc); | ||
break; | break; | ||
default: | default: | ||
check_args(mdoc, mdoc->last); | |||
/* | /* | ||
* Closing delimiters are not special at the | * Closing delimiters are not special at the | ||
|
|
||
*/ | */ | ||
if (n->child != NULL) | if (n->child != NULL) | ||
n->child->flags &= ~MDOC_DELIMC; | n->child->flags &= ~NODE_DELIMC; | ||
if (n->last != NULL) | if (n->last != NULL) | ||
n->last->flags &= ~MDOC_DELIMO; | n->last->flags &= ~NODE_DELIMO; | ||
/* Call the macro's postprocessor. */ | /* Call the macro's postprocessor. */ | ||
p = mdoc_valids[n->tok].post; | if (n->tok < ROFF_MAX) { | ||
switch(n->tok) { | |||
case ROFF_br: | |||
case ROFF_sp: | |||
post_par(mdoc); | |||
break; | |||
default: | |||
roff_validate(mdoc); | |||
break; | |||
} | |||
break; | |||
} | |||
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); | |||
p = mdoc_valids + n->tok; | |||
if (*p) | if (*p) | ||
(*p)(mdoc); | (*p)(mdoc); | ||
if (mdoc->last == n) | |||
mdoc_state(mdoc, n); | |||
break; | break; | ||
} | } | ||
} | } | ||
static void | static void | ||
check_count(struct mdoc *mdoc, enum mdoc_type type, | check_args(struct roff_man *mdoc, struct roff_node *n) | ||
enum check_ineq ineq, int val) | |||
{ | { | ||
const char *p; | |||
if (mdoc->last->type != type) | |||
return; | |||
switch (ineq) { | |||
case CHECK_LT: | |||
p = "less than "; | |||
if (mdoc->last->nchild < val) | |||
return; | |||
break; | |||
case CHECK_GT: | |||
p = "more than "; | |||
if (mdoc->last->nchild > val) | |||
return; | |||
break; | |||
case CHECK_EQ: | |||
p = ""; | |||
if (val == mdoc->last->nchild) | |||
return; | |||
break; | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
mandoc_vmsg(MANDOCERR_ARGCWARN, mdoc->parse, mdoc->last->line, | |||
mdoc->last->pos, "want %s%d children (have %d)", | |||
p, val, mdoc->last->nchild); | |||
} | |||
static void | |||
bwarn_ge1(POST_ARGS) | |||
{ | |||
check_count(mdoc, MDOC_BODY, CHECK_GT, 0); | |||
} | |||
static void | |||
ewarn_eq1(POST_ARGS) | |||
{ | |||
check_count(mdoc, MDOC_ELEM, CHECK_EQ, 1); | |||
} | |||
static void | |||
ewarn_ge1(POST_ARGS) | |||
{ | |||
check_count(mdoc, MDOC_ELEM, CHECK_GT, 0); | |||
} | |||
static void | |||
check_args(struct mdoc *mdoc, struct mdoc_node *n) | |||
{ | |||
int i; | int i; | ||
if (NULL == n->args) | if (NULL == n->args) | ||
|
|
||
} | } | ||
static void | static void | ||
check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v) | check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) | ||
{ | { | ||
int i; | int i; | ||
|
|
||
} | } | ||
static void | static void | ||
check_text(struct mdoc *mdoc, int ln, int pos, char *p) | check_text(struct roff_man *mdoc, int ln, int pos, char *p) | ||
{ | { | ||
char *cp; | char *cp; | ||
|
|
||
} | } | ||
static void | static void | ||
pre_display(PRE_ARGS) | check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) | ||
{ | { | ||
struct mdoc_node *node; | const char *cp, *cpr; | ||
if (MDOC_BLOCK != n->type) | if (*p == '\0') | ||
return; | return; | ||
for (node = mdoc->last->parent; node; node = node->parent) | if ((cp = strstr(p, "OpenBSD")) != NULL) | ||
if (MDOC_BLOCK == node->type) | mandoc_msg(MANDOCERR_BX, mdoc->parse, | ||
if (MDOC_Bd == node->tok) | ln, pos + (cp - p), "Ox"); | ||
if ((cp = strstr(p, "NetBSD")) != NULL) | |||
mandoc_msg(MANDOCERR_BX, mdoc->parse, | |||
ln, pos + (cp - p), "Nx"); | |||
if ((cp = strstr(p, "FreeBSD")) != NULL) | |||
mandoc_msg(MANDOCERR_BX, mdoc->parse, | |||
ln, pos + (cp - p), "Fx"); | |||
if ((cp = strstr(p, "DragonFly")) != NULL) | |||
mandoc_msg(MANDOCERR_BX, mdoc->parse, | |||
ln, pos + (cp - p), "Dx"); | |||
cp = p; | |||
while ((cp = strstr(cp + 1, "()")) != NULL) { | |||
for (cpr = cp - 1; cpr >= p; cpr--) | |||
if (*cpr != '_' && !isalnum((unsigned char)*cpr)) | |||
break; | break; | ||
if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { | |||
cpr++; | |||
mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse, | |||
ln, pos + (cpr - p), | |||
"%.*s()", (int)(cp - cpr), cpr); | |||
} | |||
} | |||
} | |||
if (node) | static void | ||
mandoc_vmsg(MANDOCERR_BD_NEST, | post_delim(POST_ARGS) | ||
mdoc->parse, n->line, n->pos, | { | ||
"%s in Bd", mdoc_macronames[n->tok]); | const struct roff_node *nch; | ||
const char *lc, *cp; | |||
int nw; | |||
enum mdelim delim; | |||
enum roff_tok tok; | |||
/* | |||
* Find candidates: at least two bytes, | |||
* the last one a closing or middle delimiter. | |||
*/ | |||
tok = mdoc->last->tok; | |||
nch = mdoc->last->last; | |||
if (nch == NULL || nch->type != ROFFT_TEXT) | |||
return; | |||
lc = strchr(nch->string, '\0') - 1; | |||
if (lc <= nch->string) | |||
return; | |||
delim = mdoc_isdelim(lc); | |||
if (delim == DELIM_NONE || delim == DELIM_OPEN) | |||
return; | |||
/* | |||
* Reduce false positives by allowing various cases. | |||
*/ | |||
/* Escaped delimiters. */ | |||
if (lc > nch->string + 1 && lc[-2] == '\\' && | |||
(lc[-1] == '&' || lc[-1] == 'e')) | |||
return; | |||
/* Specific byte sequences. */ | |||
switch (*lc) { | |||
case ')': | |||
for (cp = lc; cp >= nch->string; cp--) | |||
if (*cp == '(') | |||
return; | |||
break; | |||
case '.': | |||
if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') | |||
return; | |||
if (lc[-1] == '.') | |||
return; | |||
break; | |||
case ';': | |||
if (tok == MDOC_Vt) | |||
return; | |||
break; | |||
case '?': | |||
if (lc[-1] == '?') | |||
return; | |||
break; | |||
case ']': | |||
for (cp = lc; cp >= nch->string; cp--) | |||
if (*cp == '[') | |||
return; | |||
break; | |||
case '|': | |||
if (lc == nch->string + 1 && lc[-1] == '|') | |||
return; | |||
default: | |||
break; | |||
} | |||
/* Exactly two non-alphanumeric bytes. */ | |||
if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) | |||
return; | |||
/* At least three alphabetic words with a sentence ending. */ | |||
if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || | |||
tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq || | |||
tok == MDOC_Sy)) { | |||
nw = 0; | |||
for (cp = lc - 1; cp >= nch->string; cp--) { | |||
if (*cp == ' ') { | |||
nw++; | |||
if (cp > nch->string && cp[-1] == ',') | |||
cp--; | |||
} else if (isalpha((unsigned int)*cp)) { | |||
if (nw > 1) | |||
return; | |||
} else | |||
break; | |||
} | |||
} | |||
mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse, | |||
nch->line, nch->pos + (lc - nch->string), | |||
"%s%s %s", roff_name[tok], | |||
nch == mdoc->last->child ? "" : " ...", nch->string); | |||
} | } | ||
static void | static void | ||
pre_bl(PRE_ARGS) | post_bl_norm(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np; | struct roff_node *n; | ||
struct mdoc_argv *argv, *wa; | struct mdoc_argv *argv, *wa; | ||
int i; | int i; | ||
enum mdocargt mdoclt; | enum mdocargt mdoclt; | ||
enum mdoc_list lt; | enum mdoc_list lt; | ||
if (MDOC_BLOCK != n->type) { | n = mdoc->last->parent; | ||
if (ENDBODY_NOT != n->end) { | n->norm->Bl.type = LIST__NONE; | ||
assert(n->pending); | |||
np = n->pending->parent; | |||
} else | |||
np = n->parent; | |||
assert(np); | |||
assert(MDOC_BLOCK == np->type); | |||
assert(MDOC_Bl == np->tok); | |||
return; | |||
} | |||
/* | /* | ||
* First figure out which kind of list to use: bind ourselves to | * First figure out which kind of list to use: bind ourselves to | ||
* the first mentioned list type and warn about any remaining | * the first mentioned list type and warn about any remaining | ||
|
|
||
mdoc->parse, argv->line, | mdoc->parse, argv->line, | ||
argv->pos, "Bl -width %s", | argv->pos, "Bl -width %s", | ||
argv->value[0]); | argv->value[0]); | ||
rewrite_macro2len(argv->value); | rewrite_macro2len(mdoc, argv->value); | ||
n->norm->Bl.width = argv->value[0]; | n->norm->Bl.width = argv->value[0]; | ||
break; | break; | ||
case MDOC_Offset: | case MDOC_Offset: | ||
|
|
||
mdoc->parse, argv->line, | mdoc->parse, argv->line, | ||
argv->pos, "Bl -offset %s", | argv->pos, "Bl -offset %s", | ||
argv->value[0]); | argv->value[0]); | ||
rewrite_macro2len(argv->value); | rewrite_macro2len(mdoc, argv->value); | ||
n->norm->Bl.offs = argv->value[0]; | n->norm->Bl.offs = argv->value[0]; | ||
break; | break; | ||
default: | default: | ||
|
|
||
mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, | mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, | ||
n->line, n->pos, "Bl"); | n->line, n->pos, "Bl"); | ||
n->norm->Bl.type = LIST_item; | n->norm->Bl.type = LIST_item; | ||
mdoclt = MDOC_Item; | |||
} | } | ||
/* | /* | ||
|
|
||
n->line, n->pos, "Bl -tag"); | n->line, n->pos, "Bl -tag"); | ||
break; | break; | ||
case LIST_column: | case LIST_column: | ||
/* FALLTHROUGH */ | |||
case LIST_diag: | case LIST_diag: | ||
/* FALLTHROUGH */ | |||
case LIST_ohang: | case LIST_ohang: | ||
/* FALLTHROUGH */ | |||
case LIST_inset: | case LIST_inset: | ||
/* FALLTHROUGH */ | |||
case LIST_item: | case LIST_item: | ||
if (n->norm->Bl.width) | if (n->norm->Bl.width) | ||
mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, | mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, | ||
|
|
||
mdoc_argnames[mdoclt]); | mdoc_argnames[mdoclt]); | ||
break; | break; | ||
case LIST_bullet: | case LIST_bullet: | ||
/* FALLTHROUGH */ | |||
case LIST_dash: | case LIST_dash: | ||
/* FALLTHROUGH */ | |||
case LIST_hyphen: | case LIST_hyphen: | ||
if (NULL == n->norm->Bl.width) | if (NULL == n->norm->Bl.width) | ||
n->norm->Bl.width = "2n"; | n->norm->Bl.width = "2n"; | ||
|
|
||
default: | default: | ||
break; | break; | ||
} | } | ||
pre_par(mdoc, n); | |||
} | } | ||
static void | static void | ||
pre_bd(PRE_ARGS) | post_bd(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np; | struct roff_node *n; | ||
struct mdoc_argv *argv; | struct mdoc_argv *argv; | ||
int i; | int i; | ||
enum mdoc_disp dt; | enum mdoc_disp dt; | ||
pre_literal(mdoc, n); | n = mdoc->last; | ||
if (MDOC_BLOCK != n->type) { | |||
if (ENDBODY_NOT != n->end) { | |||
assert(n->pending); | |||
np = n->pending->parent; | |||
} else | |||
np = n->parent; | |||
assert(np); | |||
assert(MDOC_BLOCK == np->type); | |||
assert(MDOC_Bd == np->tok); | |||
return; | |||
} | |||
for (i = 0; n->args && i < (int)n->args->argc; i++) { | for (i = 0; n->args && i < (int)n->args->argc; i++) { | ||
argv = n->args->argv + i; | argv = n->args->argv + i; | ||
dt = DISP__NONE; | dt = DISP__NONE; | ||
|
|
||
mdoc->parse, argv->line, | mdoc->parse, argv->line, | ||
argv->pos, "Bd -offset %s", | argv->pos, "Bd -offset %s", | ||
argv->value[0]); | argv->value[0]); | ||
rewrite_macro2len(argv->value); | rewrite_macro2len(mdoc, argv->value); | ||
n->norm->Bd.offs = argv->value[0]; | n->norm->Bd.offs = argv->value[0]; | ||
break; | break; | ||
case MDOC_Compact: | case MDOC_Compact: | ||
|
|
||
break; | break; | ||
default: | default: | ||
abort(); | abort(); | ||
/* NOTREACHED */ | |||
} | } | ||
if (DISP__NONE == dt) | if (DISP__NONE == dt) | ||
continue; | continue; | ||
|
|
||
n->line, n->pos, "Bd"); | n->line, n->pos, "Bd"); | ||
n->norm->Bd.type = DISP_ragged; | n->norm->Bd.type = DISP_ragged; | ||
} | } | ||
pre_par(mdoc, n); | |||
} | } | ||
/* | |||
* Stand-alone line macros. | |||
*/ | |||
static void | static void | ||
pre_an(PRE_ARGS) | post_an_norm(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
struct mdoc_argv *argv; | struct mdoc_argv *argv; | ||
size_t i; | size_t i; | ||
n = mdoc->last; | |||
if (n->args == NULL) | if (n->args == NULL) | ||
return; | return; | ||
|
|
||
} | } | ||
static void | static void | ||
pre_std(PRE_ARGS) | post_eoln(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
if (n->args && 1 == n->args->argc) | post_useless(mdoc); | ||
if (MDOC_Std == n->args->argv[0].arg) | n = mdoc->last; | ||
return; | if (n->child != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line, | |||
n->pos, "%s %s", roff_name[n->tok], n->child->string); | |||
mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, | while (n->child != NULL) | ||
n->line, n->pos, mdoc_macronames[n->tok]); | roff_node_delete(mdoc, n->child); | ||
roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? | |||
"is currently in beta test." : "currently under development."); | |||
mdoc->last->flags |= NODE_EOS | NODE_NOSRC; | |||
mdoc->last = n; | |||
} | } | ||
static int | |||
build_list(struct roff_man *mdoc, int tok) | |||
{ | |||
struct roff_node *n; | |||
int ic; | |||
n = mdoc->last->next; | |||
for (ic = 1;; ic++) { | |||
roff_elem_alloc(mdoc, n->line, n->pos, tok); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc_node_relink(mdoc, n); | |||
n = mdoc->last = mdoc->last->parent; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
if (n->next == NULL) | |||
return ic; | |||
if (ic > 1 || n->next->next != NULL) { | |||
roff_word_alloc(mdoc, n->line, n->pos, ","); | |||
mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC; | |||
} | |||
n = mdoc->last->next; | |||
if (n->next == NULL) { | |||
roff_word_alloc(mdoc, n->line, n->pos, "and"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
} | |||
} | |||
} | |||
static void | static void | ||
pre_obsolete(PRE_ARGS) | post_ex(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
int ic; | |||
if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type) | post_std(mdoc); | ||
mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, | |||
n->line, n->pos, mdoc_macronames[n->tok]); | n = mdoc->last; | ||
mdoc->next = ROFF_NEXT_CHILD; | |||
roff_word_alloc(mdoc, n->line, n->pos, "The"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
if (mdoc->last->next != NULL) | |||
ic = build_list(mdoc, MDOC_Nm); | |||
else if (mdoc->meta.name != NULL) { | |||
roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last = mdoc->last->parent; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
ic = 1; | |||
} else { | |||
mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, | |||
n->line, n->pos, "Ex"); | |||
ic = 0; | |||
} | |||
roff_word_alloc(mdoc, n->line, n->pos, | |||
ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, | |||
"on success, and\\~>0 if an error occurs."); | |||
mdoc->last->flags |= NODE_EOS | NODE_NOSRC; | |||
mdoc->last = n; | |||
} | } | ||
static void | static void | ||
pre_dt(PRE_ARGS) | post_lb(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
const char *p; | |||
if (mdoc->meta.title != NULL) | post_delim(mdoc); | ||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | |||
n->line, n->pos, "Dt"); | n = mdoc->last; | ||
else if (mdoc->meta.os != NULL) | assert(n->child->type == ROFFT_TEXT); | ||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | mdoc->next = ROFF_NEXT_CHILD; | ||
n->line, n->pos, "Dt after Os"); | |||
if ((p = mdoc_a2lib(n->child->string)) != NULL) { | |||
n->child->flags |= NODE_NOPRT; | |||
roff_word_alloc(mdoc, n->line, n->pos, p); | |||
mdoc->last->flags = NODE_NOSRC; | |||
mdoc->last = n; | |||
return; | |||
} | |||
mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line, | |||
n->child->pos, "Lb %s", n->child->string); | |||
roff_word_alloc(mdoc, n->line, n->pos, "library"); | |||
mdoc->last->flags = NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq"); | |||
mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; | |||
mdoc->last = mdoc->last->next; | |||
roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq"); | |||
mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; | |||
mdoc->last = n; | |||
} | } | ||
static void | static void | ||
pre_os(PRE_ARGS) | post_rv(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
int ic; | |||
if (mdoc->meta.os != NULL) | post_std(mdoc); | ||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | |||
n->line, n->pos, "Os"); | n = mdoc->last; | ||
else if (mdoc->flags & MDOC_PBODY) | mdoc->next = ROFF_NEXT_CHILD; | ||
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, | if (n->child != NULL) { | ||
n->line, n->pos, "Os"); | roff_word_alloc(mdoc, n->line, n->pos, "The"); | ||
mdoc->last->flags |= NODE_NOSRC; | |||
ic = build_list(mdoc, MDOC_Fn); | |||
roff_word_alloc(mdoc, n->line, n->pos, | |||
ic > 1 ? "functions return" : "function returns"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, | |||
"the value\\~0 if successful;"); | |||
} else | |||
roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " | |||
"completion, the value\\~0 is returned;"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, "otherwise " | |||
"the value\\~\\-1 is returned and the global variable"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, n->line, n->pos, "errno"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last = mdoc->last->parent; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
roff_word_alloc(mdoc, n->line, n->pos, | |||
"is set to indicate the error."); | |||
mdoc->last->flags |= NODE_EOS | NODE_NOSRC; | |||
mdoc->last = n; | |||
} | } | ||
static void | static void | ||
pre_dd(PRE_ARGS) | post_std(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
if (mdoc->meta.date != NULL) | n = mdoc->last; | ||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | if (n->args && n->args->argc == 1) | ||
n->line, n->pos, "Dd"); | if (n->args->argv[0].arg == MDOC_Std) | ||
else if (mdoc->flags & MDOC_PBODY) | return; | ||
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, | |||
n->line, n->pos, "Dd"); | mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, | ||
else if (mdoc->meta.title != NULL) | n->line, n->pos, roff_name[n->tok]); | ||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | |||
n->line, n->pos, "Dd after Dt"); | |||
else if (mdoc->meta.os != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | |||
n->line, n->pos, "Dd after Os"); | |||
} | } | ||
static void | static void | ||
post_st(POST_ARGS) | |||
{ | |||
struct roff_node *n, *nch; | |||
const char *p; | |||
n = mdoc->last; | |||
nch = n->child; | |||
assert(nch->type == ROFFT_TEXT); | |||
if ((p = mdoc_a2st(nch->string)) == NULL) { | |||
mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, | |||
nch->line, nch->pos, "St %s", nch->string); | |||
roff_node_delete(mdoc, n); | |||
return; | |||
} | |||
nch->flags |= NODE_NOPRT; | |||
mdoc->next = ROFF_NEXT_CHILD; | |||
roff_word_alloc(mdoc, nch->line, nch->pos, p); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last= n; | |||
} | |||
static void | |||
post_obsolete(POST_ARGS) | |||
{ | |||
struct roff_node *n; | |||
n = mdoc->last; | |||
if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) | |||
mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, | |||
n->line, n->pos, roff_name[n->tok]); | |||
} | |||
static void | |||
post_useless(POST_ARGS) | |||
{ | |||
struct roff_node *n; | |||
n = mdoc->last; | |||
mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse, | |||
n->line, n->pos, roff_name[n->tok]); | |||
} | |||
/* | |||
* Block macros. | |||
*/ | |||
static void | |||
post_bf(POST_ARGS) | post_bf(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np, *nch; | struct roff_node *np, *nch; | ||
enum mdocargt arg; | |||
/* | /* | ||
* Unlike other data pointers, these are "housed" by the HEAD | * Unlike other data pointers, these are "housed" by the HEAD | ||
* element, which contains the goods. | * element, which contains the goods. | ||
*/ | */ | ||
if (MDOC_HEAD != mdoc->last->type) { | np = mdoc->last; | ||
if (ENDBODY_NOT != mdoc->last->end) { | if (np->type != ROFFT_HEAD) | ||
assert(mdoc->last->pending); | |||
np = mdoc->last->pending->parent->head; | |||
} else if (MDOC_BLOCK != mdoc->last->type) { | |||
np = mdoc->last->parent->head; | |||
} else | |||
np = mdoc->last->head; | |||
assert(np); | |||
assert(MDOC_HEAD == np->type); | |||
assert(MDOC_Bf == np->tok); | |||
return; | return; | ||
} | |||
np = mdoc->last; | assert(np->parent->type == ROFFT_BLOCK); | ||
assert(MDOC_BLOCK == np->parent->type); | assert(np->parent->tok == MDOC_Bf); | ||
assert(MDOC_Bf == np->parent->tok); | |||
/* Check the number of arguments. */ | /* Check the number of arguments. */ | ||
nch = np->child; | nch = np->child; | ||
if (NULL == np->parent->args) { | if (np->parent->args == NULL) { | ||
if (NULL == nch) { | if (nch == NULL) { | ||
mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, | mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, | ||
np->line, np->pos, "Bf"); | np->line, np->pos, "Bf"); | ||
return; | return; | ||
} | } | ||
nch = nch->next; | nch = nch->next; | ||
} | } | ||
if (NULL != nch) | if (nch != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | ||
nch->line, nch->pos, "Bf ... %s", nch->string); | nch->line, nch->pos, "Bf ... %s", nch->string); | ||
/* Extract argument into data. */ | /* Extract argument into data. */ | ||
if (np->parent->args) { | if (np->parent->args != NULL) { | ||
arg = np->parent->args->argv[0].arg; | switch (np->parent->args->argv[0].arg) { | ||
if (MDOC_Emphasis == arg) | case MDOC_Emphasis: | ||
np->norm->Bf.font = FONT_Em; | np->norm->Bf.font = FONT_Em; | ||
else if (MDOC_Literal == arg) | break; | ||
case MDOC_Literal: | |||
np->norm->Bf.font = FONT_Li; | np->norm->Bf.font = FONT_Li; | ||
else if (MDOC_Symbolic == arg) | break; | ||
case MDOC_Symbolic: | |||
np->norm->Bf.font = FONT_Sy; | np->norm->Bf.font = FONT_Sy; | ||
else | break; | ||
default: | |||
abort(); | abort(); | ||
} | |||
return; | return; | ||
} | } | ||
/* Extract parameter into data. */ | /* Extract parameter into data. */ | ||
if (0 == strcmp(np->child->string, "Em")) | if ( ! strcmp(np->child->string, "Em")) | ||
np->norm->Bf.font = FONT_Em; | np->norm->Bf.font = FONT_Em; | ||
else if (0 == strcmp(np->child->string, "Li")) | else if ( ! strcmp(np->child->string, "Li")) | ||
np->norm->Bf.font = FONT_Li; | np->norm->Bf.font = FONT_Li; | ||
else if (0 == strcmp(np->child->string, "Sy")) | else if ( ! strcmp(np->child->string, "Sy")) | ||
np->norm->Bf.font = FONT_Sy; | np->norm->Bf.font = FONT_Sy; | ||
else | else | ||
mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, | mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, | ||
|
|
||
} | } | ||
static void | static void | ||
post_lb(POST_ARGS) | |||
{ | |||
struct mdoc_node *n; | |||
const char *stdlibname; | |||
char *libname; | |||
check_count(mdoc, MDOC_ELEM, CHECK_EQ, 1); | |||
n = mdoc->last->child; | |||
assert(MDOC_TEXT == n->type); | |||
if (NULL == (stdlibname = mdoc_a2lib(n->string))) | |||
mandoc_asprintf(&libname, | |||
"library \\(lq%s\\(rq", n->string); | |||
else | |||
libname = mandoc_strdup(stdlibname); | |||
free(n->string); | |||
n->string = libname; | |||
} | |||
static void | |||
post_eoln(POST_ARGS) | |||
{ | |||
const struct mdoc_node *n; | |||
n = mdoc->last; | |||
if (n->child) | |||
mandoc_vmsg(MANDOCERR_ARG_SKIP, | |||
mdoc->parse, n->line, n->pos, | |||
"%s %s", mdoc_macronames[n->tok], | |||
n->child->string); | |||
} | |||
static void | |||
post_fname(POST_ARGS) | post_fname(POST_ARGS) | ||
{ | { | ||
const struct mdoc_node *n; | const struct roff_node *n; | ||
const char *cp; | const char *cp; | ||
size_t pos; | size_t pos; | ||
|
|
||
static void | static void | ||
post_fo(POST_ARGS) | post_fo(POST_ARGS) | ||
{ | { | ||
const struct roff_node *n; | |||
check_count(mdoc, MDOC_HEAD, CHECK_EQ, 1); | n = mdoc->last; | ||
bwarn_ge1(mdoc); | |||
if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild) | if (n->type != ROFFT_HEAD) | ||
post_fname(mdoc); | return; | ||
if (n->child == NULL) { | |||
mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse, | |||
n->line, n->pos, "Fo"); | |||
return; | |||
} | |||
if (n->child != n->last) { | |||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | |||
n->child->next->line, n->child->next->pos, | |||
"Fo ... %s", n->child->next->string); | |||
while (n->child != n->last) | |||
roff_node_delete(mdoc, n->last); | |||
} | |||
post_fname(mdoc); | |||
} | } | ||
static void | static void | ||
post_fa(POST_ARGS) | post_fa(POST_ARGS) | ||
{ | { | ||
const struct mdoc_node *n; | const struct roff_node *n; | ||
const char *cp; | const char *cp; | ||
for (n = mdoc->last->child; n != NULL; n = n->next) { | for (n = mdoc->last->child; n != NULL; n = n->next) { | ||
|
|
||
break; | break; | ||
} | } | ||
} | } | ||
post_delim(mdoc); | |||
} | } | ||
static void | static void | ||
post_vt(POST_ARGS) | |||
{ | |||
const struct mdoc_node *n; | |||
/* | |||
* The Vt macro comes in both ELEM and BLOCK form, both of which | |||
* have different syntaxes (yet more context-sensitive | |||
* behaviour). ELEM types must have a child, which is already | |||
* guaranteed by the in_line parsing routine; BLOCK types, | |||
* specifically the BODY, should only have TEXT children. | |||
*/ | |||
if (MDOC_BODY != mdoc->last->type) | |||
return; | |||
for (n = mdoc->last->child; n; n = n->next) | |||
if (MDOC_TEXT != n->type) | |||
mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse, | |||
n->line, n->pos, mdoc_macronames[n->tok]); | |||
} | |||
static void | |||
post_nm(POST_ARGS) | post_nm(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
n = mdoc->last; | n = mdoc->last; | ||
|
|
||
n->last->tok == MDOC_Lp)) | n->last->tok == MDOC_Lp)) | ||
mdoc_node_relink(mdoc, n->last); | mdoc_node_relink(mdoc, n->last); | ||
if (NULL != mdoc->meta.name) | if (mdoc->meta.name == NULL) | ||
return; | deroff(&mdoc->meta.name, n); | ||
mdoc_deroff(&mdoc->meta.name, n); | if (mdoc->meta.name == NULL || | ||
(mdoc->lastsec == SEC_NAME && n->child == NULL)) | |||
if (NULL == mdoc->meta.name) | |||
mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, | mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, | ||
n->line, n->pos, "Nm"); | n->line, n->pos, "Nm"); | ||
} | |||
static void | if (n->type == ROFFT_ELEM) | ||
post_nd(POST_ARGS) | post_delim(mdoc); | ||
{ | |||
struct mdoc_node *n; | |||
n = mdoc->last; | if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) || | ||
(n->child != NULL && n->child->type == ROFFT_TEXT) || | |||
if (n->type != MDOC_BODY) | mdoc->meta.name == NULL) | ||
return; | return; | ||
if (n->child == NULL) | mdoc->next = ROFF_NEXT_CHILD; | ||
mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, | roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | ||
n->line, n->pos, "Nd"); | mdoc->last->flags |= NODE_NOSRC; | ||
mdoc->last = n; | |||
post_hyph(mdoc); | |||
} | } | ||
static void | static void | ||
post_d1(POST_ARGS) | post_nd(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
size_t sz; | |||
n = mdoc->last; | n = mdoc->last; | ||
if (n->type != MDOC_BODY) | if (n->type != ROFFT_BODY) | ||
return; | return; | ||
if (n->sec != SEC_NAME) | |||
mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse, | |||
n->line, n->pos, "Nd"); | |||
if (n->child == NULL) | if (n->child == NULL) | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, | ||
n->line, n->pos, "D1"); | n->line, n->pos, "Nd"); | ||
else if (n->last->type == ROFFT_TEXT && | |||
(sz = strlen(n->last->string)) != 0 && | |||
n->last->string[sz - 1] == '.') | |||
mandoc_msg(MANDOCERR_ND_DOT, mdoc->parse, | |||
n->last->line, n->last->pos + sz - 1, NULL); | |||
post_hyph(mdoc); | post_hyph(mdoc); | ||
} | } | ||
static void | static void | ||
post_literal(POST_ARGS) | post_display(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n, *np; | ||
n = mdoc->last; | n = mdoc->last; | ||
switch (n->type) { | |||
if (n->type != MDOC_BODY) | case ROFFT_BODY: | ||
return; | if (n->end != ENDBODY_NOT) { | ||
if (n->tok == MDOC_Bd && | |||
if (n->child == NULL) | n->body->parent->args == NULL) | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | roff_node_delete(mdoc, n); | ||
n->line, n->pos, mdoc_macronames[n->tok]); | } else if (n->child == NULL) | ||
mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, | |||
if (n->tok == MDOC_Bd && | n->line, n->pos, roff_name[n->tok]); | ||
n->norm->Bd.type != DISP_literal && | else if (n->tok == MDOC_D1) | ||
n->norm->Bd.type != DISP_unfilled) | post_hyph(mdoc); | ||
return; | break; | ||
case ROFFT_BLOCK: | |||
mdoc->flags &= ~MDOC_LITERAL; | if (n->tok == MDOC_Bd) { | ||
if (n->args == NULL) { | |||
mandoc_msg(MANDOCERR_BD_NOARG, | |||
mdoc->parse, n->line, n->pos, "Bd"); | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
while (n->body->child != NULL) | |||
mdoc_node_relink(mdoc, | |||
n->body->child); | |||
roff_node_delete(mdoc, n); | |||
break; | |||
} | |||
post_bd(mdoc); | |||
post_prevpar(mdoc); | |||
} | |||
for (np = n->parent; np != NULL; np = np->parent) { | |||
if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { | |||
mandoc_vmsg(MANDOCERR_BD_NEST, | |||
mdoc->parse, n->line, n->pos, | |||
"%s in Bd", roff_name[n->tok]); | |||
break; | |||
} | |||
} | |||
break; | |||
default: | |||
break; | |||
} | |||
} | } | ||
static void | static void | ||
post_defaults(POST_ARGS) | post_defaults(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nn; | struct roff_node *nn; | ||
if (mdoc->last->child != NULL) { | |||
post_delim(mdoc); | |||
return; | |||
} | |||
/* | /* | ||
* The `Ar' defaults to "file ..." if no value is provided as an | * The `Ar' defaults to "file ..." if no value is provided as an | ||
* argument; the `Mt' and `Pa' macros use "~"; the `Li' just | * argument; the `Mt' and `Pa' macros use "~"; the `Li' just | ||
* gets an empty string. | * gets an empty string. | ||
*/ | */ | ||
if (mdoc->last->child) | |||
return; | |||
nn = mdoc->last; | nn = mdoc->last; | ||
mdoc->next = MDOC_NEXT_CHILD; | |||
switch (nn->tok) { | switch (nn->tok) { | ||
case MDOC_Ar: | case MDOC_Ar: | ||
mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"); | mdoc->next = ROFF_NEXT_CHILD; | ||
mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."); | roff_word_alloc(mdoc, nn->line, nn->pos, "file"); | ||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_word_alloc(mdoc, nn->line, nn->pos, "..."); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
break; | break; | ||
case MDOC_Pa: | case MDOC_Pa: | ||
/* FALLTHROUGH */ | |||
case MDOC_Mt: | case MDOC_Mt: | ||
mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"); | mdoc->next = ROFF_NEXT_CHILD; | ||
roff_word_alloc(mdoc, nn->line, nn->pos, "~"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
break; | break; | ||
default: | default: | ||
abort(); | abort(); | ||
/* NOTREACHED */ | |||
} | } | ||
mdoc->last = nn; | mdoc->last = nn; | ||
} | } | ||
|
|
||
static void | static void | ||
post_at(POST_ARGS) | post_at(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n, *nch; | ||
const char *std_att; | const char *att; | ||
char *att; | |||
n = mdoc->last; | n = mdoc->last; | ||
if (n->child == NULL) { | nch = n->child; | ||
mdoc->next = MDOC_NEXT_CHILD; | |||
mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); | |||
mdoc->last = n; | |||
return; | |||
} | |||
/* | /* | ||
* If we have a child, look it up in the standard keys. If a | * If we have a child, look it up in the standard keys. If a | ||
|
|
||
* prefix "AT&T UNIX " to the existing data. | * prefix "AT&T UNIX " to the existing data. | ||
*/ | */ | ||
n = n->child; | att = NULL; | ||
assert(MDOC_TEXT == n->type); | if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) | ||
if (NULL == (std_att = mdoc_a2att(n->string))) { | |||
mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, | mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, | ||
n->line, n->pos, "At %s", n->string); | nch->line, nch->pos, "At %s", nch->string); | ||
mandoc_asprintf(&att, "AT&T UNIX %s", n->string); | |||
} else | |||
att = mandoc_strdup(std_att); | |||
free(n->string); | mdoc->next = ROFF_NEXT_CHILD; | ||
n->string = att; | if (att != NULL) { | ||
roff_word_alloc(mdoc, nch->line, nch->pos, att); | |||
nch->flags |= NODE_NOPRT; | |||
} else | |||
roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last = n; | |||
} | } | ||
static void | static void | ||
post_an(POST_ARGS) | post_an(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np, *nch; | struct roff_node *np, *nch; | ||
post_an_norm(mdoc); | |||
np = mdoc->last; | np = mdoc->last; | ||
nch = np->child; | nch = np->child; | ||
if (np->norm->An.auth == AUTH__NONE) { | if (np->norm->An.auth == AUTH__NONE) { | ||
if (nch == NULL) | if (nch == NULL) | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | ||
np->line, np->pos, "An"); | np->line, np->pos, "An"); | ||
else | |||
post_delim(mdoc); | |||
} else if (nch != NULL) | } else if (nch != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | ||
nch->line, nch->pos, "An ... %s", nch->string); | nch->line, nch->pos, "An ... %s", nch->string); | ||
|
|
||
post_en(POST_ARGS) | post_en(POST_ARGS) | ||
{ | { | ||
if (MDOC_BLOCK == mdoc->last->type) | post_obsolete(mdoc); | ||
if (mdoc->last->type == ROFFT_BLOCK) | |||
mdoc->last->norm->Es = mdoc->last_es; | mdoc->last->norm->Es = mdoc->last_es; | ||
} | } | ||
|
|
||
post_es(POST_ARGS) | post_es(POST_ARGS) | ||
{ | { | ||
post_obsolete(mdoc); | |||
mdoc->last_es = mdoc->last; | mdoc->last_es = mdoc->last; | ||
} | } | ||
static void | static void | ||
post_xx(POST_ARGS) | |||
{ | |||
struct roff_node *n; | |||
const char *os; | |||
post_delim(mdoc); | |||
n = mdoc->last; | |||
switch (n->tok) { | |||
case MDOC_Bsx: | |||
os = "BSD/OS"; | |||
break; | |||
case MDOC_Dx: | |||
os = "DragonFly"; | |||
break; | |||
case MDOC_Fx: | |||
os = "FreeBSD"; | |||
break; | |||
case MDOC_Nx: | |||
os = "NetBSD"; | |||
break; | |||
case MDOC_Ox: | |||
os = "OpenBSD"; | |||
break; | |||
case MDOC_Ux: | |||
os = "UNIX"; | |||
break; | |||
default: | |||
abort(); | |||
} | |||
mdoc->next = ROFF_NEXT_CHILD; | |||
roff_word_alloc(mdoc, n->line, n->pos, os); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last = n; | |||
} | |||
static void | |||
post_it(POST_ARGS) | post_it(POST_ARGS) | ||
{ | { | ||
struct roff_node *nbl, *nit, *nch; | |||
int i, cols; | int i, cols; | ||
enum mdoc_list lt; | enum mdoc_list lt; | ||
struct mdoc_node *nbl, *nit, *nch; | |||
post_prevpar(mdoc); | |||
nit = mdoc->last; | nit = mdoc->last; | ||
if (nit->type != MDOC_BLOCK) | if (nit->type != ROFFT_BLOCK) | ||
return; | return; | ||
nbl = nit->parent->parent; | nbl = nit->parent->parent; | ||
|
|
||
switch (lt) { | switch (lt) { | ||
case LIST_tag: | case LIST_tag: | ||
/* FALLTHROUGH */ | |||
case LIST_hang: | case LIST_hang: | ||
/* FALLTHROUGH */ | |||
case LIST_ohang: | case LIST_ohang: | ||
/* FALLTHROUGH */ | |||
case LIST_inset: | case LIST_inset: | ||
/* FALLTHROUGH */ | |||
case LIST_diag: | case LIST_diag: | ||
if (nit->head->child == NULL) | if (nit->head->child == NULL) | ||
mandoc_vmsg(MANDOCERR_IT_NOHEAD, | mandoc_vmsg(MANDOCERR_IT_NOHEAD, | ||
|
|
||
mdoc_argnames[nbl->args->argv[0].arg]); | mdoc_argnames[nbl->args->argv[0].arg]); | ||
break; | break; | ||
case LIST_bullet: | case LIST_bullet: | ||
/* FALLTHROUGH */ | |||
case LIST_dash: | case LIST_dash: | ||
/* FALLTHROUGH */ | |||
case LIST_enum: | case LIST_enum: | ||
/* FALLTHROUGH */ | |||
case LIST_hyphen: | case LIST_hyphen: | ||
if (nit->body == NULL || nit->body->child == NULL) | if (nit->body == NULL || nit->body->child == NULL) | ||
mandoc_vmsg(MANDOCERR_IT_NOBODY, | mandoc_vmsg(MANDOCERR_IT_NOBODY, | ||
|
|
||
mdoc_argnames[nbl->args->argv[0].arg]); | mdoc_argnames[nbl->args->argv[0].arg]); | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case LIST_item: | case LIST_item: | ||
if (nit->head->child != NULL) | if ((nch = nit->head->child) != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_SKIP, | mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, | ||
mdoc->parse, nit->line, nit->pos, | nit->line, nit->pos, "It %s", | ||
"It %s", nit->head->child->string); | nch->string == NULL ? roff_name[nch->tok] : | ||
nch->string); | |||
break; | break; | ||
case LIST_column: | case LIST_column: | ||
cols = (int)nbl->norm->Bl.ncols; | cols = (int)nbl->norm->Bl.ncols; | ||
assert(nit->head->child == NULL); | assert(nit->head->child == NULL); | ||
for (i = 0, nch = nit->child; nch; nch = nch->next) | i = 0; | ||
if (nch->type == MDOC_BODY) | for (nch = nit->child; nch != NULL; nch = nch->next) | ||
if (nch->type == ROFFT_BODY) | |||
i++; | i++; | ||
if (i < cols || i > cols + 1) | if (i < cols || i > cols + 1) | ||
mandoc_vmsg(MANDOCERR_ARGCOUNT, | mandoc_vmsg(MANDOCERR_BL_COL, | ||
mdoc->parse, nit->line, nit->pos, | mdoc->parse, nit->line, nit->pos, | ||
"columns == %d (have %d)", cols, i); | "%d columns, %d cells", cols, i); | ||
break; | break; | ||
default: | default: | ||
abort(); | abort(); | ||
|
|
||
static void | static void | ||
post_bl_block(POST_ARGS) | post_bl_block(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n, *ni, *nc; | struct roff_node *n, *ni, *nc; | ||
/* | post_prevpar(mdoc); | ||
* These are fairly complicated, so we've broken them into two | |||
* functions. post_bl_block_tag() is called when a -tag is | |||
* specified, but no -width (it must be guessed). The second | |||
* when a -width is specified (macro indicators must be | |||
* rewritten into real lengths). | |||
*/ | |||
n = mdoc->last; | n = mdoc->last; | ||
for (ni = n->body->child; ni != NULL; ni = ni->next) { | |||
if (LIST_tag == n->norm->Bl.type && | if (ni->body == NULL) | ||
NULL == n->norm->Bl.width) { | |||
post_bl_block_tag(mdoc); | |||
assert(n->norm->Bl.width); | |||
} | |||
for (ni = n->body->child; ni; ni = ni->next) { | |||
if (NULL == ni->body) | |||
continue; | continue; | ||
nc = ni->body->last; | nc = ni->body->last; | ||
while (NULL != nc) { | while (nc != NULL) { | ||
switch (nc->tok) { | switch (nc->tok) { | ||
case MDOC_Pp: | case MDOC_Pp: | ||
/* FALLTHROUGH */ | |||
case MDOC_Lp: | case MDOC_Lp: | ||
/* FALLTHROUGH */ | case ROFF_br: | ||
case MDOC_br: | |||
break; | break; | ||
default: | default: | ||
nc = NULL; | nc = NULL; | ||
continue; | continue; | ||
} | } | ||
if (NULL == ni->next) { | if (ni->next == NULL) { | ||
mandoc_msg(MANDOCERR_PAR_MOVE, | mandoc_msg(MANDOCERR_PAR_MOVE, | ||
mdoc->parse, nc->line, nc->pos, | mdoc->parse, nc->line, nc->pos, | ||
mdoc_macronames[nc->tok]); | roff_name[nc->tok]); | ||
mdoc_node_relink(mdoc, nc); | mdoc_node_relink(mdoc, nc); | ||
} else if (0 == n->norm->Bl.comp && | } else if (n->norm->Bl.comp == 0 && | ||
LIST_column != n->norm->Bl.type) { | n->norm->Bl.type != LIST_column) { | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, | mandoc_vmsg(MANDOCERR_PAR_SKIP, | ||
mdoc->parse, nc->line, nc->pos, | mdoc->parse, nc->line, nc->pos, | ||
"%s before It", | "%s before It", roff_name[nc->tok]); | ||
mdoc_macronames[nc->tok]); | roff_node_delete(mdoc, nc); | ||
mdoc_node_delete(mdoc, nc); | |||
} else | } else | ||
break; | break; | ||
nc = ni->body->last; | nc = ni->body->last; | ||
|
|
||
* If the argument of -offset or -width is a macro, | * If the argument of -offset or -width is a macro, | ||
* replace it with the associated default width. | * replace it with the associated default width. | ||
*/ | */ | ||
void | static void | ||
rewrite_macro2len(char **arg) | rewrite_macro2len(struct roff_man *mdoc, char **arg) | ||
{ | { | ||
size_t width; | size_t width; | ||
enum mdoct tok; | enum roff_tok tok; | ||
if (*arg == NULL) | if (*arg == NULL) | ||
return; | return; | ||
else if ( ! strcmp(*arg, "Ds")) | else if ( ! strcmp(*arg, "Ds")) | ||
width = 6; | width = 6; | ||
else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX) | else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) | ||
return; | return; | ||
else | else | ||
width = macro2len(tok); | width = macro2len(tok); | ||
|
|
||
} | } | ||
static void | static void | ||
post_bl_block_tag(POST_ARGS) | |||
{ | |||
struct mdoc_node *n, *nn; | |||
size_t sz, ssz; | |||
int i; | |||
char buf[24]; | |||
/* | |||
* Calculate the -width for a `Bl -tag' list if it hasn't been | |||
* provided. Uses the first head macro. NOTE AGAIN: this is | |||
* ONLY if the -width argument has NOT been provided. See | |||
* rewrite_macro2len() for converting the -width string. | |||
*/ | |||
sz = 10; | |||
n = mdoc->last; | |||
for (nn = n->body->child; nn; nn = nn->next) { | |||
if (MDOC_It != nn->tok) | |||
continue; | |||
assert(MDOC_BLOCK == nn->type); | |||
nn = nn->head->child; | |||
if (nn == NULL) | |||
break; | |||
if (MDOC_TEXT == nn->type) { | |||
sz = strlen(nn->string) + 1; | |||
break; | |||
} | |||
if (0 != (ssz = macro2len(nn->tok))) | |||
sz = ssz; | |||
break; | |||
} | |||
/* Defaults to ten ens. */ | |||
(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz); | |||
/* | |||
* We have to dynamically add this to the macro's argument list. | |||
* We're guaranteed that a MDOC_Width doesn't already exist. | |||
*/ | |||
assert(n->args); | |||
i = (int)(n->args->argc)++; | |||
n->args->argv = mandoc_reallocarray(n->args->argv, | |||
n->args->argc, sizeof(struct mdoc_argv)); | |||
n->args->argv[i].arg = MDOC_Width; | |||
n->args->argv[i].line = n->line; | |||
n->args->argv[i].pos = n->pos; | |||
n->args->argv[i].sz = 1; | |||
n->args->argv[i].value = mandoc_malloc(sizeof(char *)); | |||
n->args->argv[i].value[0] = mandoc_strdup(buf); | |||
/* Set our width! */ | |||
n->norm->Bl.width = n->args->argv[i].value[0]; | |||
} | |||
static void | |||
post_bl_head(POST_ARGS) | post_bl_head(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nbl, *nh, *nch, *nnext; | struct roff_node *nbl, *nh, *nch, *nnext; | ||
struct mdoc_argv *argv; | struct mdoc_argv *argv; | ||
int i, j; | int i, j; | ||
nh = mdoc->last; | post_bl_norm(mdoc); | ||
nh = mdoc->last; | |||
if (nh->norm->Bl.type != LIST_column) { | if (nh->norm->Bl.type != LIST_column) { | ||
if ((nch = nh->child) == NULL) | if ((nch = nh->child) == NULL) | ||
return; | return; | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | ||
nch->line, nch->pos, "Bl ... %s", nch->string); | nch->line, nch->pos, "Bl ... %s", nch->string); | ||
while (nch != NULL) { | while (nch != NULL) { | ||
mdoc_node_delete(mdoc, nch); | roff_node_delete(mdoc, nch); | ||
nch = nh->child; | nch = nh->child; | ||
} | } | ||
return; | return; | ||
|
|
||
argv = nbl->args->argv + j; | argv = nbl->args->argv + j; | ||
i = argv->sz; | i = argv->sz; | ||
argv->sz += nh->nchild; | for (nch = nh->child; nch != NULL; nch = nch->next) | ||
argv->sz++; | |||
argv->value = mandoc_reallocarray(argv->value, | argv->value = mandoc_reallocarray(argv->value, | ||
argv->sz, sizeof(char *)); | argv->sz, sizeof(char *)); | ||
|
|
||
argv->value[i++] = nch->string; | argv->value[i++] = nch->string; | ||
nch->string = NULL; | nch->string = NULL; | ||
nnext = nch->next; | nnext = nch->next; | ||
mdoc_node_delete(NULL, nch); | roff_node_delete(NULL, nch); | ||
} | } | ||
nh->nchild = 0; | |||
nh->child = NULL; | nh->child = NULL; | ||
} | } | ||
static void | static void | ||
post_bl(POST_ARGS) | post_bl(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nparent, *nprev; /* of the Bl block */ | struct roff_node *nparent, *nprev; /* of the Bl block */ | ||
struct mdoc_node *nblock, *nbody; /* of the Bl */ | struct roff_node *nblock, *nbody; /* of the Bl */ | ||
struct mdoc_node *nchild, *nnext; /* of the Bl body */ | struct roff_node *nchild, *nnext; /* of the Bl body */ | ||
const char *prev_Er; | |||
int order; | |||
nbody = mdoc->last; | nbody = mdoc->last; | ||
switch (nbody->type) { | switch (nbody->type) { | ||
case MDOC_BLOCK: | case ROFFT_BLOCK: | ||
post_bl_block(mdoc); | post_bl_block(mdoc); | ||
return; | return; | ||
case MDOC_HEAD: | case ROFFT_HEAD: | ||
post_bl_head(mdoc); | post_bl_head(mdoc); | ||
return; | return; | ||
case MDOC_BODY: | case ROFFT_BODY: | ||
break; | break; | ||
default: | default: | ||
return; | return; | ||
} | } | ||
if (nbody->end != ENDBODY_NOT) | |||
return; | |||
nchild = nbody->child; | nchild = nbody->child; | ||
if (nchild == NULL) { | if (nchild == NULL) { | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, | mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, | ||
nbody->line, nbody->pos, "Bl"); | nbody->line, nbody->pos, "Bl"); | ||
return; | return; | ||
} | } | ||
while (nchild != NULL) { | while (nchild != NULL) { | ||
nnext = nchild->next; | |||
if (nchild->tok == MDOC_It || | if (nchild->tok == MDOC_It || | ||
(nchild->tok == MDOC_Sm && | (nchild->tok == MDOC_Sm && | ||
nchild->next != NULL && | nnext != NULL && nnext->tok == MDOC_It)) { | ||
nchild->next->tok == MDOC_It)) { | nchild = nnext; | ||
nchild = nchild->next; | |||
continue; | continue; | ||
} | } | ||
/* | |||
* In .Bl -column, the first rows may be implicit, | |||
* that is, they may not start with .It macros. | |||
* Such rows may be followed by nodes generated on the | |||
* roff level, for example .TS, which cannot be moved | |||
* out of the list. In that case, wrap such roff nodes | |||
* into an implicit row. | |||
*/ | |||
if (nchild->prev != NULL) { | |||
mdoc->last = nchild; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
roff_block_alloc(mdoc, nchild->line, | |||
nchild->pos, MDOC_It); | |||
roff_head_alloc(mdoc, nchild->line, | |||
nchild->pos, MDOC_It); | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
roff_body_alloc(mdoc, nchild->line, | |||
nchild->pos, MDOC_It); | |||
while (nchild->tok != MDOC_It) { | |||
mdoc_node_relink(mdoc, nchild); | |||
if ((nchild = nnext) == NULL) | |||
break; | |||
nnext = nchild->next; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
} | |||
mdoc->last = nbody; | |||
continue; | |||
} | |||
mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, | mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, | ||
nchild->line, nchild->pos, | nchild->line, nchild->pos, roff_name[nchild->tok]); | ||
mdoc_macronames[nchild->tok]); | |||
/* | /* | ||
* Move the node out of the Bl block. | * Move the node out of the Bl block. | ||
|
|
||
nblock = nbody->parent; | nblock = nbody->parent; | ||
nprev = nblock->prev; | nprev = nblock->prev; | ||
nparent = nblock->parent; | nparent = nblock->parent; | ||
nnext = nchild->next; | |||
/* | /* | ||
* Unlink this child. | * Unlink this child. | ||
*/ | */ | ||
assert(NULL == nchild->prev); | nbody->child = nnext; | ||
if (0 == --nbody->nchild) { | if (nnext == NULL) | ||
nbody->child = NULL; | |||
nbody->last = NULL; | nbody->last = NULL; | ||
assert(NULL == nnext); | else | ||
} else { | |||
nbody->child = nnext; | |||
nnext->prev = NULL; | nnext->prev = NULL; | ||
} | |||
/* | /* | ||
* Relink this child. | * Relink this child. | ||
|
|
||
nchild->next = nblock; | nchild->next = nblock; | ||
nblock->prev = nchild; | nblock->prev = nchild; | ||
nparent->nchild++; | if (nprev == NULL) | ||
if (NULL == nprev) | |||
nparent->child = nchild; | nparent->child = nchild; | ||
else | else | ||
nprev->next = nchild; | nprev->next = nchild; | ||
nchild = nnext; | nchild = nnext; | ||
} | } | ||
if (mdoc->meta.os_e != MDOC_OS_NETBSD) | |||
return; | |||
prev_Er = NULL; | |||
for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { | |||
if (nchild->tok != MDOC_It) | |||
continue; | |||
if ((nnext = nchild->head->child) == NULL) | |||
continue; | |||
if (nnext->type == ROFFT_BLOCK) | |||
nnext = nnext->body->child; | |||
if (nnext == NULL || nnext->tok != MDOC_Er) | |||
continue; | |||
nnext = nnext->child; | |||
if (prev_Er != NULL) { | |||
order = strcmp(prev_Er, nnext->string); | |||
if (order > 0) | |||
mandoc_vmsg(MANDOCERR_ER_ORDER, | |||
mdoc->parse, nnext->line, nnext->pos, | |||
"Er %s %s", prev_Er, nnext->string); | |||
else if (order == 0) | |||
mandoc_vmsg(MANDOCERR_ER_REP, | |||
mdoc->parse, nnext->line, nnext->pos, | |||
"Er %s", prev_Er); | |||
} | |||
prev_Er = nnext->string; | |||
} | |||
} | } | ||
static void | static void | ||
post_bk(POST_ARGS) | post_bk(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
n = mdoc->last; | n = mdoc->last; | ||
if (n->type == MDOC_BLOCK && n->body->child == NULL) { | if (n->type == ROFFT_BLOCK && n->body->child == NULL) { | ||
mandoc_msg(MANDOCERR_MACRO_EMPTY, | mandoc_msg(MANDOCERR_BLK_EMPTY, | ||
mdoc->parse, n->line, n->pos, "Bk"); | mdoc->parse, n->line, n->pos, "Bk"); | ||
mdoc_node_delete(mdoc, n); | roff_node_delete(mdoc, n); | ||
} | } | ||
} | } | ||
static void | static void | ||
post_sm(struct mdoc *mdoc) | post_sm(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nch; | struct roff_node *nch; | ||
nch = mdoc->last->child; | nch = mdoc->last->child; | ||
|
|
||
return; | return; | ||
} | } | ||
assert(nch->type == MDOC_TEXT); | assert(nch->type == ROFFT_TEXT); | ||
if ( ! strcmp(nch->string, "on")) { | if ( ! strcmp(nch->string, "on")) { | ||
mdoc->flags &= ~MDOC_SMOFF; | mdoc->flags &= ~MDOC_SMOFF; | ||
|
|
||
mandoc_vmsg(MANDOCERR_SM_BAD, | mandoc_vmsg(MANDOCERR_SM_BAD, | ||
mdoc->parse, nch->line, nch->pos, | mdoc->parse, nch->line, nch->pos, | ||
"%s %s", mdoc_macronames[mdoc->last->tok], nch->string); | "%s %s", roff_name[mdoc->last->tok], nch->string); | ||
mdoc_node_relink(mdoc, nch); | mdoc_node_relink(mdoc, nch); | ||
return; | return; | ||
} | } | ||
|
|
||
static void | static void | ||
post_root(POST_ARGS) | post_root(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
/* Add missing prologue data. */ | /* Add missing prologue data. */ | ||
if (mdoc->meta.date == NULL) | if (mdoc->meta.date == NULL) | ||
mdoc->meta.date = mdoc->quick ? | mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : | ||
mandoc_strdup("") : | mandoc_normdate(mdoc, NULL, 0, 0); | ||
mandoc_normdate(mdoc->parse, NULL, 0, 0); | |||
if (mdoc->meta.title == NULL) { | if (mdoc->meta.title == NULL) { | ||
mandoc_msg(MANDOCERR_DT_NOTITLE, | mandoc_msg(MANDOCERR_DT_NOTITLE, | ||
|
|
||
/* Check that we begin with a proper `Sh'. */ | /* Check that we begin with a proper `Sh'. */ | ||
n = mdoc->first->child; | n = mdoc->first->child; | ||
while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE) | while (n != NULL && n->tok != TOKEN_NONE && | ||
mdoc_macros[n->tok].flags & MDOC_PROLOGUE) | |||
n = n->next; | n = n->next; | ||
if (n == NULL) | if (n == NULL) | ||
mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); | mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); | ||
else if (n->tok != MDOC_Sh) | else if (n->tok != MDOC_Sh) | ||
mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, | mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, | ||
n->line, n->pos, mdoc_macronames[n->tok]); | n->line, n->pos, roff_name[n->tok]); | ||
} | } | ||
static void | static void | ||
post_st(POST_ARGS) | |||
{ | |||
struct mdoc_node *n, *nch; | |||
const char *p; | |||
n = mdoc->last; | |||
nch = n->child; | |||
assert(MDOC_TEXT == nch->type); | |||
if (NULL == (p = mdoc_a2st(nch->string))) { | |||
mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, | |||
nch->line, nch->pos, "St %s", nch->string); | |||
mdoc_node_delete(mdoc, n); | |||
} else { | |||
free(nch->string); | |||
nch->string = mandoc_strdup(p); | |||
} | |||
} | |||
static void | |||
post_rs(POST_ARGS) | post_rs(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np, *nch, *next, *prev; | struct roff_node *np, *nch, *next, *prev; | ||
int i, j; | int i, j; | ||
np = mdoc->last; | np = mdoc->last; | ||
if (np->type != MDOC_BODY) | if (np->type != ROFFT_BODY) | ||
return; | return; | ||
if (np->child == NULL) { | if (np->child == NULL) { | ||
|
|
||
break; | break; | ||
if (i == RSORD_MAX) { | if (i == RSORD_MAX) { | ||
mandoc_msg(MANDOCERR_RS_BAD, | mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse, | ||
mdoc->parse, nch->line, nch->pos, | nch->line, nch->pos, roff_name[nch->tok]); | ||
mdoc_macronames[nch->tok]); | |||
i = -1; | i = -1; | ||
} else if (nch->tok == MDOC__J || nch->tok == MDOC__B) | } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) | ||
np->norm->Rs.quote_T++; | np->norm->Rs.quote_T++; | ||
/* | /* | ||
* Remove this child from the chain. This somewhat | * Remove this child from the chain. This somewhat | ||
* repeats mdoc_node_unlink(), but since we're | * repeats roff_node_unlink(), but since we're | ||
* just re-ordering, there's no need for the | * just re-ordering, there's no need for the | ||
* full unlink process. | * full unlink process. | ||
*/ | */ | ||
|
|
||
static void | static void | ||
post_hyph(POST_ARGS) | post_hyph(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nch; | struct roff_node *nch; | ||
char *cp; | char *cp; | ||
for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { | for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { | ||
if (nch->type != MDOC_TEXT) | if (nch->type != ROFFT_TEXT) | ||
continue; | continue; | ||
cp = nch->string; | cp = nch->string; | ||
if (*cp == '\0') | if (*cp == '\0') | ||
|
|
||
} | } | ||
static void | static void | ||
post_hyphtext(POST_ARGS) | |||
{ | |||
ewarn_ge1(mdoc); | |||
post_hyph(mdoc); | |||
} | |||
static void | |||
post_ns(POST_ARGS) | post_ns(POST_ARGS) | ||
{ | { | ||
if (MDOC_LINE & mdoc->last->flags) | if (mdoc->last->flags & NODE_LINE) | ||
mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, | mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, NULL); | mdoc->last->line, mdoc->last->pos, NULL); | ||
} | } | ||
|
|
||
post_ignpar(mdoc); | post_ignpar(mdoc); | ||
switch (mdoc->last->type) { | switch (mdoc->last->type) { | ||
case MDOC_HEAD: | case ROFFT_HEAD: | ||
post_sh_head(mdoc); | post_sh_head(mdoc); | ||
break; | break; | ||
case MDOC_BODY: | case ROFFT_BODY: | ||
switch (mdoc->lastsec) { | switch (mdoc->lastsec) { | ||
case SEC_NAME: | case SEC_NAME: | ||
post_sh_name(mdoc); | post_sh_name(mdoc); | ||
|
|
||
static void | static void | ||
post_sh_name(POST_ARGS) | post_sh_name(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
int hasnm, hasnd; | |||
/* | hasnm = hasnd = 0; | ||
* Warn if the NAME section doesn't contain the `Nm' and `Nd' | |||
* macros (can have multiple `Nm' and one `Nd'). Note that the | |||
* children of the BODY declaration can also be "text". | |||
*/ | |||
if (NULL == (n = mdoc->last->child)) { | for (n = mdoc->last->child; n != NULL; n = n->next) { | ||
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, | switch (n->tok) { | ||
mdoc->last->line, mdoc->last->pos, "empty"); | case MDOC_Nm: | ||
return; | if (hasnm && n->child != NULL) | ||
} | mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT, | ||
mdoc->parse, n->line, n->pos, | |||
for ( ; n && n->next; n = n->next) { | "Nm %s", n->child->string); | ||
if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) | hasnm = 1; | ||
continue; | continue; | ||
if (MDOC_TEXT == n->type) | case MDOC_Nd: | ||
hasnd = 1; | |||
if (n->next != NULL) | |||
mandoc_msg(MANDOCERR_NAMESEC_ND, | |||
mdoc->parse, n->line, n->pos, NULL); | |||
break; | |||
case TOKEN_NONE: | |||
if (n->type == ROFFT_TEXT && | |||
n->string[0] == ',' && n->string[1] == '\0' && | |||
n->next != NULL && n->next->tok == MDOC_Nm) { | |||
n = n->next; | |||
continue; | |||
} | |||
/* FALLTHROUGH */ | |||
default: | |||
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, | |||
n->line, n->pos, roff_name[n->tok]); | |||
continue; | continue; | ||
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, | } | ||
n->line, n->pos, mdoc_macronames[n->tok]); | break; | ||
} | } | ||
assert(n); | if ( ! hasnm) | ||
if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) | mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, | ||
return; | mdoc->last->line, mdoc->last->pos, NULL); | ||
if ( ! hasnd) | |||
mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, | mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, | ||
n->line, n->pos, mdoc_macronames[n->tok]); | mdoc->last->line, mdoc->last->pos, NULL); | ||
} | } | ||
static void | static void | ||
post_sh_see_also(POST_ARGS) | post_sh_see_also(POST_ARGS) | ||
{ | { | ||
const struct mdoc_node *n; | const struct roff_node *n; | ||
const char *name, *sec; | const char *name, *sec; | ||
const char *lastname, *lastsec, *lastpunct; | const char *lastname, *lastsec, *lastpunct; | ||
int cmp; | int cmp; | ||
n = mdoc->last->child; | n = mdoc->last->child; | ||
lastname = lastsec = lastpunct = NULL; | lastname = lastsec = lastpunct = NULL; | ||
while (n != NULL) { | while (n != NULL) { | ||
if (n->tok != MDOC_Xr || n->nchild < 2) | if (n->tok != MDOC_Xr || | ||
n->child == NULL || | |||
n->child->next == NULL) | |||
break; | break; | ||
/* Process one .Xr node. */ | /* Process one .Xr node. */ | ||
|
|
||
lastpunct = "none"; | lastpunct = "none"; | ||
continue; | continue; | ||
} | } | ||
if (n->type != MDOC_TEXT) | if (n->type != ROFFT_TEXT) | ||
break; | break; | ||
for (name = n->string; *name != '\0'; name++) | for (name = n->string; *name != '\0'; name++) | ||
if (isalpha((const unsigned char)*name)) | if (isalpha((const unsigned char)*name)) | ||
return; | return; | ||
lastpunct = n->string; | lastpunct = n->string; | ||
if (n->next == NULL) | if (n->next == NULL || n->next->tok == MDOC_Rs) | ||
mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, | mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, | ||
n->line, n->pos, "%s after %s(%s)", | n->line, n->pos, "%s after %s(%s)", | ||
lastpunct, lastname, lastsec); | lastpunct, lastname, lastsec); | ||
|
|
||
} | } | ||
static int | static int | ||
child_an(const struct mdoc_node *n) | child_an(const struct roff_node *n) | ||
{ | { | ||
for (n = n->child; n != NULL; n = n->next) | for (n = n->child; n != NULL; n = n->next) | ||
if ((n->tok == MDOC_An && n->nchild) || child_an(n)) | if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) | ||
return(1); | return 1; | ||
return(0); | return 0; | ||
} | } | ||
static void | static void | ||
|
|
||
static void | static void | ||
post_sh_head(POST_ARGS) | post_sh_head(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *nch; | ||
const char *goodsec; | const char *goodsec; | ||
char *secname; | enum roff_sec sec; | ||
enum mdoc_sec sec; | |||
/* | /* | ||
* Process a new section. Sections are either "named" or | * Process a new section. Sections are either "named" or | ||
|
|
||
* manual sections. | * manual sections. | ||
*/ | */ | ||
secname = NULL; | sec = mdoc->last->sec; | ||
sec = SEC_CUSTOM; | |||
mdoc_deroff(&secname, mdoc->last); | |||
sec = NULL == secname ? SEC_CUSTOM : a2sec(secname); | |||
/* The NAME should be first. */ | /* The NAME should be first. */ | ||
if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) | if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) | ||
mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, | mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | mdoc->last->line, mdoc->last->pos, "Sh %s", | ||
"Sh %s", secname); | sec != SEC_CUSTOM ? secnames[sec] : | ||
(nch = mdoc->last->child) == NULL ? "" : | |||
nch->type == ROFFT_TEXT ? nch->string : | |||
roff_name[nch->tok]); | |||
/* The SYNOPSIS gets special attention in other areas. */ | /* The SYNOPSIS gets special attention in other areas. */ | ||
if (SEC_SYNOPSIS == sec) { | if (sec == SEC_SYNOPSIS) { | ||
roff_setreg(mdoc->roff, "nS", 1, '='); | roff_setreg(mdoc->roff, "nS", 1, '='); | ||
mdoc->flags |= MDOC_SYNOPSIS; | mdoc->flags |= MDOC_SYNOPSIS; | ||
} else { | } else { | ||
|
|
||
mdoc->lastsec = sec; | mdoc->lastsec = sec; | ||
/* | |||
* Set the section attribute for the current HEAD, for its | |||
* parent BLOCK, and for the HEAD children; the latter can | |||
* only be TEXT nodes, so no recursion is needed. | |||
* For other blocks and elements, including .Sh BODY, this is | |||
* done when allocating the node data structures, but for .Sh | |||
* BLOCK and HEAD, the section is still unknown at that time. | |||
*/ | |||
mdoc->last->parent->sec = sec; | |||
mdoc->last->sec = sec; | |||
for (n = mdoc->last->child; n; n = n->next) | |||
n->sec = sec; | |||
/* We don't care about custom sections after this. */ | /* We don't care about custom sections after this. */ | ||
if (SEC_CUSTOM == sec) { | if (sec == SEC_CUSTOM) | ||
free(secname); | |||
return; | return; | ||
} | |||
/* | /* | ||
* Check whether our non-custom section is being repeated or is | * Check whether our non-custom section is being repeated or is | ||
|
|
||
if (sec == mdoc->lastnamed) | if (sec == mdoc->lastnamed) | ||
mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, | mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | mdoc->last->line, mdoc->last->pos, | ||
"Sh %s", secname); | "Sh %s", secnames[sec]); | ||
if (sec < mdoc->lastnamed) | if (sec < mdoc->lastnamed) | ||
mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, | mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | mdoc->last->line, mdoc->last->pos, | ||
"Sh %s", secname); | "Sh %s", secnames[sec]); | ||
/* Mark the last named section. */ | /* Mark the last named section. */ | ||
|
|
||
/* Check particular section/manual conventions. */ | /* Check particular section/manual conventions. */ | ||
if (mdoc->meta.msec == NULL) { | if (mdoc->meta.msec == NULL) | ||
free(secname); | |||
return; | return; | ||
} | |||
goodsec = NULL; | goodsec = NULL; | ||
switch (sec) { | switch (sec) { | ||
|
|
||
goodsec = "2, 3, 4, 9"; | goodsec = "2, 3, 4, 9"; | ||
/* FALLTHROUGH */ | /* FALLTHROUGH */ | ||
case SEC_RETURN_VALUES: | case SEC_RETURN_VALUES: | ||
/* FALLTHROUGH */ | |||
case SEC_LIBRARY: | case SEC_LIBRARY: | ||
if (*mdoc->meta.msec == '2') | if (*mdoc->meta.msec == '2') | ||
break; | break; | ||
|
|
||
goodsec = "9"; | goodsec = "9"; | ||
mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, | mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | mdoc->last->line, mdoc->last->pos, | ||
"Sh %s for %s only", secname, goodsec); | "Sh %s for %s only", secnames[sec], goodsec); | ||
break; | break; | ||
default: | default: | ||
break; | break; | ||
} | } | ||
free(secname); | |||
} | } | ||
static void | static void | ||
post_xr(POST_ARGS) | |||
{ | |||
struct roff_node *n, *nch; | |||
n = mdoc->last; | |||
nch = n->child; | |||
if (nch->next == NULL) { | |||
mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, | |||
n->line, n->pos, "Xr %s", nch->string); | |||
} else | |||
assert(nch->next == n->last); | |||
post_delim(mdoc); | |||
} | |||
static void | |||
post_ignpar(POST_ARGS) | post_ignpar(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np; | struct roff_node *np; | ||
switch (mdoc->last->type) { | switch (mdoc->last->type) { | ||
case MDOC_HEAD: | case ROFFT_BLOCK: | ||
post_prevpar(mdoc); | |||
return; | |||
case ROFFT_HEAD: | |||
post_hyph(mdoc); | post_hyph(mdoc); | ||
return; | return; | ||
case MDOC_BODY: | case ROFFT_BODY: | ||
break; | break; | ||
default: | default: | ||
return; | return; | ||
} | } | ||
if (NULL != (np = mdoc->last->child)) | if ((np = mdoc->last->child) != NULL) | ||
if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { | if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, | mandoc_vmsg(MANDOCERR_PAR_SKIP, | ||
mdoc->parse, np->line, np->pos, | mdoc->parse, np->line, np->pos, | ||
"%s after %s", mdoc_macronames[np->tok], | "%s after %s", roff_name[np->tok], | ||
mdoc_macronames[mdoc->last->tok]); | roff_name[mdoc->last->tok]); | ||
mdoc_node_delete(mdoc, np); | roff_node_delete(mdoc, np); | ||
} | } | ||
if (NULL != (np = mdoc->last->last)) | if ((np = mdoc->last->last) != NULL) | ||
if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { | if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | ||
np->line, np->pos, "%s at the end of %s", | np->line, np->pos, "%s at the end of %s", | ||
mdoc_macronames[np->tok], | roff_name[np->tok], | ||
mdoc_macronames[mdoc->last->tok]); | roff_name[mdoc->last->tok]); | ||
mdoc_node_delete(mdoc, np); | roff_node_delete(mdoc, np); | ||
} | } | ||
} | } | ||
static void | static void | ||
pre_par(PRE_ARGS) | post_prevpar(POST_ARGS) | ||
{ | { | ||
struct roff_node *n; | |||
if (NULL == mdoc->last) | n = mdoc->last; | ||
if (NULL == n->prev) | |||
return; | return; | ||
if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) | if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) | ||
return; | return; | ||
/* | /* | ||
|
|
||
* block: `Lp', `Pp', or non-compact `Bd' or `Bl'. | * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. | ||
*/ | */ | ||
if (MDOC_Pp != mdoc->last->tok && | if (n->prev->tok != MDOC_Pp && | ||
MDOC_Lp != mdoc->last->tok && | n->prev->tok != MDOC_Lp && | ||
MDOC_br != mdoc->last->tok) | n->prev->tok != ROFF_br) | ||
return; | return; | ||
if (MDOC_Bl == n->tok && n->norm->Bl.comp) | if (n->tok == MDOC_Bl && n->norm->Bl.comp) | ||
return; | return; | ||
if (MDOC_Bd == n->tok && n->norm->Bd.comp) | if (n->tok == MDOC_Bd && n->norm->Bd.comp) | ||
return; | return; | ||
if (MDOC_It == n->tok && n->parent->norm->Bl.comp) | if (n->tok == MDOC_It && n->parent->norm->Bl.comp) | ||
return; | return; | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | n->prev->line, n->prev->pos, "%s before %s", | ||
"%s before %s", mdoc_macronames[mdoc->last->tok], | roff_name[n->prev->tok], roff_name[n->tok]); | ||
mdoc_macronames[n->tok]); | roff_node_delete(mdoc, n->prev); | ||
mdoc_node_delete(mdoc, mdoc->last); | |||
} | } | ||
static void | static void | ||
post_par(POST_ARGS) | post_par(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *np; | struct roff_node *np; | ||
np = mdoc->last; | np = mdoc->last; | ||
if (np->tok != ROFF_br && np->tok != ROFF_sp) | |||
post_prevpar(mdoc); | |||
if (np->tok == MDOC_sp) { | if (np->tok == ROFF_sp) { | ||
if (np->nchild > 1) | if (np->child != NULL && np->child->next != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | ||
np->child->next->line, np->child->next->pos, | np->child->next->line, np->child->next->pos, | ||
"sp ... %s", np->child->next->string); | "sp ... %s", np->child->next->string); | ||
} else if (np->child != NULL) | } else if (np->child != NULL) | ||
mandoc_vmsg(MANDOCERR_ARG_SKIP, | mandoc_vmsg(MANDOCERR_ARG_SKIP, | ||
mdoc->parse, np->line, np->pos, "%s %s", | mdoc->parse, np->line, np->pos, "%s %s", | ||
mdoc_macronames[np->tok], np->child->string); | roff_name[np->tok], np->child->string); | ||
if (NULL == (np = mdoc->last->prev)) { | if ((np = mdoc->last->prev) == NULL) { | ||
np = mdoc->last->parent; | np = mdoc->last->parent; | ||
if (MDOC_Sh != np->tok && MDOC_Ss != np->tok) | if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) | ||
return; | return; | ||
} else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok && | } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && | ||
(MDOC_br != mdoc->last->tok || | (mdoc->last->tok != ROFF_br || | ||
(MDOC_sp != np->tok && MDOC_br != np->tok))) | (np->tok != ROFF_sp && np->tok != ROFF_br))) | ||
return; | return; | ||
mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, | ||
mdoc->last->line, mdoc->last->pos, | mdoc->last->line, mdoc->last->pos, "%s after %s", | ||
"%s after %s", mdoc_macronames[mdoc->last->tok], | roff_name[mdoc->last->tok], roff_name[np->tok]); | ||
mdoc_macronames[np->tok]); | roff_node_delete(mdoc, mdoc->last); | ||
mdoc_node_delete(mdoc, mdoc->last); | |||
} | } | ||
static void | static void | ||
pre_literal(PRE_ARGS) | |||
{ | |||
pre_display(mdoc, n); | |||
if (MDOC_BODY != n->type) | |||
return; | |||
/* | |||
* The `Dl' (note "el" not "one") and `Bd -literal' and `Bd | |||
* -unfilled' macros set MDOC_LITERAL on entrance to the body. | |||
*/ | |||
switch (n->tok) { | |||
case MDOC_Dl: | |||
mdoc->flags |= MDOC_LITERAL; | |||
break; | |||
case MDOC_Bd: | |||
if (DISP_literal == n->norm->Bd.type) | |||
mdoc->flags |= MDOC_LITERAL; | |||
if (DISP_unfilled == n->norm->Bd.type) | |||
mdoc->flags |= MDOC_LITERAL; | |||
break; | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
} | |||
static void | |||
post_dd(POST_ARGS) | post_dd(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n; | ||
char *datestr; | char *datestr; | ||
if (mdoc->meta.date) | n = mdoc->last; | ||
n->flags |= NODE_NOPRT; | |||
if (mdoc->meta.date != NULL) { | |||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | |||
n->line, n->pos, "Dd"); | |||
free(mdoc->meta.date); | free(mdoc->meta.date); | ||
} else if (mdoc->flags & MDOC_PBODY) | |||
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, | |||
n->line, n->pos, "Dd"); | |||
else if (mdoc->meta.title != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | |||
n->line, n->pos, "Dd after Dt"); | |||
else if (mdoc->meta.os != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | |||
n->line, n->pos, "Dd after Os"); | |||
n = mdoc->last; | if (n->child == NULL || n->child->string[0] == '\0') { | ||
if (NULL == n->child || '\0' == n->child->string[0]) { | |||
mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : | mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : | ||
mandoc_normdate(mdoc->parse, NULL, n->line, n->pos); | mandoc_normdate(mdoc, NULL, n->line, n->pos); | ||
goto out; | return; | ||
} | } | ||
datestr = NULL; | datestr = NULL; | ||
mdoc_deroff(&datestr, n); | deroff(&datestr, n); | ||
if (mdoc->quick) | if (mdoc->quick) | ||
mdoc->meta.date = datestr; | mdoc->meta.date = datestr; | ||
else { | else { | ||
mdoc->meta.date = mandoc_normdate(mdoc->parse, | mdoc->meta.date = mandoc_normdate(mdoc, | ||
datestr, n->line, n->pos); | datestr, n->line, n->pos); | ||
free(datestr); | free(datestr); | ||
} | } | ||
out: | |||
mdoc_node_delete(mdoc, n); | |||
} | } | ||
static void | static void | ||
post_dt(POST_ARGS) | post_dt(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *nn, *n; | struct roff_node *nn, *n; | ||
const char *cp; | const char *cp; | ||
char *p; | char *p; | ||
n = mdoc->last; | n = mdoc->last; | ||
n->flags |= NODE_NOPRT; | |||
if (mdoc->flags & MDOC_PBODY) { | |||
mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, | |||
n->line, n->pos, "Dt"); | |||
return; | |||
} | |||
if (mdoc->meta.title != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | |||
n->line, n->pos, "Dt"); | |||
else if (mdoc->meta.os != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, | |||
n->line, n->pos, "Dt after Os"); | |||
free(mdoc->meta.title); | free(mdoc->meta.title); | ||
free(mdoc->meta.msec); | free(mdoc->meta.msec); | ||
free(mdoc->meta.vol); | free(mdoc->meta.vol); | ||
|
|
||
mdoc->meta.vol = NULL; | mdoc->meta.vol = NULL; | ||
mdoc->meta.arch = NULL; | mdoc->meta.arch = NULL; | ||
/* First check that all characters are uppercase. */ | /* Mandatory first argument: title. */ | ||
if (NULL != (nn = n->child)) | nn = n->child; | ||
for (p = nn->string; *p; p++) { | if (nn == NULL || *nn->string == '\0') { | ||
if (toupper((unsigned char)*p) == *p) | |||
continue; | |||
mandoc_vmsg(MANDOCERR_TITLE_CASE, | |||
mdoc->parse, nn->line, | |||
nn->pos + (p - nn->string), | |||
"Dt %s", nn->string); | |||
break; | |||
} | |||
/* No argument: msec and arch remain NULL. */ | |||
if (NULL == (nn = n->child)) { | |||
mandoc_msg(MANDOCERR_DT_NOTITLE, | mandoc_msg(MANDOCERR_DT_NOTITLE, | ||
mdoc->parse, n->line, n->pos, "Dt"); | mdoc->parse, n->line, n->pos, "Dt"); | ||
mdoc->meta.title = mandoc_strdup("UNTITLED"); | mdoc->meta.title = mandoc_strdup("UNTITLED"); | ||
mdoc->meta.vol = mandoc_strdup("LOCAL"); | } else { | ||
goto out; | mdoc->meta.title = mandoc_strdup(nn->string); | ||
/* Check that all characters are uppercase. */ | |||
for (p = nn->string; *p != '\0'; p++) | |||
if (islower((unsigned char)*p)) { | |||
mandoc_vmsg(MANDOCERR_TITLE_CASE, | |||
mdoc->parse, nn->line, | |||
nn->pos + (p - nn->string), | |||
"Dt %s", nn->string); | |||
break; | |||
} | |||
} | } | ||
/* One argument: msec and arch remain NULL. */ | /* Mandatory second argument: section. */ | ||
mdoc->meta.title = mandoc_strdup( | if (nn != NULL) | ||
'\0' == nn->string[0] ? "UNTITLED" : nn->string); | nn = nn->next; | ||
if (NULL == (nn = nn->next)) { | if (nn == NULL) { | ||
mandoc_vmsg(MANDOCERR_MSEC_MISSING, | mandoc_vmsg(MANDOCERR_MSEC_MISSING, | ||
mdoc->parse, n->line, n->pos, | mdoc->parse, n->line, n->pos, | ||
"Dt %s", mdoc->meta.title); | "Dt %s", mdoc->meta.title); | ||
mdoc->meta.vol = mandoc_strdup("LOCAL"); | mdoc->meta.vol = mandoc_strdup("LOCAL"); | ||
goto out; | return; /* msec and arch remain NULL. */ | ||
} | } | ||
/* Handles: `.Dt TITLE SEC' | mdoc->meta.msec = mandoc_strdup(nn->string); | ||
* title = TITLE, | |||
* volume = SEC is msec ? format(msec) : SEC, | |||
* msec = SEC is msec ? atoi(msec) : 0, | |||
* arch = NULL | |||
*/ | |||
/* Infer volume title from section number. */ | |||
cp = mandoc_a2msec(nn->string); | cp = mandoc_a2msec(nn->string); | ||
if (cp) { | if (cp == NULL) { | ||
mdoc->meta.vol = mandoc_strdup(cp); | |||
mdoc->meta.msec = mandoc_strdup(nn->string); | |||
} else { | |||
mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, | mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, | ||
nn->line, nn->pos, "Dt ... %s", nn->string); | nn->line, nn->pos, "Dt ... %s", nn->string); | ||
mdoc->meta.vol = mandoc_strdup(nn->string); | mdoc->meta.vol = mandoc_strdup(nn->string); | ||
mdoc->meta.msec = mandoc_strdup(nn->string); | } else | ||
} | mdoc->meta.vol = mandoc_strdup(cp); | ||
/* Handle an optional architecture */ | /* Optional third argument: architecture. */ | ||
if ((nn = nn->next) != NULL) { | if ((nn = nn->next) == NULL) | ||
for (p = nn->string; *p; p++) | return; | ||
*p = tolower((unsigned char)*p); | |||
mdoc->meta.arch = mandoc_strdup(nn->string); | |||
} | |||
/* Ignore any subsequent parameters... */ | for (p = nn->string; *p != '\0'; p++) | ||
/* FIXME: warn about subsequent parameters. */ | *p = tolower((unsigned char)*p); | ||
out: | mdoc->meta.arch = mandoc_strdup(nn->string); | ||
mdoc_node_delete(mdoc, n); | |||
/* Ignore fourth and later arguments. */ | |||
if ((nn = nn->next) != NULL) | |||
mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, | |||
nn->line, nn->pos, "Dt ... %s", nn->string); | |||
} | } | ||
static void | static void | ||
post_bx(POST_ARGS) | post_bx(POST_ARGS) | ||
{ | { | ||
struct mdoc_node *n; | struct roff_node *n, *nch; | ||
const char *macro; | |||
post_delim(mdoc); | |||
n = mdoc->last; | |||
nch = n->child; | |||
if (nch != NULL) { | |||
macro = !strcmp(nch->string, "Open") ? "Ox" : | |||
!strcmp(nch->string, "Net") ? "Nx" : | |||
!strcmp(nch->string, "Free") ? "Fx" : | |||
!strcmp(nch->string, "DragonFly") ? "Dx" : NULL; | |||
if (macro != NULL) | |||
mandoc_msg(MANDOCERR_BX, mdoc->parse, | |||
n->line, n->pos, macro); | |||
mdoc->last = nch; | |||
nch = nch->next; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
} else | |||
mdoc->next = ROFF_NEXT_CHILD; | |||
roff_word_alloc(mdoc, n->line, n->pos, "BSD"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
if (nch == NULL) { | |||
mdoc->last = n; | |||
return; | |||
} | |||
roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->next = ROFF_NEXT_SIBLING; | |||
roff_word_alloc(mdoc, n->line, n->pos, "-"); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
mdoc->last->flags |= NODE_NOSRC; | |||
mdoc->last = n; | |||
/* | /* | ||
* Make `Bx's second argument always start with an uppercase | * Make `Bx's second argument always start with an uppercase | ||
* letter. Groff checks if it's an "accepted" term, but we just | * letter. Groff checks if it's an "accepted" term, but we just | ||
* uppercase blindly. | * uppercase blindly. | ||
*/ | */ | ||
n = mdoc->last->child; | *nch->string = (char)toupper((unsigned char)*nch->string); | ||
if (n && NULL != (n = n->next)) | |||
*n->string = (char)toupper((unsigned char)*n->string); | |||
} | } | ||
static void | static void | ||
|
|
||
struct utsname utsname; | struct utsname utsname; | ||
static char *defbuf; | static char *defbuf; | ||
#endif | #endif | ||
struct mdoc_node *n; | struct roff_node *n; | ||
n = mdoc->last; | n = mdoc->last; | ||
n->flags |= NODE_NOPRT; | |||
if (mdoc->meta.os != NULL) | |||
mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, | |||
n->line, n->pos, "Os"); | |||
else if (mdoc->flags & MDOC_PBODY) | |||
mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, | |||
n->line, n->pos, "Os"); | |||
/* | /* | ||
* Set the operating system by way of the `Os' macro. | * Set the operating system by way of the `Os' macro. | ||
* The order of precedence is: | * The order of precedence is: | ||
|
|
||
free(mdoc->meta.os); | free(mdoc->meta.os); | ||
mdoc->meta.os = NULL; | mdoc->meta.os = NULL; | ||
mdoc_deroff(&mdoc->meta.os, n); | deroff(&mdoc->meta.os, n); | ||
if (mdoc->meta.os) | if (mdoc->meta.os) | ||
goto out; | goto out; | ||
|
|
||
#ifdef OSNAME | #ifdef OSNAME | ||
mdoc->meta.os = mandoc_strdup(OSNAME); | mdoc->meta.os = mandoc_strdup(OSNAME); | ||
#else /*!OSNAME */ | #else /*!OSNAME */ | ||
if (NULL == defbuf) { | if (defbuf == NULL) { | ||
if (-1 == uname(&utsname)) { | if (uname(&utsname) == -1) { | ||
mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, | mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, | ||
n->line, n->pos, "Os"); | n->line, n->pos, "Os"); | ||
defbuf = mandoc_strdup("UNKNOWN"); | defbuf = mandoc_strdup("UNKNOWN"); | ||
|
|
||
mdoc->meta.os = mandoc_strdup(defbuf); | mdoc->meta.os = mandoc_strdup(defbuf); | ||
#endif /*!OSNAME*/ | #endif /*!OSNAME*/ | ||
out: | out: mdoc->meta.os_e = strstr(mdoc->meta.os, "OpenBSD") != NULL ? | ||
mdoc_node_delete(mdoc, n); | MDOC_OS_OPENBSD : strstr(mdoc->meta.os, "NetBSD") != NULL ? | ||
} | MDOC_OS_NETBSD : MDOC_OS_OTHER; | ||
/* | /* | ||
* If no argument is provided, | * This is the earliest point where we can check | ||
* fill in the name of the current manual page. | * Mdocdate conventions because we don't know | ||
*/ | * the operating system earlier. | ||
static void | */ | ||
post_ex(POST_ARGS) | |||
{ | |||
struct mdoc_node *n; | |||
n = mdoc->last; | while (n->tok != MDOC_Dd) | ||
if ((n = n->prev) == NULL) | |||
if (n->child) | return; | ||
if ((n = n->child) == NULL) | |||
return; | return; | ||
if (strcmp(n->string, "$" "Mdocdate")) { | |||
if (mdoc->meta.name == NULL) { | if (mdoc->meta.os_e == MDOC_OS_OPENBSD) | ||
mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, | mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING, | ||
n->line, n->pos, "Ex"); | mdoc->parse, n->line, n->pos, | ||
return; | "Dd %s", n->string); | ||
} else { | |||
if (mdoc->meta.os_e == MDOC_OS_NETBSD) | |||
mandoc_vmsg(MANDOCERR_MDOCDATE, | |||
mdoc->parse, n->line, n->pos, | |||
"Dd %s", n->string); | |||
} | } | ||
mdoc->next = MDOC_NEXT_CHILD; | |||
mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | |||
mdoc->last = n; | |||
} | } | ||
static enum mdoc_sec | enum roff_sec | ||
a2sec(const char *p) | mdoc_a2sec(const char *p) | ||
{ | { | ||
int i; | int i; | ||
for (i = 0; i < (int)SEC__MAX; i++) | for (i = 0; i < (int)SEC__MAX; i++) | ||
if (secnames[i] && 0 == strcmp(p, secnames[i])) | if (secnames[i] && 0 == strcmp(p, secnames[i])) | ||
return((enum mdoc_sec)i); | return (enum roff_sec)i; | ||
return(SEC_CUSTOM); | return SEC_CUSTOM; | ||
} | } | ||
static size_t | static size_t | ||
macro2len(enum mdoct macro) | macro2len(enum roff_tok macro) | ||
{ | { | ||
switch (macro) { | switch (macro) { | ||
case MDOC_Ad: | case MDOC_Ad: | ||
return(12); | return 12; | ||
case MDOC_Ao: | case MDOC_Ao: | ||
return(12); | return 12; | ||
case MDOC_An: | case MDOC_An: | ||
return(12); | return 12; | ||
case MDOC_Aq: | case MDOC_Aq: | ||
return(12); | return 12; | ||
case MDOC_Ar: | case MDOC_Ar: | ||
return(12); | return 12; | ||
case MDOC_Bo: | case MDOC_Bo: | ||
return(12); | return 12; | ||
case MDOC_Bq: | case MDOC_Bq: | ||
return(12); | return 12; | ||
case MDOC_Cd: | case MDOC_Cd: | ||
return(12); | return 12; | ||
case MDOC_Cm: | case MDOC_Cm: | ||
return(10); | return 10; | ||
case MDOC_Do: | case MDOC_Do: | ||
return(10); | return 10; | ||
case MDOC_Dq: | case MDOC_Dq: | ||
return(12); | return 12; | ||
case MDOC_Dv: | case MDOC_Dv: | ||
return(12); | return 12; | ||
case MDOC_Eo: | case MDOC_Eo: | ||
return(12); | return 12; | ||
case MDOC_Em: | case MDOC_Em: | ||
return(10); | return 10; | ||
case MDOC_Er: | case MDOC_Er: | ||
return(17); | return 17; | ||
case MDOC_Ev: | case MDOC_Ev: | ||
return(15); | return 15; | ||
case MDOC_Fa: | case MDOC_Fa: | ||
return(12); | return 12; | ||
case MDOC_Fl: | case MDOC_Fl: | ||
return(10); | return 10; | ||
case MDOC_Fo: | case MDOC_Fo: | ||
return(16); | return 16; | ||
case MDOC_Fn: | case MDOC_Fn: | ||
return(16); | return 16; | ||
case MDOC_Ic: | case MDOC_Ic: | ||
return(10); | return 10; | ||
case MDOC_Li: | case MDOC_Li: | ||
return(16); | return 16; | ||
case MDOC_Ms: | case MDOC_Ms: | ||
return(6); | return 6; | ||
case MDOC_Nm: | case MDOC_Nm: | ||
return(10); | return 10; | ||
case MDOC_No: | case MDOC_No: | ||
return(12); | return 12; | ||
case MDOC_Oo: | case MDOC_Oo: | ||
return(10); | return 10; | ||
case MDOC_Op: | case MDOC_Op: | ||
return(14); | return 14; | ||
case MDOC_Pa: | case MDOC_Pa: | ||
return(32); | return 32; | ||
case MDOC_Pf: | case MDOC_Pf: | ||
return(12); | return 12; | ||
case MDOC_Po: | case MDOC_Po: | ||
return(12); | return 12; | ||
case MDOC_Pq: | case MDOC_Pq: | ||
return(12); | return 12; | ||
case MDOC_Ql: | case MDOC_Ql: | ||
return(16); | return 16; | ||
case MDOC_Qo: | case MDOC_Qo: | ||
return(12); | return 12; | ||
case MDOC_So: | case MDOC_So: | ||
return(12); | return 12; | ||
case MDOC_Sq: | case MDOC_Sq: | ||
return(12); | return 12; | ||
case MDOC_Sy: | case MDOC_Sy: | ||
return(6); | return 6; | ||
case MDOC_Sx: | case MDOC_Sx: | ||
return(16); | return 16; | ||
case MDOC_Tn: | case MDOC_Tn: | ||
return(10); | return 10; | ||
case MDOC_Va: | case MDOC_Va: | ||
return(12); | return 12; | ||
case MDOC_Vt: | case MDOC_Vt: | ||
return(12); | return 12; | ||
case MDOC_Xr: | case MDOC_Xr: | ||
return(10); | return 10; | ||
default: | default: | ||
break; | break; | ||
}; | }; | ||
return(0); | return 0; | ||
} | } |