Return to man_term.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.56, 2010/03/22 05:59:32 | version 1.121, 2011/09/21 09:57:13 | ||
---|---|---|---|
|
|
||
/* $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 | ||
|
|
||
#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 /* fixed-width char full-indent */ | ||
#define HALFINDENT 3 | #define HALFINDENT 3 /* fixed-width char half-indent */ | ||
#define MAXMARGINS 64 /* maximum number of indented scopes */ | |||
/* FIXME: have PD set the default vspace width. */ | /* FIXME: have PD set the default vspace width. */ | ||
struct mtermp { | struct mtermp { | ||
int fl; | int fl; | ||
#define MANT_LITERAL (1 << 0) | #define MANT_LITERAL (1 << 0) | ||
/* | size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ | ||
* Default amount to indent the left margin after leading text | int lmargincur; /* index of current margin */ | ||
* has been printed (e.g., `HP' left-indent, `TP' and `IP' body | int lmarginsz; /* actual number of nested margins */ | ||
* indent). This needs to be saved because `HP' and so on, if | size_t offset; /* default offset to visible page */ | ||
* not having a specified value, must default. | |||
* | |||
* Note that this is the indentation AFTER the left offset, so | |||
* the total offset is usually offset + lmargin. | |||
*/ | |||
size_t lmargin; | |||
/* | |||
* The default offset, i.e., the amount between any text and the | |||
* page boundary. | |||
*/ | |||
size_t offset; | |||
}; | }; | ||
#define DECL_ARGS struct termp *p, \ | #define DECL_ARGS struct termp *p, \ | ||
|
|
||
#define MAN_NOTEXT (1 << 0) /* Never has text children. */ | #define MAN_NOTEXT (1 << 0) /* Never has text children. */ | ||
}; | }; | ||
static int a2width(const struct man_node *); | static int a2width(const struct termp *, const char *); | ||
static int a2height(const struct man_node *); | static size_t a2height(const struct termp *, const char *); | ||
static void print_man_head(struct termp *, | |||
const struct man_meta *); | |||
static void print_man_nodelist(DECL_ARGS); | static void print_man_nodelist(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_literal(DECL_ARGS); | |||
static int pre_sp(DECL_ARGS); | static int pre_sp(DECL_ARGS); | ||
static int pre_ft(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_TP(DECL_ARGS); | static void post_TP(DECL_ARGS); | ||
static const struct termact termacts[MAN_MAX] = { | static const struct termact termacts[MAN_MAX] = { | ||
{ pre_br, NULL, MAN_NOTEXT }, /* br */ | { pre_sp, NULL, MAN_NOTEXT }, /* br */ | ||
{ NULL, NULL, 0 }, /* TH */ | { NULL, NULL, 0 }, /* TH */ | ||
{ pre_SH, post_SH, 0 }, /* SH */ | { pre_SH, post_SH, 0 }, /* SH */ | ||
{ pre_SS, post_SS, 0 }, /* SS */ | { pre_SS, post_SS, 0 }, /* SS */ | ||
|
|
||
{ pre_HP, post_HP, 0 }, /* HP */ | { pre_HP, post_HP, 0 }, /* HP */ | ||
{ NULL, NULL, 0 }, /* SM */ | { NULL, NULL, 0 }, /* SM */ | ||
{ pre_B, NULL, 0 }, /* SB */ | { pre_B, NULL, 0 }, /* SB */ | ||
{ pre_BI, NULL, 0 }, /* BI */ | { pre_alternate, NULL, 0 }, /* BI */ | ||
{ pre_BI, NULL, 0 }, /* IB */ | { pre_alternate, NULL, 0 }, /* IB */ | ||
{ pre_RB, NULL, 0 }, /* BR */ | { pre_alternate, NULL, 0 }, /* BR */ | ||
{ pre_RB, NULL, 0 }, /* RB */ | { pre_alternate, NULL, 0 }, /* RB */ | ||
{ NULL, NULL, 0 }, /* R */ | { NULL, NULL, 0 }, /* R */ | ||
{ pre_B, NULL, 0 }, /* B */ | { pre_B, NULL, 0 }, /* B */ | ||
{ pre_I, NULL, 0 }, /* I */ | { pre_I, NULL, 0 }, /* I */ | ||
{ pre_RI, NULL, 0 }, /* IR */ | { pre_alternate, NULL, 0 }, /* IR */ | ||
{ pre_RI, NULL, 0 }, /* RI */ | { pre_alternate, NULL, 0 }, /* RI */ | ||
{ NULL, NULL, MAN_NOTEXT }, /* na */ | { pre_ign, NULL, MAN_NOTEXT }, /* na */ | ||
{ pre_I, NULL, 0 }, /* i */ | |||
{ pre_sp, NULL, MAN_NOTEXT }, /* sp */ | { pre_sp, NULL, MAN_NOTEXT }, /* sp */ | ||
{ pre_nf, NULL, 0 }, /* nf */ | { pre_literal, NULL, 0 }, /* nf */ | ||
{ pre_fi, NULL, 0 }, /* fi */ | { pre_literal, NULL, 0 }, /* fi */ | ||
{ NULL, NULL, 0 }, /* r */ | |||
{ NULL, NULL, 0 }, /* RE */ | { NULL, NULL, 0 }, /* RE */ | ||
{ pre_RS, post_RS, 0 }, /* RS */ | { pre_RS, post_RS, 0 }, /* RS */ | ||
{ pre_ign, NULL, 0 }, /* DT */ | { pre_ign, NULL, 0 }, /* DT */ | ||
{ pre_ign, NULL, 0 }, /* UC */ | { pre_ign, NULL, 0 }, /* UC */ | ||
{ pre_ign, NULL, 0 }, /* PD */ | { pre_ign, NULL, 0 }, /* PD */ | ||
{ pre_ign, NULL, 0 }, /* AT */ | |||
{ 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; | memset(&mt, 0, sizeof(struct mtermp)); | ||
mt.lmargin = INDENT; | |||
mt.offset = INDENT; | |||
mt.lmargin[mt.lmargincur] = term_len(p, INDENT); | |||
mt.offset = term_len(p, INDENT); | |||
if (n->child) | if (n->child) | ||
print_man_nodelist(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, atoi(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) | ||
|
|
||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_fi(DECL_ARGS) | pre_literal(DECL_ARGS) | ||
{ | { | ||
mt->fl &= ~MANT_LITERAL; | term_newln(p); | ||
return(1); | |||
} | |||
if (MAN_nf == n->tok) | |||
mt->fl |= MANT_LITERAL; | |||
else | |||
mt->fl &= ~MANT_LITERAL; | |||
/* ARGSUSED */ | /* | ||
static int | * Unlike .IP and .TP, .HP does not have a HEAD. | ||
pre_nf(DECL_ARGS) | * So in case a second call to term_flushln() is needed, | ||
{ | * indentation has to be set up explicitly. | ||
*/ | |||
if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { | |||
p->offset = p->rmargin; | |||
p->rmargin = p->maxrmargin; | |||
p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE); | |||
p->flags |= TERMP_NOSPACE; | |||
} | |||
term_newln(p); | return(0); | ||
mt->fl |= MANT_LITERAL; | |||
return(1); | |||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_RB(DECL_ARGS) | pre_alternate(DECL_ARGS) | ||
{ | { | ||
const struct man_node *nn; | enum termfont font[2]; | ||
int i; | const struct man_node *nn; | ||
int savelit, i; | |||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) { | switch (n->tok) { | ||
if (i % 2 && MAN_RB == n->tok) | case (MAN_RB): | ||
term_fontrepl(p, TERMFONT_BOLD); | font[0] = TERMFONT_NONE; | ||
else if ( ! (i % 2) && MAN_RB != n->tok) | font[1] = TERMFONT_BOLD; | ||
term_fontrepl(p, TERMFONT_BOLD); | break; | ||
else | case (MAN_RI): | ||
term_fontrepl(p, TERMFONT_NONE); | 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(); | |||
} | |||
if (i > 0) | savelit = MANT_LITERAL & mt->fl; | ||
p->flags |= TERMP_NOSPACE; | mt->fl &= ~MANT_LITERAL; | ||
for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { | |||
term_fontrepl(p, font[i]); | |||
if (savelit && NULL == nn->next) | |||
mt->fl |= MANT_LITERAL; | |||
print_man_node(p, mt, nn, m); | print_man_node(p, mt, nn, m); | ||
if (nn->next) | |||
p->flags |= TERMP_NOSPACE; | |||
} | } | ||
return(0); | return(0); | ||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_RI(DECL_ARGS) | pre_B(DECL_ARGS) | ||
{ | { | ||
const struct man_node *nn; | |||
int i; | |||
for (i = 0, nn = n->child; nn; nn = nn->next, i++) { | term_fontrepl(p, TERMFONT_BOLD); | ||
if (i % 2 && MAN_RI == n->tok) | return(1); | ||
term_fontrepl(p, TERMFONT_UNDER); | |||
else if ( ! (i % 2) && MAN_RI != n->tok) | |||
term_fontrepl(p, TERMFONT_UNDER); | |||
else | |||
term_fontrepl(p, TERMFONT_NONE); | |||
if (i > 0) | |||
p->flags |= TERMP_NOSPACE; | |||
print_man_node(p, mt, nn, m); | |||
} | |||
return(0); | |||
} | } | ||
/* ARGSUSED */ | /* ARGSUSED */ | ||
static int | static int | ||
pre_BI(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_BI == n->tok) | term_fontlast(p); | ||
term_fontrepl(p, TERMFONT_UNDER); | return(0); | ||
else if (i % 2) | } | ||
term_fontrepl(p, TERMFONT_BOLD); | |||
else if (MAN_BI == n->tok) | |||
term_fontrepl(p, TERMFONT_BOLD); | |||
else | |||
term_fontrepl(p, TERMFONT_UNDER); | |||
if (i) | cp = n->child->string; | ||
p->flags |= TERMP_NOSPACE; | switch (*cp) { | ||
case ('4'): | |||
print_man_node(p, mt, nn, m); | /* FALLTHROUGH */ | ||
case ('3'): | |||
/* FALLTHROUGH */ | |||
case ('B'): | |||
term_fontrepl(p, TERMFONT_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_B(DECL_ARGS) | pre_in(DECL_ARGS) | ||
{ | { | ||
int len, less; | |||
size_t v; | |||
const char *cp; | |||
term_fontrepl(p, TERMFONT_BOLD); | term_newln(p); | ||
return(1); | |||
if (NULL == n->child) { | |||
p->offset = mt->offset; | |||
return(0); | |||
} | |||
cp = n->child->string; | |||
less = 0; | |||
if ('-' == *cp) | |||
less = -1; | |||
else if ('+' == *cp) | |||
less = 1; | |||
else | |||
cp--; | |||
if ((len = a2width(p, ++cp)) < 0) | |||
return(0); | |||
v = (size_t)len; | |||
if (less < 0) | |||
p->offset -= p->offset > v ? v : p->offset; | |||
else if (less > 0) | |||
p->offset += v; | |||
else | |||
p->offset = v; | |||
/* Don't let this creep beyond the right margin. */ | |||
if (p->offset > p->rmargin) | |||
p->offset = p->rmargin; | |||
return(0); | |||
} | } | ||
|
|
||
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; | if ((NULL == n->prev && n->parent)) { | ||
if (MAN_SS == n->parent->tok) | |||
return(0); | |||
if (MAN_SH == n->parent->tok) | |||
return(0); | |||
} | |||
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); | ||
for (i = 0; i < len; i++) | for (i = 0; i < len; i++) | ||
|
|
||
/* 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, one; | ||
int ival; | int ival; | ||
const struct man_node *nn; | const struct man_node *nn; | ||
|
|
||
return(0); | return(0); | ||
} | } | ||
len = mt->lmargin; | len = mt->lmargin[mt->lmargincur]; | ||
ival = -1; | ival = -1; | ||
/* 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) | one = term_len(p, 1); | ||
len = 1; | if (len < one) | ||
len = one; | |||
p->offset = mt->offset; | p->offset = mt->offset; | ||
p->rmargin = mt->offset + len; | p->rmargin = mt->offset + len; | ||
if (ival >= 0) | if (ival >= 0) | ||
mt->lmargin = (size_t)ival; | mt->lmargin[mt->lmargincur] = (size_t)ival; | ||
return(1); | return(1); | ||
} | } | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->lmargin[mt->lmargincur] = 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): | ||
p->flags |= TERMP_NOLPAD; | |||
p->flags |= TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
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); | ||
|
|
||
return(1); | return(1); | ||
} | } | ||
len = mt->lmargin; | len = mt->lmargin[mt->lmargincur]; | ||
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; | ||
|
|
||
break; | break; | ||
/* Set the saved left-margin. */ | /* Set the saved left-margin. */ | ||
mt->lmargin = (size_t)ival; | mt->lmargin[mt->lmargincur] = (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; | |||
break; | break; | ||
default: | default: | ||
break; | break; | ||
|
|
||
{ | { | ||
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_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
break; | break; | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
|
|
||
return(1); | return(1); | ||
} | } | ||
len = (size_t)mt->lmargin; | len = (size_t)mt->lmargin[mt->lmargincur]; | ||
ival = -1; | ival = -1; | ||
/* Calculate offset. */ | /* Calculate offset. */ | ||
if (NULL != (nn = n->parent->head->child)) { | if (NULL != (nn = n->parent->head->child)) | ||
while (nn && MAN_TEXT != nn->type) | if (nn->string && nn->parent->line == nn->line) | ||
nn = nn->next; | if ((ival = a2width(p, nn->string)) >= 0) | ||
if (nn && nn->next) | |||
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-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[mt->lmargincur] = (size_t)ival; | ||
return(0); | return(0); | ||
case (MAN_BODY): | case (MAN_BODY): | ||
|
|
||
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; | |||
break; | break; | ||
default: | default: | ||
break; | break; | ||
|
|
||
switch (n->type) { | switch (n->type) { | ||
case (MAN_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->fl &= ~MANT_LITERAL; | ||
mt->offset = INDENT; | mt->lmargin[mt->lmargincur] = term_len(p, 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) | ||
|
|
||
break; | break; | ||
case (MAN_HEAD): | case (MAN_HEAD): | ||
term_fontrepl(p, TERMFONT_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_BLOCK): | case (MAN_BLOCK): | ||
mt->lmargin = INDENT; | mt->fl &= ~MANT_LITERAL; | ||
mt->offset = INDENT; | mt->lmargin[mt->lmargincur] = term_len(p, 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): | ||
|
|
||
} | } | ||
} | } | ||
/* 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->rmargin = p->maxrmargin; | ||
p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin; | |||
if (++mt->lmarginsz < MAXMARGINS) | |||
mt->lmargincur = mt->lmarginsz; | |||
mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; | |||
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; | |||
if (--mt->lmarginsz < MAXMARGINS) | |||
mt->lmargincur = mt->lmarginsz; | |||
} | |||
static void | static void | ||
print_man_node(DECL_ARGS) | print_man_node(DECL_ARGS) | ||
{ | { | ||
size_t rm, rmax; | |||
int c; | int c; | ||
c = 1; | |||
switch (n->type) { | switch (n->type) { | ||
case(MAN_TEXT): | case(MAN_TEXT): | ||
if (0 == *n->string) { | /* | ||
* If we have a blank line, output a vertical space. | |||
* If we have a space as the first character, break | |||
* before printing the line's data. | |||
*/ | |||
if ('\0' == *n->string) { | |||
term_vspace(p); | term_vspace(p); | ||
break; | return; | ||
} | } else if (' ' == *n->string && MAN_LINE & n->flags) | ||
term_newln(p); | |||
term_word(p, n->string); | term_word(p, n->string); | ||
/* FIXME: this means that macro lines are munged! */ | /* | ||
* If we're in a literal context, make sure that words | |||
if (MANT_LITERAL & mt->fl) { | * 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 && ! (TERMP_NOBREAK & p->flags) && | |||
(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; | ||
term_flushln(p); | term_flushln(p); | ||
p->rmargin = rm; | |||
p->maxrmargin = rmax; | |||
} | } | ||
break; | |||
if (MAN_EOS & n->flags) | |||
p->flags |= TERMP_SENTENCE; | |||
return; | |||
case (MAN_EQN): | |||
term_eqn(p, n->eqn); | |||
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 ( ! (MAN_NOTEXT & termacts[n->tok].flags)) | |||
term_fontrepl(p, TERMFONT_NONE); | |||
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_nodelist(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)) | ||
if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) | term_fontrepl(p, TERMFONT_NONE); | ||
term_fontrepl(p, TERMFONT_NONE); | |||
} | if (MAN_EOS & n->flags) | ||
p->flags |= TERMP_SENTENCE; | |||
} | } | ||
|
|
||
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; | ||
meta = (const struct man_meta *)arg; | |||
term_fontrepl(p, TERMFONT_NONE); | term_fontrepl(p, TERMFONT_NONE); | ||
time2a(meta->date, buf, DATESIZ); | |||
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) | ||
term_word(p, ""); | term_word(p, ""); | ||
term_flushln(p); | term_flushln(p); | ||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | p->flags |= TERMP_NOSPACE; | ||
p->offset = p->rmargin; | p->offset = p->rmargin; | ||
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; | |||
p->rmargin = p->maxrmargin; | 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->offset = 0; | p->offset = 0; | ||
p->rmargin = p->maxrmargin; | |||
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->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; | |||
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; | p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; | ||
p->offset = 0; | |||
p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? | |||
(p->maxrmargin - | |||
term_strlen(p, buf) + term_len(p, 1)) / 2 : | |||
p->maxrmargin - buflen; | |||
term_word(p, title); | term_word(p, title); | ||
term_flushln(p); | term_flushln(p); | ||
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; | p->flags |= 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_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->offset = 0; | |||
p->flags &= ~TERMP_NOSPACE; | p->flags &= ~TERMP_NOSPACE; | ||
p->offset = 0; | |||
p->rmargin = p->maxrmargin; | |||
/* | |||
* Groff likes to have some leading spaces before content. Well | |||
* that's fine by me. | |||
*/ | |||
term_vspace(p); | |||
term_vspace(p); | |||
term_vspace(p); | |||
} | } |