Return to man_term.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.49, 2009/11/05 08:39:36 | version 1.111, 2011/06/18 17:58:48 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> | ||
* Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> | |||
* | * | ||
* Permission to use, copy, modify, and distribute this software for any | * Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | * purpose with or without fee is hereby granted, provided that the above | ||
|
|
||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | */ | ||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <sys/types.h> | #include <sys/types.h> | ||
#include <assert.h> | #include <assert.h> | ||
|
|
||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include "mandoc.h" | |||
#include "out.h" | #include "out.h" | ||
#include "man.h" | #include "man.h" | ||
#include "term.h" | #include "term.h" | ||
#include "chars.h" | |||
#include "main.h" | #include "main.h" | ||
#define INDENT 7 | #define INDENT 7 | ||
|
|
||
struct termact { | struct termact { | ||
int (*pre)(DECL_ARGS); | int (*pre)(DECL_ARGS); | ||
void (*post)(DECL_ARGS); | void (*post)(DECL_ARGS); | ||
int flags; | |||
#define MAN_NOTEXT (1 << 0) /* Never has text children. */ | |||
}; | }; | ||
#ifdef __linux__ | static int a2width(const struct termp *, const char *); | ||
extern size_t strlcpy(char *, const char *, size_t); | static size_t a2height(const struct termp *, const char *); | ||
extern size_t strlcat(char *, const char *, size_t); | |||
#endif | |||
static int a2width(const struct man_node *); | static void print_man_nodelist(DECL_ARGS); | ||
static int a2height(const struct man_node *); | |||
static void print_man_head(struct termp *, | |||
const struct man_meta *); | |||
static void print_man_body(DECL_ARGS); | |||
static void print_man_node(DECL_ARGS); | static void print_man_node(DECL_ARGS); | ||
static void print_man_foot(struct termp *, | static void print_man_head(struct termp *, const void *); | ||
const struct man_meta *); | static void print_man_foot(struct termp *, const void *); | ||
static void print_bvspace(struct termp *, | static void print_bvspace(struct termp *, | ||
const struct man_node *); | const struct man_node *); | ||
static int pre_alternate(DECL_ARGS); | |||
static int pre_B(DECL_ARGS); | static int pre_B(DECL_ARGS); | ||
static int pre_BI(DECL_ARGS); | |||
static int pre_HP(DECL_ARGS); | static int pre_HP(DECL_ARGS); | ||
static int pre_I(DECL_ARGS); | static int pre_I(DECL_ARGS); | ||
static int pre_IP(DECL_ARGS); | static int pre_IP(DECL_ARGS); | ||
static int pre_PP(DECL_ARGS); | static int pre_PP(DECL_ARGS); | ||
static int pre_RB(DECL_ARGS); | |||
static int pre_RI(DECL_ARGS); | |||
static int pre_RS(DECL_ARGS); | static int pre_RS(DECL_ARGS); | ||
static int pre_SH(DECL_ARGS); | static int pre_SH(DECL_ARGS); | ||
static int pre_SS(DECL_ARGS); | static int pre_SS(DECL_ARGS); | ||
static int pre_TP(DECL_ARGS); | static int pre_TP(DECL_ARGS); | ||
static int pre_br(DECL_ARGS); | |||
static int pre_fi(DECL_ARGS); | |||
static int pre_ign(DECL_ARGS); | static int pre_ign(DECL_ARGS); | ||
static int pre_nf(DECL_ARGS); | static int pre_in(DECL_ARGS); | ||
static int pre_r(DECL_ARGS); | static int pre_literal(DECL_ARGS); | ||
static int pre_sp(DECL_ARGS); | static int pre_sp(DECL_ARGS); | ||
static int pre_ft(DECL_ARGS); | |||
static void post_B(DECL_ARGS); | |||
static void post_I(DECL_ARGS); | |||
static void post_IP(DECL_ARGS); | static void post_IP(DECL_ARGS); | ||
static void post_HP(DECL_ARGS); | static void post_HP(DECL_ARGS); | ||
static void post_RS(DECL_ARGS); | static void post_RS(DECL_ARGS); | ||
static void post_SH(DECL_ARGS); | static void post_SH(DECL_ARGS); | ||
static void post_SS(DECL_ARGS); | static void post_SS(DECL_ARGS); | ||
static void post_TP(DECL_ARGS); | static void post_TP(DECL_ARGS); | ||
static void post_i(DECL_ARGS); | |||
static const struct termact termacts[MAN_MAX] = { | static const struct termact termacts[MAN_MAX] = { | ||
{ pre_br, NULL }, /* br */ | { pre_sp, NULL, MAN_NOTEXT }, /* br */ | ||
{ NULL, NULL }, /* TH */ | { NULL, NULL, 0 }, /* TH */ | ||
{ pre_SH, post_SH }, /* SH */ | { pre_SH, post_SH, 0 }, /* SH */ | ||
{ pre_SS, post_SS }, /* SS */ | { pre_SS, post_SS, 0 }, /* SS */ | ||
{ pre_TP, post_TP }, /* TP */ | { pre_TP, post_TP, 0 }, /* TP */ | ||
{ pre_PP, NULL }, /* LP */ | { pre_PP, NULL, 0 }, /* LP */ | ||
{ pre_PP, NULL }, /* PP */ | { pre_PP, NULL, 0 }, /* PP */ | ||
{ pre_PP, NULL }, /* P */ | { pre_PP, NULL, 0 }, /* P */ | ||
{ pre_IP, post_IP }, /* IP */ | { pre_IP, post_IP, 0 }, /* IP */ | ||
{ pre_HP, post_HP }, /* HP */ | { pre_HP, post_HP, 0 }, /* HP */ | ||
{ NULL, NULL }, /* SM */ | { NULL, NULL, 0 }, /* SM */ | ||
{ pre_B, post_B }, /* SB */ | { pre_B, NULL, 0 }, /* SB */ | ||
{ pre_BI, NULL }, /* BI */ | { pre_alternate, NULL, 0 }, /* BI */ | ||
{ pre_BI, NULL }, /* IB */ | { pre_alternate, NULL, 0 }, /* IB */ | ||
{ pre_RB, NULL }, /* BR */ | { pre_alternate, NULL, 0 }, /* BR */ | ||
{ pre_RB, NULL }, /* RB */ | { pre_alternate, NULL, 0 }, /* RB */ | ||
{ NULL, NULL }, /* R */ | { NULL, NULL, 0 }, /* R */ | ||
{ pre_B, post_B }, /* B */ | { pre_B, NULL, 0 }, /* B */ | ||
{ pre_I, post_I }, /* I */ | { pre_I, NULL, 0 }, /* I */ | ||
{ pre_RI, NULL }, /* IR */ | { pre_alternate, NULL, 0 }, /* IR */ | ||
{ pre_RI, NULL }, /* RI */ | { pre_alternate, NULL, 0 }, /* RI */ | ||
{ NULL, NULL }, /* na */ | { pre_ign, NULL, MAN_NOTEXT }, /* na */ | ||
{ pre_I, post_i }, /* i */ | { pre_sp, NULL, MAN_NOTEXT }, /* sp */ | ||
{ pre_sp, NULL }, /* sp */ | { pre_literal, NULL, 0 }, /* nf */ | ||
{ pre_nf, NULL }, /* nf */ | { pre_literal, NULL, 0 }, /* fi */ | ||
{ pre_fi, NULL }, /* fi */ | { NULL, NULL, 0 }, /* RE */ | ||
{ pre_r, NULL }, /* r */ | { pre_RS, post_RS, 0 }, /* RS */ | ||
{ NULL, NULL }, /* RE */ | { pre_ign, NULL, 0 }, /* DT */ | ||
{ pre_RS, post_RS }, /* RS */ | { pre_ign, NULL, 0 }, /* UC */ | ||
{ pre_ign, NULL }, /* DT */ | { pre_ign, NULL, 0 }, /* PD */ | ||
{ pre_ign, NULL }, /* UC */ | { pre_ign, NULL, 0 }, /* AT */ | ||
{ pre_ign, NULL }, /* PD */ | { pre_in, NULL, MAN_NOTEXT }, /* in */ | ||
{ pre_ft, NULL, MAN_NOTEXT }, /* ft */ | |||
}; | }; | ||
|
|
||
p = (struct termp *)arg; | p = (struct termp *)arg; | ||
p->overstep = 0; | |||
p->maxrmargin = p->defrmargin; | |||
p->tabwidth = term_len(p, 5); | |||
if (NULL == p->symtab) | if (NULL == p->symtab) | ||
switch (p->enc) { | p->symtab = mchars_alloc(); | ||
case (TERMENC_ASCII): | |||
p->symtab = chars_init(CHARS_ASCII); | |||
break; | |||
default: | |||
abort(); | |||
/* NOTREACHED */ | |||
} | |||
n = man_node(man); | n = man_node(man); | ||
m = man_meta(man); | m = man_meta(man); | ||
print_man_head(p, m); | term_begin(p, print_man_head, print_man_foot, m); | ||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
mt.fl = 0; | mt.fl = 0; | ||
mt.lmargin = INDENT; | mt.lmargin = term_len(p, INDENT); | ||
mt.offset = INDENT; | mt.offset = term_len(p, INDENT); | ||
if (n->child) | if (n->child) | ||
print_man_body(p, &mt, n->child, m); | print_man_nodelist(p, &mt, n->child, m); | ||
print_man_foot(p, m); | |||
term_end(p); | |||
} | } | ||
static int | static size_t | ||
a2height(const struct man_node *n) | a2height(const struct termp *p, const char *cp) | ||
{ | { | ||
struct roffsu su; | struct roffsu su; | ||
assert(MAN_TEXT == n->type); | if ( ! a2roffsu(cp, &su, SCALE_VS)) | ||
assert(n->string); | SCALE_VS_INIT(&su, term_strlen(p, cp)); | ||
if ( ! a2roffsu(n->string, &su, SCALE_VS)) | |||
SCALE_VS_INIT(&su, strlen(n->string)); | |||
return((int)term_vspan(&su)); | return(term_vspan(p, &su)); | ||
} | } | ||
static int | static int | ||
a2width(const struct man_node *n) | a2width(const struct termp *p, const char *cp) | ||
{ | { | ||
struct roffsu su; | struct roffsu su; | ||
assert(MAN_TEXT == n->type); | if ( ! a2roffsu(cp, &su, SCALE_BU)) | ||
assert(n->string); | |||
if ( ! a2roffsu(n->string, &su, SCALE_BU)) | |||
return(-1); | return(-1); | ||
return((int)term_hspan(&su)); | return((int)term_hspan(p, &su)); | ||
} | } | ||
/* | |||
* Printing leading vertical space before a block. | |||
* This is used for the paragraph macros. | |||
* The rules are pretty simple, since there's very little nesting going | |||
* on here. Basically, if we're the first within another block (SS/SH), | |||
* then don't emit vertical space. If we are (RS), then do. If not the | |||
* first, print it. | |||
*/ | |||
static void | static void | ||
print_bvspace(struct termp *p, const struct man_node *n) | print_bvspace(struct termp *p, const struct man_node *n) | ||
{ | { | ||
term_newln(p); | term_newln(p); | ||
if (NULL == n->prev) | if (n->body && n->body->child) | ||
return; | if (MAN_TBL == n->body->child->type) | ||
return; | |||
if (MAN_SS == n->prev->tok) | if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) | ||
return; | if (NULL == n->prev) | ||
if (MAN_SH == n->prev->tok) | return; | ||
return; | |||
term_vspace(p); | term_vspace(p); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_ign(DECL_ARGS) | pre_ign(DECL_ARGS) | ||
|
|
||
pre_I(DECL_ARGS) | pre_I(DECL_ARGS) | ||
{ | { | ||
p->under++; | term_fontrepl(p, TERMFONT_UNDER); | ||
return(1); | return(1); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_r(DECL_ARGS) | pre_literal(DECL_ARGS) | ||
{ | { | ||
p->bold = p->under = 0; | term_newln(p); | ||
return(1); | |||
} | |||
if (MAN_nf == n->tok) | |||
mt->fl |= MANT_LITERAL; | |||
else | |||
mt->fl &= ~MANT_LITERAL; | |||
/* ARGSUSED */ | return(0); | ||
static void | |||
post_i(DECL_ARGS) | |||
{ | |||
if (n->nchild) | |||
p->under--; | |||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static void | static int | ||
post_I(DECL_ARGS) | pre_alternate(DECL_ARGS) | ||
{ | { | ||
enum termfont font[2]; | |||
const struct man_node *nn; | |||
int savelit, i; | |||
p->under--; | switch (n->tok) { | ||
} | case (MAN_RB): | ||
font[0] = TERMFONT_NONE; | |||
font[1] = TERMFONT_BOLD; | |||
break; | |||
case (MAN_RI): | |||
font[0] = TERMFONT_NONE; | |||
font[1] = TERMFONT_UNDER; | |||
break; | |||
case (MAN_BR): | |||
font[0] = TERMFONT_BOLD; | |||
font[1] = TERMFONT_NONE; | |||
break; | |||
case (MAN_BI): | |||
font[0] = TERMFONT_BOLD; | |||
font[1] = TERMFONT_UNDER; | |||
break; | |||
case (MAN_IR): | |||
font[0] = TERMFONT_UNDER; | |||
font[1] = TERMFONT_NONE; | |||
break; | |||
case (MAN_IB): | |||
font[0] = TERMFONT_UNDER; | |||
font[1] = TERMFONT_BOLD; | |||
break; | |||
default: | |||
abort(); | |||
} | |||
savelit = MANT_LITERAL & mt->fl; | |||
mt->fl &= ~MANT_LITERAL; | |||
/* ARGSUSED */ | for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { | ||
static int | term_fontrepl(p, font[i]); | ||
pre_fi(DECL_ARGS) | if (savelit && NULL == nn->next) | ||
{ | mt->fl |= MANT_LITERAL; | ||
print_man_node(p, mt, nn, m); | |||
if (nn->next) | |||
p->flags |= TERMP_NOSPACE; | |||
} | |||
mt->fl &= ~MANT_LITERAL; | return(0); | ||
return(1); | |||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_nf(DECL_ARGS) | pre_B(DECL_ARGS) | ||
{ | { | ||
term_newln(p); | term_fontrepl(p, TERMFONT_BOLD); | ||
mt->fl |= MANT_LITERAL; | |||
return(1); | return(1); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_RB(DECL_ARGS) | pre_ft(DECL_ARGS) | ||
{ | { | ||
const struct man_node *nn; | const char *cp; | ||
int i; | |||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) { | if (NULL == n->child) { | ||
if (i % 2 && MAN_RB == n->tok) | term_fontlast(p); | ||
p->bold++; | return(0); | ||
else if ( ! (i % 2) && MAN_RB != n->tok) | } | ||
p->bold++; | |||
if (i > 0) | cp = n->child->string; | ||
p->flags |= TERMP_NOSPACE; | switch (*cp) { | ||
case ('4'): | |||
print_man_node(p, mt, nn, m); | /* FALLTHROUGH */ | ||
case ('3'): | |||
if (i % 2 && MAN_RB == n->tok) | /* FALLTHROUGH */ | ||
p->bold--; | case ('B'): | ||
else if ( ! (i % 2) && MAN_RB != n->tok) | term_fontrepl(p, TERMFONT_BOLD); | ||
p->bold--; | break; | ||
case ('2'): | |||
/* FALLTHROUGH */ | |||
case ('I'): | |||
term_fontrepl(p, TERMFONT_UNDER); | |||
break; | |||
case ('P'): | |||
term_fontlast(p); | |||
break; | |||
case ('1'): | |||
/* FALLTHROUGH */ | |||
case ('C'): | |||
/* FALLTHROUGH */ | |||
case ('R'): | |||
term_fontrepl(p, TERMFONT_NONE); | |||
break; | |||
default: | |||
break; | |||
} | } | ||
return(0); | return(0); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_RI(DECL_ARGS) | pre_in(DECL_ARGS) | ||
{ | { | ||
const struct man_node *nn; | int len, less; | ||
int i; | size_t v; | ||
const char *cp; | |||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) { | term_newln(p); | ||
if (i % 2 && MAN_RI == n->tok) | |||
p->under++; | |||
else if ( ! (i % 2) && MAN_RI != n->tok) | |||
p->under++; | |||
if (i > 0) | if (NULL == n->child) { | ||
p->flags |= TERMP_NOSPACE; | p->offset = mt->offset; | ||
print_man_node(p, mt, nn, m); | return(0); | ||
if (i % 2 && MAN_RI == n->tok) | |||
p->under--; | |||
else if ( ! (i % 2) && MAN_RI != n->tok) | |||
p->under--; | |||
} | } | ||
return(0); | |||
} | |||
cp = n->child->string; | |||
less = 0; | |||
/* ARGSUSED */ | if ('-' == *cp) | ||
static int | less = -1; | ||
pre_BI(DECL_ARGS) | else if ('+' == *cp) | ||
{ | less = 1; | ||
const struct man_node *nn; | else | ||
int i; | cp--; | ||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) { | if ((len = a2width(p, ++cp)) < 0) | ||
if (i % 2 && MAN_BI == n->tok) | return(0); | ||
p->under++; | |||
else if (i % 2) | |||
p->bold++; | |||
else if (MAN_BI == n->tok) | |||
p->bold++; | |||
else | |||
p->under++; | |||
if (i) | v = (size_t)len; | ||
p->flags |= TERMP_NOSPACE; | |||
print_man_node(p, mt, nn, m); | |||
if (i % 2 && MAN_BI == n->tok) | if (less < 0) | ||
p->under--; | p->offset -= p->offset > v ? v : p->offset; | ||
else if (i % 2) | else if (less > 0) | ||
p->bold--; | p->offset += v; | ||
else if (MAN_BI == n->tok) | else | ||
p->bold--; | p->offset = v; | ||
else | |||
p->under--; | |||
} | |||
return(0); | |||
} | |||
/* Don't let this creep beyond the right margin. */ | |||
/* ARGSUSED */ | if (p->offset > p->rmargin) | ||
static int | p->offset = p->rmargin; | ||
pre_B(DECL_ARGS) | |||
{ | |||
p->bold++; | return(0); | ||
return(1); | |||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static void | |||
post_B(DECL_ARGS) | |||
{ | |||
p->bold--; | |||
} | |||
/* ARGSUSED */ | |||
static int | static int | ||
pre_sp(DECL_ARGS) | pre_sp(DECL_ARGS) | ||
{ | { | ||
int i, len; | size_t i, len; | ||
len = n->child ? a2height(n->child) : 1; | switch (n->tok) { | ||
case (MAN_br): | |||
len = 0; | |||
break; | |||
default: | |||
len = n->child ? a2height(p, n->child->string) : 1; | |||
break; | |||
} | |||
if (0 == len) | if (0 == len) | ||
term_newln(p); | term_newln(p); | ||
|
|
||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_br(DECL_ARGS) | |||
{ | |||
term_newln(p); | |||
return(0); | |||
} | |||
/* ARGSUSED */ | |||
static int | |||
pre_HP(DECL_ARGS) | pre_HP(DECL_ARGS) | ||
{ | { | ||
size_t len; | size_t len; | ||
|
|
||
/* Calculate offset. */ | /* Calculate offset. */ | ||
if (NULL != (nn = n->parent->head->child)) | if (NULL != (nn = n->parent->head->child)) | ||
if ((ival = a2width(nn)) >= 0) | if ((ival = a2width(p, nn->string)) >= 0) | ||
len = (size_t)ival; | len = (size_t)ival; | ||
if (0 == len) | if (0 == len) | ||
len = 1; | len = term_len(p, 1); | ||
p->offset = mt->offset; | p->offset = mt->offset; | ||
p->rmargin = mt->offset + len; | p->rmargin = mt->offset + len; | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->lmargin = term_len(p, INDENT); | ||
print_bvspace(p, n); | print_bvspace(p, n); | ||
break; | break; | ||
default: | default: | ||
|
|
||
break; | break; | ||
} | } | ||
return(1); | return(MAN_HEAD != n->type); | ||
} | } | ||
|
|
||
{ | { | ||
const struct man_node *nn; | const struct man_node *nn; | ||
size_t len; | size_t len; | ||
int ival; | int savelit, ival; | ||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BODY): | case (MAN_BODY): | ||
|
|
||
break; | break; | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
p->flags |= TERMP_NOBREAK; | p->flags |= TERMP_NOBREAK; | ||
p->flags |= TERMP_TWOSPACE; | |||
break; | break; | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
print_bvspace(p, n); | print_bvspace(p, n); | ||
|
|
||
len = mt->lmargin; | len = mt->lmargin; | ||
ival = -1; | ival = -1; | ||
/* Calculate offset. */ | /* Calculate the offset from the optional second argument. */ | ||
if (NULL != (nn = n->parent->head->child)) | if (NULL != (nn = n->parent->head->child)) | ||
if (NULL != (nn = nn->next)) { | if (NULL != (nn = nn->next)) | ||
for ( ; nn->next; nn = nn->next) | if ((ival = a2width(p, nn->string)) >= 0) | ||
/* Do nothing. */ ; | |||
if ((ival = a2width(nn)) >= 0) | |||
len = (size_t)ival; | len = (size_t)ival; | ||
} | |||
switch (n->type) { | switch (n->type) { | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
/* Handle zero-width lengths. */ | /* Handle zero-width lengths. */ | ||
if (0 == len) | if (0 == len) | ||
len = 1; | len = term_len(p, 1); | ||
p->offset = mt->offset; | p->offset = mt->offset; | ||
p->rmargin = mt->offset + len; | p->rmargin = mt->offset + len; | ||
|
|
||
/* Set the saved left-margin. */ | /* Set the saved left-margin. */ | ||
mt->lmargin = (size_t)ival; | mt->lmargin = (size_t)ival; | ||
/* Don't print the length value. */ | savelit = MANT_LITERAL & mt->fl; | ||
for (nn = n->child; nn->next; nn = nn->next) | mt->fl &= ~MANT_LITERAL; | ||
print_man_node(p, mt, nn, m); | |||
if (n->child) | |||
print_man_node(p, mt, n->child, m); | |||
if (savelit) | |||
mt->fl |= MANT_LITERAL; | |||
return(0); | return(0); | ||
case (MAN_BODY): | case (MAN_BODY): | ||
p->offset = mt->offset + len; | p->offset = mt->offset + len; | ||
|
|
||
case (MAN_HEAD): | case (MAN_HEAD): | ||
term_flushln(p); | term_flushln(p); | ||
p->flags &= ~TERMP_NOBREAK; | p->flags &= ~TERMP_NOBREAK; | ||
p->flags &= ~TERMP_TWOSPACE; | |||
p->rmargin = p->maxrmargin; | p->rmargin = p->maxrmargin; | ||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
term_flushln(p); | term_newln(p); | ||
p->flags &= ~TERMP_NOLPAD; | p->flags &= ~TERMP_NOLPAD; | ||
break; | break; | ||
default: | default: | ||
|
|
||
{ | { | ||
const struct man_node *nn; | const struct man_node *nn; | ||
size_t len; | size_t len; | ||
int ival; | int savelit, ival; | ||
switch (n->type) { | switch (n->type) { | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
p->flags |= TERMP_NOBREAK; | p->flags |= TERMP_NOBREAK; | ||
p->flags |= TERMP_TWOSPACE; | |||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
p->flags |= TERMP_NOLPAD; | p->flags |= TERMP_NOLPAD; | ||
|
|
||
/* Calculate offset. */ | /* Calculate offset. */ | ||
if (NULL != (nn = n->parent->head->child)) | if (NULL != (nn = n->parent->head->child)) { | ||
if (NULL != nn->next) | while (nn && MAN_TEXT != nn->type) | ||
if ((ival = a2width(nn)) >= 0) | nn = nn->next; | ||
if (nn && nn->next) | |||
if ((ival = a2width(p, nn->string)) >= 0) | |||
len = (size_t)ival; | len = (size_t)ival; | ||
} | |||
switch (n->type) { | switch (n->type) { | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
/* Handle zero-length properly. */ | /* Handle zero-length properly. */ | ||
if (0 == len) | if (0 == len) | ||
len = 1; | len = term_len(p, 1); | ||
p->offset = mt->offset; | p->offset = mt->offset; | ||
p->rmargin = mt->offset + len; | p->rmargin = mt->offset + len; | ||
savelit = MANT_LITERAL & mt->fl; | |||
mt->fl &= ~MANT_LITERAL; | |||
/* Don't print same-line elements. */ | /* Don't print same-line elements. */ | ||
for (nn = n->child; nn; nn = nn->next) | for (nn = n->child; nn; nn = nn->next) | ||
if (nn->line > n->line) | if (nn->line > n->line) | ||
print_man_node(p, mt, nn, m); | print_man_node(p, mt, nn, m); | ||
if (savelit) | |||
mt->fl |= MANT_LITERAL; | |||
if (ival >= 0) | if (ival >= 0) | ||
mt->lmargin = (size_t)ival; | mt->lmargin = (size_t)ival; | ||
|
|
||
p->rmargin = p->maxrmargin; | p->rmargin = p->maxrmargin; | ||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
term_flushln(p); | term_newln(p); | ||
p->flags &= ~TERMP_NOLPAD; | p->flags &= ~TERMP_NOLPAD; | ||
break; | break; | ||
default: | default: | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->lmargin = term_len(p, INDENT); | ||
mt->offset = INDENT; | mt->offset = term_len(p, INDENT); | ||
/* If following a prior empty `SS', no vspace. */ | /* If following a prior empty `SS', no vspace. */ | ||
if (n->prev && MAN_SS == n->prev->tok) | if (n->prev && MAN_SS == n->prev->tok) | ||
if (NULL == n->prev->body->child) | if (NULL == n->prev->body->child) | ||
|
|
||
term_vspace(p); | term_vspace(p); | ||
break; | break; | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
p->bold++; | term_fontrepl(p, TERMFONT_BOLD); | ||
p->offset = HALFINDENT; | p->offset = term_len(p, HALFINDENT); | ||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
p->offset = mt->offset; | p->offset = mt->offset; | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
term_newln(p); | term_newln(p); | ||
p->bold--; | |||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
term_newln(p); | term_newln(p); | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->lmargin = term_len(p, INDENT); | ||
mt->offset = INDENT; | mt->offset = term_len(p, INDENT); | ||
/* If following a prior empty `SH', no vspace. */ | /* If following a prior empty `SH', no vspace. */ | ||
if (n->prev && MAN_SH == n->prev->tok) | if (n->prev && MAN_SH == n->prev->tok) | ||
if (NULL == n->prev->body->child) | if (NULL == n->prev->body->child) | ||
break; | break; | ||
/* If the first macro, no vspae. */ | |||
if (NULL == n->prev) | |||
break; | |||
term_vspace(p); | term_vspace(p); | ||
break; | break; | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
p->bold++; | term_fontrepl(p, TERMFONT_BOLD); | ||
p->offset = 0; | p->offset = 0; | ||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
term_newln(p); | term_newln(p); | ||
p->bold--; | |||
break; | break; | ||
case (MAN_BODY): | case (MAN_BODY): | ||
term_newln(p); | term_newln(p); | ||
|
|
||
} | } | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_RS(DECL_ARGS) | pre_RS(DECL_ARGS) | ||
{ | { | ||
const struct man_node *nn; | int ival; | ||
int ival; | size_t sz; | ||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
|
|
||
break; | break; | ||
} | } | ||
if (NULL == (nn = n->parent->head->child)) { | sz = term_len(p, INDENT); | ||
mt->offset = mt->lmargin + INDENT; | |||
p->offset = mt->offset; | |||
return(1); | |||
} | |||
if ((ival = a2width(nn)) < 0) | if (NULL != (n = n->parent->head->child)) | ||
return(1); | if ((ival = a2width(p, n->string)) >= 0) | ||
sz = (size_t)ival; | |||
mt->offset = INDENT + (size_t)ival; | mt->offset += sz; | ||
p->offset = mt->offset; | p->offset = mt->offset; | ||
return(1); | return(1); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static void | static void | ||
post_RS(DECL_ARGS) | post_RS(DECL_ARGS) | ||
{ | { | ||
int ival; | |||
size_t sz; | |||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->offset = mt->lmargin = INDENT; | return; | ||
break; | case (MAN_HEAD): | ||
return; | |||
default: | default: | ||
term_newln(p); | term_newln(p); | ||
p->offset = INDENT; | |||
break; | break; | ||
} | } | ||
} | |||
sz = term_len(p, INDENT); | |||
if (NULL != (n = n->parent->head->child)) | |||
if ((ival = a2width(p, n->string)) >= 0) | |||
sz = (size_t)ival; | |||
mt->offset = mt->offset < sz ? 0 : mt->offset - sz; | |||
p->offset = mt->offset; | |||
} | |||
static void | static void | ||
print_man_node(DECL_ARGS) | print_man_node(DECL_ARGS) | ||
{ | { | ||
int c, sz; | size_t rm, rmax; | ||
int c; | |||
c = 1; | |||
switch (n->type) { | switch (n->type) { | ||
case(MAN_TEXT): | case(MAN_TEXT): | ||
if (0 == *n->string) { | |||
term_vspace(p); | |||
break; | |||
} | |||
/* | /* | ||
* Note! This is hacky. Here, we recognise the `\c' | * If we have a blank line, output a vertical space. | ||
* escape embedded in so many -man pages. It's supposed | * If we have a space as the first character, break | ||
* to remove the subsequent space, so we mark NOSPACE if | * before printing the line's data. | ||
* it's encountered in the string. | |||
*/ | */ | ||
sz = (int)strlen(n->string); | if ('\0' == *n->string) { | ||
term_vspace(p); | |||
return; | |||
} else if (' ' == *n->string && MAN_LINE & n->flags) | |||
term_newln(p); | |||
term_word(p, n->string); | term_word(p, n->string); | ||
if (sz >= 2 && n->string[sz - 1] == 'c' && | |||
n->string[sz - 2] == '\\') | /* | ||
* If we're in a literal context, make sure that words | |||
* togehter on the same line stay together. This is a | |||
* POST-printing call, so we check the NEXT word. Since | |||
* -man doesn't have nested macros, we don't need to be | |||
* more specific than this. | |||
*/ | |||
if (MANT_LITERAL & mt->fl && | |||
(NULL == n->next || | |||
n->next->line > n->line)) { | |||
rm = p->rmargin; | |||
rmax = p->maxrmargin; | |||
p->rmargin = p->maxrmargin = TERM_MAXMARGIN; | |||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
/* FIXME: this means that macro lines are munged! */ | |||
if (MANT_LITERAL & mt->fl) { | |||
p->flags |= TERMP_NOSPACE; | |||
term_flushln(p); | term_flushln(p); | ||
p->flags &= ~TERMP_NOLPAD; | |||
p->rmargin = rm; | |||
p->maxrmargin = rmax; | |||
} | } | ||
break; | |||
if (MAN_EOS & n->flags) | |||
p->flags |= TERMP_SENTENCE; | |||
return; | |||
case (MAN_EQN): | |||
term_word(p, n->eqn->data); | |||
return; | |||
case (MAN_TBL): | |||
/* | |||
* Tables are preceded by a newline. Then process a | |||
* table line, which will cause line termination, | |||
*/ | |||
if (TBL_SPAN_FIRST & n->span->flags) | |||
term_newln(p); | |||
term_tbl(p, n->span); | |||
return; | |||
default: | default: | ||
if (termacts[n->tok].pre) | |||
c = (*termacts[n->tok].pre)(p, mt, n, m); | |||
break; | break; | ||
} | } | ||
if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) | |||
term_fontrepl(p, TERMFONT_NONE); | |||
c = 1; | |||
if (termacts[n->tok].pre) | |||
c = (*termacts[n->tok].pre)(p, mt, n, m); | |||
if (c && n->child) | if (c && n->child) | ||
print_man_body(p, mt, n->child, m); | print_man_nodelist(p, mt, n->child, m); | ||
if (MAN_TEXT != n->type) | if (termacts[n->tok].post) | ||
if (termacts[n->tok].post) | (*termacts[n->tok].post)(p, mt, n, m); | ||
(*termacts[n->tok].post)(p, mt, n, m); | if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) | ||
term_fontrepl(p, TERMFONT_NONE); | |||
if (MAN_EOS & n->flags) | |||
p->flags |= TERMP_SENTENCE; | |||
} | } | ||
static void | static void | ||
print_man_body(DECL_ARGS) | print_man_nodelist(DECL_ARGS) | ||
{ | { | ||
print_man_node(p, mt, n, m); | print_man_node(p, mt, n, m); | ||
if ( ! n->next) | if ( ! n->next) | ||
return; | return; | ||
print_man_body(p, mt, n->next, m); | print_man_nodelist(p, mt, n->next, m); | ||
} | } | ||
static void | static void | ||
print_man_foot(struct termp *p, const struct man_meta *meta) | print_man_foot(struct termp *p, const void *arg) | ||
{ | { | ||
char buf[DATESIZ]; | const struct man_meta *meta; | ||
time2a(meta->date, buf, DATESIZ); | meta = (const struct man_meta *)arg; | ||
term_fontrepl(p, TERMFONT_NONE); | |||
term_vspace(p); | term_vspace(p); | ||
term_vspace(p); | |||
term_vspace(p); | |||
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; | p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; | ||
p->rmargin = p->maxrmargin - strlen(buf); | p->rmargin = p->maxrmargin - term_strlen(p, meta->date); | ||
p->offset = 0; | p->offset = 0; | ||
/* term_strlen() can return zero. */ | |||
if (p->rmargin == p->maxrmargin) | |||
p->rmargin--; | |||
if (meta->source) | if (meta->source) | ||
term_word(p, meta->source); | term_word(p, meta->source); | ||
if (meta->source) | if (meta->source) | ||
|
|
||
p->rmargin = p->maxrmargin; | p->rmargin = p->maxrmargin; | ||
p->flags &= ~TERMP_NOBREAK; | p->flags &= ~TERMP_NOBREAK; | ||
term_word(p, buf); | term_word(p, meta->date); | ||
term_flushln(p); | term_flushln(p); | ||
} | } | ||
static void | static void | ||
print_man_head(struct termp *p, const struct man_meta *m) | print_man_head(struct termp *p, const void *arg) | ||
{ | { | ||
char buf[BUFSIZ], title[BUFSIZ]; | char buf[BUFSIZ], title[BUFSIZ]; | ||
size_t buflen, titlen; | |||
const struct man_meta *m; | |||
m = (const struct man_meta *)arg; | |||
/* | |||
* Note that old groff would spit out some spaces before the | |||
* header. We discontinue this strange behaviour, but at one | |||
* point we did so here. | |||
*/ | |||
p->rmargin = p->maxrmargin; | p->rmargin = p->maxrmargin; | ||
p->offset = 0; | p->offset = 0; | ||
buf[0] = title[0] = '\0'; | buf[0] = title[0] = '\0'; | ||
if (m->vol) | if (m->vol) | ||
strlcpy(buf, m->vol, BUFSIZ); | strlcpy(buf, m->vol, BUFSIZ); | ||
buflen = term_strlen(p, buf); | |||
snprintf(title, BUFSIZ, "%s(%d)", m->title, m->msec); | snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); | ||
titlen = term_strlen(p, title); | |||
p->offset = 0; | p->offset = 0; | ||
p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; | p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? | ||
(p->maxrmargin - | |||
term_strlen(p, buf) + term_len(p, 1)) / 2 : | |||
p->maxrmargin - buflen; | |||
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; | p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; | ||
term_word(p, title); | term_word(p, title); | ||
|
|
||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | ||
p->offset = p->rmargin; | p->offset = p->rmargin; | ||
p->rmargin = p->maxrmargin - strlen(title); | p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? | ||
p->maxrmargin - titlen : p->maxrmargin; | |||
term_word(p, buf); | term_word(p, buf); | ||
term_flushln(p); | term_flushln(p); | ||
p->offset = p->rmargin; | |||
p->rmargin = p->maxrmargin; | |||
p->flags &= ~TERMP_NOBREAK; | p->flags &= ~TERMP_NOBREAK; | ||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | if (p->rmargin + titlen <= p->maxrmargin) { | ||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | |||
p->offset = p->rmargin; | |||
p->rmargin = p->maxrmargin; | |||
term_word(p, title); | |||
term_flushln(p); | |||
} | |||
term_word(p, title); | |||
term_flushln(p); | |||
p->rmargin = p->maxrmargin; | p->rmargin = p->maxrmargin; | ||
p->offset = 0; | p->offset = 0; | ||
p->flags &= ~TERMP_NOSPACE; | p->flags &= ~TERMP_NOSPACE; | ||
/* | |||
* Groff likes to have some leading spaces before content. Well | |||
* that's fine by me. | |||
*/ | |||
term_vspace(p); | |||
term_vspace(p); | |||
term_vspace(p); | |||
} | } |