-/* $Id: mdoc_man.c,v 1.41 2012/07/16 10:45:41 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.57 2013/12/25 22:00:45 schwarze Exp $ */
/*
- * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#include "mdoc.h"
#include "main.h"
-#define DECL_ARGS const struct mdoc_meta *m, \
+#define DECL_ARGS const struct mdoc_meta *meta, \
const struct mdoc_node *n
struct manact {
static int cond_head(DECL_ARGS);
static void font_push(char);
static void font_pop(void);
+static void mid_it(void);
static void post__t(DECL_ARGS);
static void post_bd(DECL_ARGS);
static void post_bf(DECL_ARGS);
{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
{ NULL, NULL, NULL, NULL, NULL }, /* Db */
{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
- { cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
- { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
+ { cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
+ { cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
- { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
+ { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
{ NULL, NULL, NULL, NULL, NULL }, /* Re */
{ 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 */
+ { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
+ { cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
#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" */
+#define MMAN_Bk_susp (1 << 8) /* suspend this (after a macro) */
+#define MMAN_An_split (1 << 9) /* author mode is "split" */
+#define MMAN_An_nosplit (1 << 10) /* author mode is "nosplit" */
+#define MMAN_PD (1 << 11) /* inter-paragraph spacing disabled */
+#define MMAN_nbrword (1 << 12) /* do not break the next word */
+#define BL_STACK_MAX 32
+
+static size_t Bl_stack[BL_STACK_MAX]; /* offsets [chars] */
+static int Bl_stack_post[BL_STACK_MAX]; /* add final .RE */
+static int Bl_stack_len; /* number of nested Bl blocks */
static int TPremain; /* characters before tag is full */
static struct {
* If we need a newline, print it now and start afresh.
*/
if (MMAN_PP & outflags) {
- if ( ! (MMAN_sp & outflags))
- printf("\n.sp -1v");
+ if (MMAN_sp & outflags) {
+ if (MMAN_PD & outflags) {
+ printf("\n.PD");
+ outflags &= ~MMAN_PD;
+ }
+ } else if ( ! (MMAN_PD & outflags)) {
+ printf("\n.PD 0");
+ outflags |= MMAN_PD;
+ }
printf("\n.PP\n");
} else if (MMAN_sp & outflags)
printf("\n.sp\n");
*/
if (MMAN_spc_force & outflags || '\0' == s[0] ||
NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
- if (MMAN_Bk & outflags) {
+ if (MMAN_Bk & outflags &&
+ ! (MMAN_Bk_susp & outflags))
putchar('\\');
- putchar('~');
- } else
- putchar(' ');
+ putchar(' ');
if (TPremain)
TPremain--;
}
outflags |= MMAN_spc;
else
outflags &= ~MMAN_spc;
- outflags &= ~MMAN_spc_force;
+ outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
for ( ; *s; s++) {
switch (*s) {
case (ASCII_NBRSP):
- printf("\\~");
+ printf("\\ ");
break;
case (ASCII_HYPH):
putchar('-');
break;
+ case (' '):
+ if (MMAN_nbrword & outflags) {
+ printf("\\ ");
+ break;
+ }
+ /* FALLTHROUGH */
default:
putchar((unsigned char)*s);
break;
if (TPremain)
TPremain--;
}
+ outflags &= ~MMAN_nbrword;
}
static void
{
outflags &= ~MMAN_PP;
- if (MMAN_sp & outflags)
+ if (MMAN_sp & outflags) {
outflags &= ~(MMAN_sp | MMAN_br);
- else
- print_line(".sp -1v", 0);
+ if (MMAN_PD & outflags) {
+ print_line(".PD", 0);
+ outflags &= ~MMAN_PD;
+ }
+ } else if (! (MMAN_PD & outflags))
+ print_line(".PD 0", MMAN_PD);
outflags |= MMAN_nl;
print_word(s);
- outflags |= newflags;
+ outflags |= MMAN_Bk_susp | newflags;
}
static void
struct roffsu su;
size_t sz;
+ print_line(".RS", MMAN_Bk_susp);
+
+ /* Convert v into a number (of characters). */
if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
sz = 0;
else if (0 == strcmp(v, "indent"))
else if (0 == strcmp(v, "indent-two"))
sz = 12;
else if (a2roffsu(v, &su, SCALE_MAX)) {
- print_word(v);
- return;
+ if (SCALE_EN == su.unit)
+ sz = su.scale;
+ else {
+ /*
+ * XXX
+ * If we are inside an enclosing list,
+ * there is no easy way to add the two
+ * indentations because they are provided
+ * in terms of different units.
+ */
+ print_word(v);
+ outflags |= MMAN_nl;
+ return;
+ }
} else
sz = strlen(v);
- snprintf(buf, sizeof(buf), "%ldn", sz);
+ /*
+ * We are inside an enclosing list.
+ * Add the two indentations.
+ */
+ if (Bl_stack_len)
+ sz += Bl_stack[Bl_stack_len - 1];
+
+ snprintf(buf, sizeof(buf), "%zun", sz);
print_word(buf);
+ outflags |= MMAN_nl;
}
+/*
+ * Set up the indentation for a list item; used from pre_it().
+ */
void
print_width(const char *v, const struct mdoc_node *child, size_t defsz)
{
numeric = 1;
remain = 0;
+
+ /* Convert v into a number (of characters). */
if (NULL == v)
sz = defsz;
else if (a2roffsu(v, &su, SCALE_MAX)) {
chsz = (NULL != child && MDOC_TEXT == child->type) ?
strlen(child->string) : 0;
+ /* Maybe we are inside an enclosing list? */
+ mid_it();
+
+ /*
+ * Save our own indentation,
+ * such that child lists can use it.
+ */
+ Bl_stack[Bl_stack_len++] = sz + 2;
+
+ /* Set up the current list. */
if (defsz && chsz > sz)
print_block(".HP", 0);
else {
remain = sz + 2;
}
if (numeric) {
- snprintf(buf, sizeof(buf), "%ldn", sz + 2);
+ snprintf(buf, sizeof(buf), "%zun", sz + 2);
print_word(buf);
} else
print_word(v);
void
man_mdoc(void *arg, const struct mdoc *mdoc)
{
- const struct mdoc_meta *m;
+ const struct mdoc_meta *meta;
const struct mdoc_node *n;
- m = mdoc_meta(mdoc);
+ meta = mdoc_meta(mdoc);
n = mdoc_node(mdoc);
- printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
- m->title, m->msec, m->date, m->os, m->vol);
+ printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
+ meta->title, meta->msec, meta->date,
+ meta->os, meta->vol);
+
+ /* Disable hyphenation and if nroff, disable justification. */
+ printf(".nh\n.if n .ad l");
outflags = MMAN_nl | MMAN_Sm;
if (0 == fontqueue.size) {
fontqueue.head = fontqueue.tail = mandoc_malloc(8);
*fontqueue.tail = 'R';
}
- print_node(m, n);
+ print_node(meta, n);
putchar('\n');
}
static void
print_node(DECL_ARGS)
{
- const struct mdoc_node *prev, *sub;
+ const struct mdoc_node *sub;
const struct manact *act;
int cond, do_sub;
-
+
/*
* Break the line if we were parsed subsequent the current node.
* This makes the page structure be more consistent.
*/
- prev = n->prev ? n->prev : n->parent;
- if (MMAN_spc & outflags && prev && prev->line < n->line)
+ if (MMAN_spc & outflags && MDOC_LINE & n->flags)
outflags |= MMAN_nl;
act = NULL;
* node.
*/
act = manacts + n->tok;
- cond = NULL == act->cond || (*act->cond)(m, n);
+ cond = NULL == act->cond || (*act->cond)(meta, n);
if (cond && act->pre)
- do_sub = (*act->pre)(m, n);
+ do_sub = (*act->pre)(meta, n);
}
/*
*/
if (do_sub)
for (sub = n->child; sub; sub = sub->next)
- print_node(m, sub);
+ print_node(meta, sub);
/*
* Lastly, conditionally run the post-node handler.
*/
if (cond && act->post)
- (*act->post)(m, n);
+ (*act->post)(meta, n);
}
static int
putchar('\"');
} else
font_pop();
- post_percent(m, n);
+ post_percent(meta, n);
}
/*
pre_sect(DECL_ARGS)
{
- if (MDOC_HEAD != n->type)
- return(1);
- outflags |= MMAN_sp;
- print_block(manacts[n->tok].prefix, 0);
- print_word("");
- putchar('\"');
- outflags &= ~MMAN_spc;
+ if (MDOC_HEAD == n->type) {
+ outflags |= MMAN_sp;
+ print_block(manacts[n->tok].prefix, 0);
+ print_word("");
+ putchar('\"');
+ outflags &= ~MMAN_spc;
+ }
return(1);
}
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)
{
+ /* Close out this display. */
print_line(".RE", MMAN_nl);
if (DISP_unfilled == n->norm->Bd.type ||
DISP_literal == n->norm->Bd.type)
print_line(".fi", MMAN_nl);
+
+ /* Maybe we are inside an enclosing list? */
+ if (NULL != n->parent->next)
+ mid_it();
}
static int
{
size_t icol;
+ /*
+ * print_offs() will increase the -offset to account for
+ * a possible enclosing .It, but any enclosed .It blocks
+ * just nest and do not add up their indentation.
+ */
+ if (n->norm->Bl.offs) {
+ print_offs(n->norm->Bl.offs);
+ Bl_stack[Bl_stack_len++] = 0;
+ }
+
switch (n->norm->Bl.type) {
case (LIST_enum):
n->norm->Bl.count = 0;
default:
break;
}
- outflags |= MMAN_PP | MMAN_nl;
- outflags &= ~(MMAN_sp | MMAN_br);
+
+ if (n->norm->Bl.offs) {
+ print_line(".RE", MMAN_nl);
+ assert(Bl_stack_len);
+ Bl_stack_len--;
+ assert(0 == Bl_stack[Bl_stack_len]);
+ } else {
+ outflags |= MMAN_PP | MMAN_nl;
+ outflags &= ~(MMAN_sp | MMAN_br);
+ }
+
+ /* Maybe we are inside an enclosing list? */
+ if (NULL != n->parent->next)
+ mid_it();
+
}
static int
pre_dl(DECL_ARGS)
{
- print_line(".RS 6n", MMAN_nl);
+ print_offs("6n");
return(1);
}
{
print_line(".RE", MMAN_nl);
+
+ /* Maybe we are inside an enclosing list? */
+ if (NULL != n->parent->next)
+ mid_it();
}
static int
static int
pre_fa(DECL_ARGS)
{
+ int am_Fa;
+
+ am_Fa = MDOC_Fa == n->tok;
- if (MDOC_Fa == n->tok)
+ if (am_Fa)
n = n->child;
while (NULL != n) {
font_push('I');
- print_node(m, n);
+ if (am_Fa || MDOC_SYNPRETTY & n->flags)
+ outflags |= MMAN_nbrword;
+ print_node(meta, n);
font_pop();
if (NULL != (n = n->next))
print_word(",");
{
font_push('B');
- print_word("-");
+ print_word("\\-");
outflags &= ~MMAN_spc;
return(1);
}
if (NULL == n)
return(0);
+ if (MDOC_SYNPRETTY & n->flags)
+ print_block(".HP 4n", MMAN_nl);
+
font_push('B');
- print_node(m, n);
+ print_node(meta, n);
font_pop();
outflags &= ~MMAN_spc;
print_word("(");
n = n->next;
if (NULL != n)
- pre_fa(m, n);
+ pre_fa(meta, n);
return(0);
}
print_word(")");
if (MDOC_SYNPRETTY & n->flags) {
print_word(";");
- outflags |= MMAN_br;
+ outflags |= MMAN_PP;
}
}
pre_syn(n);
break;
case (MDOC_HEAD):
+ if (MDOC_SYNPRETTY & n->flags)
+ print_block(".HP 4n", MMAN_nl);
font_push('B');
break;
case (MDOC_BODY):
}
return(1);
}
-
+
static void
post_fo(DECL_ARGS)
{
font_pop();
break;
case (MDOC_BODY):
- post_fn(m, n);
+ post_fn(meta, n);
break;
default:
break;
return(1);
}
+/*
+ * This function is called after closing out an indented block.
+ * If we are inside an enclosing list, restore its indentation.
+ */
+static void
+mid_it(void)
+{
+ char buf[24];
+
+ /* Nothing to do outside a list. */
+ if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
+ return;
+
+ /* The indentation has already been set up. */
+ if (Bl_stack_post[Bl_stack_len - 1])
+ return;
+
+ /* Restore the indentation of the enclosing list. */
+ print_line(".RS", MMAN_Bk_susp);
+ snprintf(buf, sizeof(buf), "%zun", Bl_stack[Bl_stack_len - 1]);
+ print_word(buf);
+
+ /* Remeber to close out this .RS block later. */
+ Bl_stack_post[Bl_stack_len - 1] = 1;
+}
+
static void
post_it(DECL_ARGS)
{
}
break;
case (MDOC_BODY):
- if (LIST_column == bln->norm->Bl.type &&
- NULL != n->next) {
- putchar('\t');
- outflags &= ~MMAN_spc;
+ switch (bln->norm->Bl.type) {
+ case (LIST_bullet):
+ /* FALLTHROUGH */
+ case (LIST_dash):
+ /* FALLTHROUGH */
+ case (LIST_hyphen):
+ /* FALLTHROUGH */
+ case (LIST_enum):
+ /* FALLTHROUGH */
+ case (LIST_hang):
+ /* FALLTHROUGH */
+ case (LIST_tag):
+ assert(Bl_stack_len);
+ Bl_stack[--Bl_stack_len] = 0;
+
+ /*
+ * Our indentation had to be restored
+ * after a child display or child list.
+ * Close out that indentation block now.
+ */
+ if (Bl_stack_post[Bl_stack_len]) {
+ print_line(".RE", MMAN_nl);
+ Bl_stack_post[Bl_stack_len] = 0;
+ }
+ break;
+ case (LIST_column):
+ if (NULL != n->next) {
+ putchar('\t');
+ outflags &= ~MMAN_spc;
+ }
+ break;
+ default:
+ break;
}
break;
default:
{
char *name;
- if (MDOC_BLOCK == n->type)
+ if (MDOC_BLOCK == n->type) {
+ outflags |= MMAN_Bk;
pre_syn(n);
+ }
if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
return(1);
- name = n->child ? n->child->string : m->name;
+ name = n->child ? n->child->string : meta->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);
+ printf(" %zun", strlen(name) + 1);
outflags |= MMAN_nl;
}
font_push('B');
if (NULL == n->child)
- print_word(m->name);
+ print_word(meta->name);
return(1);
}
post_nm(DECL_ARGS)
{
- if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
- return;
- font_pop();
+ switch (n->type) {
+ case (MDOC_BLOCK):
+ outflags &= ~MMAN_Bk;
+ break;
+ case (MDOC_HEAD):
+ /* FALLTHROUGH */
+ case (MDOC_ELEM):
+ font_pop();
+ break;
+ default:
+ break;
+ }
}
static int
n = n->child;
if (NULL == n)
return(0);
- print_node(m, n);
+ print_node(meta, n);
n = n->next;
if (NULL == n)
return(0);
outflags &= ~MMAN_spc;
print_word("(");
- print_node(m, n);
+ print_node(meta, n);
print_word(")");
return(0);
}
if (NULL == n->child)
return(0);
outflags &= ~MMAN_spc;
- print_word("\\~");
+ print_word("\\ ");
outflags &= ~MMAN_spc;
return(1);
}