-/* $Id: mdoc_man.c,v 1.31 2012/07/10 19:54:11 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.41 2012/07/16 10:45:41 schwarze Exp $ */
/*
* Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
*
static int cond_head(DECL_ARGS);
static void font_push(char);
static void font_pop(void);
+static void post__t(DECL_ARGS);
static void post_bd(DECL_ARGS);
static void post_bf(DECL_ARGS);
static void post_bk(DECL_ARGS);
static void post_sect(DECL_ARGS);
static void post_sp(DECL_ARGS);
static void post_vt(DECL_ARGS);
+static int pre__t(DECL_ARGS);
static int pre_an(DECL_ARGS);
static int pre_ap(DECL_ARGS);
static int pre_bd(DECL_ARGS);
static int pre_no(DECL_ARGS);
static int pre_ns(DECL_ARGS);
static int pre_pp(DECL_ARGS);
+static int pre_rs(DECL_ARGS);
static int pre_sm(DECL_ARGS);
static int pre_sp(DECL_ARGS);
static int pre_sect(DECL_ARGS);
static int pre_ux(DECL_ARGS);
static int pre_xr(DECL_ARGS);
static void print_word(const char *);
+static void print_line(const char *, int);
+static void print_block(const char *, int);
static void print_offs(const char *);
-static void print_width(const char *, const struct mdoc_node *);
+static void print_width(const char *,
+ const struct mdoc_node *, size_t);
static void print_count(int *);
static void print_node(DECL_ARGS);
{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
- { NULL, NULL, post_percent, NULL, NULL }, /* _%A */
- { NULL, NULL, NULL, NULL, NULL }, /* _%B */
- { NULL, NULL, post_percent, NULL, NULL }, /* _%D */
- { NULL, NULL, NULL, NULL, NULL }, /* _%I */
- { NULL, pre_enc, post_percent, "\\fI", "\\fP" }, /* %J */
- { NULL, NULL, NULL, NULL, NULL }, /* _%N */
- { NULL, NULL, NULL, NULL, NULL }, /* _%O */
- { NULL, NULL, NULL, NULL, NULL }, /* _%P */
- { NULL, NULL, NULL, NULL, NULL }, /* _%R */
- { NULL, pre_enc, post_percent, "\"", "\"" }, /* %T */
- { NULL, NULL, NULL, NULL, NULL }, /* _%V */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %A */
+ { NULL, pre_em, post_percent, NULL, NULL }, /* %B */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %D */
+ { NULL, pre_em, post_percent, NULL, NULL }, /* %I */
+ { NULL, pre_em, post_percent, NULL, NULL }, /* %J */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %N */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %O */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %P */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %R */
+ { NULL, pre__t, post__t, NULL, NULL }, /* %T */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %V */
{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
{ cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
{ cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
{ NULL, NULL, NULL, NULL, NULL }, /* Re */
- { cond_body, pre_pp, NULL, NULL, NULL }, /* Rs */
+ { cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
{ cond_body, pre_enc, post_enc, "`", "'" }, /* So */
{ cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
- { NULL, NULL, NULL, NULL, NULL }, /* _Xc */
- { NULL, NULL, NULL, NULL, NULL }, /* _Xo */
+ { NULL, NULL, NULL, NULL, NULL }, /* Xc */
+ { NULL, NULL, NULL, NULL, NULL }, /* Xo */
{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
- { NULL, NULL, NULL, NULL, NULL }, /* _%C */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %C */
{ NULL, NULL, NULL, NULL, NULL }, /* Es */
{ NULL, NULL, NULL, NULL, NULL }, /* En */
{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
- { NULL, NULL, NULL, NULL, NULL }, /* _%Q */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %Q */
{ NULL, pre_br, NULL, NULL, NULL }, /* br */
{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
- { NULL, NULL, NULL, NULL, NULL }, /* _%U */
- { NULL, NULL, NULL, NULL, NULL }, /* _Ta */
+ { NULL, NULL, post_percent, NULL, NULL }, /* %U */
+ { NULL, NULL, NULL, NULL, NULL }, /* Ta */
{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
};
static int outflags;
-#define MMAN_spc (1 << 0)
-#define MMAN_spc_force (1 << 1)
-#define MMAN_nl (1 << 2)
-#define MMAN_br (1 << 3)
-#define MMAN_sp (1 << 4)
-#define MMAN_Sm (1 << 5)
-#define MMAN_Bk (1 << 6)
-#define MMAN_An_split (1 << 7)
-#define MMAN_An_nosplit (1 << 8)
+#define MMAN_spc (1 << 0) /* blank character before next word */
+#define MMAN_spc_force (1 << 1) /* even before trailing punctuation */
+#define MMAN_nl (1 << 2) /* break man(7) code line */
+#define MMAN_br (1 << 3) /* break output line */
+#define MMAN_sp (1 << 4) /* insert a blank output line */
+#define MMAN_PP (1 << 5) /* reset indentation etc. */
+#define MMAN_Sm (1 << 6) /* horizontal spacing mode */
+#define MMAN_Bk (1 << 7) /* word keep mode */
+#define MMAN_An_split (1 << 8) /* author mode is "split" */
+#define MMAN_An_nosplit (1 << 9) /* author mode is "nosplit" */
+
+static int TPremain; /* characters before tag is full */
static struct {
char *head;
fontqueue.size);
}
*fontqueue.tail = newfont;
- print_word("\\f");
+ print_word("");
+ printf("\\f");
putchar(newfont);
outflags &= ~MMAN_spc;
}
if (fontqueue.tail > fontqueue.head)
fontqueue.tail--;
outflags &= ~MMAN_spc;
- print_word("\\f");
+ print_word("");
+ printf("\\f");
putchar(*fontqueue.tail);
}
print_word(const char *s)
{
- if ((MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
+ if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
/*
* If we need a newline, print it now and start afresh.
*/
- if (MMAN_sp & outflags)
+ if (MMAN_PP & outflags) {
+ if ( ! (MMAN_sp & outflags))
+ printf("\n.sp -1v");
+ printf("\n.PP\n");
+ } else if (MMAN_sp & outflags)
printf("\n.sp\n");
else if (MMAN_br & outflags)
printf("\n.br\n");
else if (MMAN_nl & outflags)
putchar('\n');
- outflags &= ~(MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
- } else if (MMAN_spc & outflags && '\0' != s[0])
+ outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
+ if (1 == TPremain)
+ printf(".br\n");
+ TPremain = 0;
+ } else if (MMAN_spc & outflags) {
/*
* If we need a space, only print it if
* (1) it is forced by `No' or
* (2) what follows is not terminating punctuation or
* (3) what follows is longer than one character.
*/
- if (MMAN_spc_force & outflags ||
+ if (MMAN_spc_force & outflags || '\0' == s[0] ||
NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
if (MMAN_Bk & outflags) {
putchar('\\');
putchar('~');
} else
putchar(' ');
+ if (TPremain)
+ TPremain--;
}
+ }
/*
* Reassign needing space if we're not following opening
* punctuation.
*/
- if (MMAN_Sm & outflags &&
- (('(' != s[0] && '[' != s[0]) || '\0' != s[1]))
+ if (MMAN_Sm & outflags && ('\0' == s[0] ||
+ (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
outflags |= MMAN_spc;
else
outflags &= ~MMAN_spc;
putchar((unsigned char)*s);
break;
}
+ if (TPremain)
+ TPremain--;
}
}
+static void
+print_line(const char *s, int newflags)
+{
+
+ outflags &= ~MMAN_br;
+ outflags |= MMAN_nl;
+ print_word(s);
+ outflags |= newflags;
+}
+
+static void
+print_block(const char *s, int newflags)
+{
+
+ outflags &= ~MMAN_PP;
+ if (MMAN_sp & outflags)
+ outflags &= ~(MMAN_sp | MMAN_br);
+ else
+ print_line(".sp -1v", 0);
+ outflags |= MMAN_nl;
+ print_word(s);
+ outflags |= newflags;
+}
+
static void
print_offs(const char *v)
{
}
void
-print_width(const char *v, const struct mdoc_node *child)
+print_width(const char *v, const struct mdoc_node *child, size_t defsz)
{
char buf[24];
struct roffsu su;
size_t sz, chsz;
+ int numeric, remain;
- /* XXX Rough estimation, might have multiple parts. */
- chsz = (NULL != child && MDOC_TEXT == child->type) ?
- strlen(child->string) : 0;
-
- if (a2roffsu(v, &su, SCALE_MAX)) {
+ numeric = 1;
+ remain = 0;
+ if (NULL == v)
+ sz = defsz;
+ else if (a2roffsu(v, &su, SCALE_MAX)) {
if (SCALE_EN == su.unit)
sz = su.scale;
else {
- if (chsz)
- print_word(".HP");
- else
- print_word(".TP");
- print_word(v);
- return;
+ sz = 0;
+ numeric = 0;
}
} else
sz = strlen(v);
- if (chsz > sz)
- print_word(".HP");
- else
- print_word(".TP");
- snprintf(buf, sizeof(buf), "%ldn", sz + 2);
- print_word(buf);
+ /* XXX Rough estimation, might have multiple parts. */
+ chsz = (NULL != child && MDOC_TEXT == child->type) ?
+ strlen(child->string) : 0;
+
+ if (defsz && chsz > sz)
+ print_block(".HP", 0);
+ else {
+ print_block(".TP", 0);
+ remain = sz + 2;
+ }
+ if (numeric) {
+ snprintf(buf, sizeof(buf), "%ldn", sz + 2);
+ print_word(buf);
+ } else
+ print_word(v);
+ TPremain = remain;
}
void
*/
if (MMAN_nl & outflags && ('.' == *n->string ||
'\'' == *n->string)) {
- print_word("\\&");
+ print_word("");
+ printf("\\&");
outflags &= ~MMAN_spc;
}
print_word(n->string);
font_pop();
}
-/*
- * Used in listings (percent = %A, e.g.).
- * FIXME: this is incomplete.
- * It doesn't print a nice ", and" for lists.
- */
static void
post_percent(DECL_ARGS)
{
- post_enc(m, n);
- if (n->next)
+ if (pre_em == manacts[n->tok].pre)
+ font_pop();
+ if (n->next) {
print_word(",");
- else {
+ if (n->prev && n->prev->tok == n->tok &&
+ n->next->tok == n->tok)
+ print_word("and");
+ } else {
print_word(".");
outflags |= MMAN_nl;
}
}
+static int
+pre__t(DECL_ARGS)
+{
+
+ if (n->parent && MDOC_Rs == n->parent->tok &&
+ n->parent->norm->Rs.quote_T) {
+ print_word("");
+ putchar('\"');
+ outflags &= ~MMAN_spc;
+ } else
+ font_push('I');
+ return(1);
+}
+
+static void
+post__t(DECL_ARGS)
+{
+
+ if (n->parent && MDOC_Rs == n->parent->tok &&
+ n->parent->norm->Rs.quote_T) {
+ outflags &= ~MMAN_spc;
+ print_word("");
+ putchar('\"');
+ } else
+ font_pop();
+ post_percent(m, n);
+}
+
/*
* Print before a section header.
*/
if (MDOC_HEAD != n->type)
return(1);
- outflags |= MMAN_nl;
- print_word(manacts[n->tok].prefix);
- print_word("\"");
+ outflags |= MMAN_sp;
+ print_block(manacts[n->tok].prefix, 0);
+ print_word("");
+ putchar('\"');
outflags &= ~MMAN_spc;
return(1);
}
if (MDOC_HEAD != n->type)
return;
outflags &= ~MMAN_spc;
- print_word("\"");
+ print_word("");
+ putchar('\"');
outflags |= MMAN_nl;
if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
pre_bd(DECL_ARGS)
{
- if (0 == n->norm->Bd.comp)
- outflags |= MMAN_sp;
+ outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
+
if (DISP_unfilled == n->norm->Bd.type ||
- DISP_literal == n->norm->Bd.type) {
- outflags |= MMAN_nl;
- print_word(".nf");
- }
- outflags |= MMAN_nl;
- print_word(".RS");
+ DISP_literal == n->norm->Bd.type)
+ print_line(".nf", 0);
+ if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
+ outflags |= MMAN_sp;
+ print_line(".RS", 0);
print_offs(n->norm->Bd.offs);
outflags |= MMAN_nl;
return(1);
post_bd(DECL_ARGS)
{
- outflags |= MMAN_nl;
- print_word(".RE");
+ print_line(".RE", MMAN_nl);
if (DISP_unfilled == n->norm->Bd.type ||
- DISP_literal == n->norm->Bd.type) {
- outflags |= MMAN_nl;
- print_word(".fi");
- }
- outflags |= MMAN_nl;
+ DISP_literal == n->norm->Bd.type)
+ print_line(".fi", MMAN_nl);
}
static int
static int
pre_bl(DECL_ARGS)
{
+ size_t icol;
- if (LIST_enum == n->norm->Bl.type)
+ switch (n->norm->Bl.type) {
+ case (LIST_enum):
n->norm->Bl.count = 0;
+ return(1);
+ case (LIST_column):
+ break;
+ default:
+ return(1);
+ }
+
+ print_line(".TS", MMAN_nl);
+ for (icol = 0; icol < n->norm->Bl.ncols; icol++)
+ print_word("l");
+ print_word(".");
+ outflags |= MMAN_nl;
return(1);
}
post_bl(DECL_ARGS)
{
- if (LIST_enum == n->norm->Bl.type)
+ switch (n->norm->Bl.type) {
+ case (LIST_column):
+ print_line(".TE", 0);
+ break;
+ case (LIST_enum):
n->norm->Bl.count = 0;
+ break;
+ default:
+ break;
+ }
+ outflags |= MMAN_PP | MMAN_nl;
+ outflags &= ~(MMAN_sp | MMAN_br);
}
static int
pre_dl(DECL_ARGS)
{
- outflags |= MMAN_nl;
- print_word(".RS 6n");
- outflags |= MMAN_nl;
+ print_line(".RS 6n", MMAN_nl);
return(1);
}
post_dl(DECL_ARGS)
{
- outflags |= MMAN_nl;
- print_word(".RE");
- outflags |= MMAN_nl;
+ print_line(".RE", MMAN_nl);
}
static int
outflags &= ~MMAN_spc;
print_word("(");
outflags &= ~MMAN_spc;
- return(pre_fa(m, n->next));
+
+ n = n->next;
+ if (NULL != n)
+ pre_fa(m, n);
+ return(0);
}
static void
switch (n->type) {
case (MDOC_HEAD):
- outflags |= MMAN_nl;
+ outflags |= MMAN_PP | MMAN_nl;
bln = n->parent->parent;
+ if (0 == bln->norm->Bl.comp ||
+ (NULL == n->parent->prev &&
+ NULL == bln->parent->prev))
+ outflags |= MMAN_sp;
+ outflags &= ~MMAN_br;
switch (bln->norm->Bl.type) {
case (LIST_item):
- if (bln->norm->Bl.comp)
- outflags |= MMAN_br;
- else
- outflags |= MMAN_sp;
return(0);
case (LIST_inset):
/* FALLTHROUGH */
case (LIST_diag):
/* FALLTHROUGH */
case (LIST_ohang):
- if (bln->norm->Bl.comp)
- outflags |= MMAN_br;
- else
- outflags |= MMAN_sp;
if (bln->norm->Bl.type == LIST_diag)
- print_word(".B \"");
+ print_line(".B \"", 0);
else
- print_word(".R \"");
+ print_line(".R \"", 0);
outflags &= ~MMAN_spc;
return(1);
case (LIST_bullet):
case (LIST_dash):
/* FALLTHROUGH */
case (LIST_hyphen):
- print_width(bln->norm->Bl.width, NULL);
+ print_width(bln->norm->Bl.width, NULL, 0);
+ TPremain = 0;
outflags |= MMAN_nl;
font_push('B');
if (LIST_bullet == bln->norm->Bl.type)
font_pop();
break;
case (LIST_enum):
- print_width(bln->norm->Bl.width, NULL);
+ print_width(bln->norm->Bl.width, NULL, 0);
+ TPremain = 0;
outflags |= MMAN_nl;
print_count(&bln->norm->Bl.count);
- outflags |= MMAN_nl;
break;
case (LIST_hang):
- print_width(bln->norm->Bl.width, n->child);
- outflags |= MMAN_nl;
+ print_width(bln->norm->Bl.width, n->child, 6);
+ TPremain = 0;
break;
+ case (LIST_tag):
+ print_width(bln->norm->Bl.width, n->child, 0);
+ putchar('\n');
+ outflags &= ~MMAN_spc;
+ return(1);
default:
- if (bln->norm->Bl.width)
- print_width(bln->norm->Bl.width, n->child);
- break;
+ return(1);
}
outflags |= MMAN_nl;
default:
{
const struct mdoc_node *bln;
- if (MDOC_HEAD == n->type) {
- bln = n->parent->parent;
+ bln = n->parent->parent;
+
+ switch (n->type) {
+ case (MDOC_HEAD):
switch (bln->norm->Bl.type) {
case (LIST_diag):
outflags &= ~MMAN_spc;
default:
break;
}
+ break;
+ case (MDOC_BODY):
+ if (LIST_column == bln->norm->Bl.type &&
+ NULL != n->next) {
+ putchar('\t');
+ outflags &= ~MMAN_spc;
+ }
+ break;
+ default:
+ break;
}
}
static int
pre_nm(DECL_ARGS)
{
+ char *name;
if (MDOC_BLOCK == n->type)
pre_syn(n);
if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
return(1);
- if (NULL == n->child && NULL == m->name)
+ name = n->child ? n->child->string : m->name;
+ if (NULL == name)
return(0);
+ if (MDOC_HEAD == n->type) {
+ if (NULL == n->parent->prev)
+ outflags |= MMAN_sp;
+ print_block(".HP", 0);
+ printf(" %ldn", strlen(name) + 1);
+ outflags |= MMAN_nl;
+ }
font_push('B');
if (NULL == n->child)
print_word(m->name);
pre_pp(DECL_ARGS)
{
- outflags |= MMAN_nl;
- if (MDOC_It == n->parent->tok)
- print_word(".sp");
- else
- print_word(".PP");
- outflags |= MMAN_nl;
- return(MDOC_Rs == n->tok);
+ if (MDOC_It != n->parent->tok)
+ outflags |= MMAN_PP;
+ outflags |= MMAN_sp | MMAN_nl;
+ outflags &= ~MMAN_br;
+ return(0);
+}
+
+static int
+pre_rs(DECL_ARGS)
+{
+
+ if (SEC_SEE_ALSO == n->sec) {
+ outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
+ outflags &= ~MMAN_br;
+ }
+ return(1);
}
static int
pre_sp(DECL_ARGS)
{
- outflags |= MMAN_nl;
- print_word(".sp");
+ if (MMAN_PP & outflags) {
+ outflags &= ~MMAN_PP;
+ print_line(".PP", 0);
+ } else
+ print_line(".sp", 0);
return(1);
}