-/* $Id: mdoc_term.c,v 1.34 2009/07/12 20:07:04 kristaps Exp $ */
+/* $Id: mdoc_term.c,v 1.59 2009/07/23 09:40:25 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
*
#include "mdoc.h"
/* FIXME: macro arguments can be escaped. */
-/* FIXME: support more offset/width tokens. */
#define TTYPE_PROG 0
#define TTYPE_CMD_FLAG 1
#define TTYPE_LINK_ANCHOR 19
#define TTYPE_LINK_TEXT 20
#define TTYPE_REF_JOURNAL 21
-#define TTYPE_LIST 22
-#define TTYPE_NMAX 23
+#define TTYPE_REF_TITLE 22
+#define TTYPE_LIST 23
+#define TTYPE_NMAX 24
const int ttypes[TTYPE_NMAX] = {
TERMP_BOLD, /* TTYPE_PROG */
TERMP_UNDER, /* TTYPE_FUNC_ARG */
TERMP_UNDER, /* TTYPE_LINK */
TERMP_BOLD, /* TTYPE_SSECTION */
- TERMP_UNDER, /* TTYPE_FILE */
+ TERMP_UNDER, /* TTYPE_FILE */
TERMP_UNDER, /* TTYPE_EMPH */
TERMP_BOLD, /* TTYPE_CONFIG */
TERMP_BOLD, /* TTYPE_CMD */
TERMP_UNDER, /* TTYPE_LINK_ANCHOR */
TERMP_BOLD, /* TTYPE_LINK_TEXT */
TERMP_UNDER, /* TTYPE_REF_JOURNAL */
+ TERMP_UNDER, /* TTYPE_REF_TITLE */
TERMP_BOLD /* TTYPE_LIST */
};
};
static void termp____post(DECL_ARGS);
-static void termp__t_post(DECL_ARGS);
static void termp_aq_post(DECL_ARGS);
static void termp_bd_post(DECL_ARGS);
static void termp_bl_post(DECL_ARGS);
static int termp_ap_pre(DECL_ARGS);
static int termp_aq_pre(DECL_ARGS);
static int termp_ar_pre(DECL_ARGS);
-static int termp_at_pre(DECL_ARGS);
static int termp_bd_pre(DECL_ARGS);
static int termp_bf_pre(DECL_ARGS);
static int termp_bq_pre(DECL_ARGS);
+static int termp_br_pre(DECL_ARGS);
static int termp_brq_pre(DECL_ARGS);
static int termp_bt_pre(DECL_ARGS);
static int termp_cd_pre(DECL_ARGS);
static int termp_ic_pre(DECL_ARGS);
static int termp_in_pre(DECL_ARGS);
static int termp_it_pre(DECL_ARGS);
-static int termp_lb_pre(DECL_ARGS);
static int termp_lk_pre(DECL_ARGS);
static int termp_ms_pre(DECL_ARGS);
static int termp_mt_pre(DECL_ARGS);
static int termp_rv_pre(DECL_ARGS);
static int termp_sh_pre(DECL_ARGS);
static int termp_sm_pre(DECL_ARGS);
+static int termp_sp_pre(DECL_ARGS);
static int termp_sq_pre(DECL_ARGS);
static int termp_ss_pre(DECL_ARGS);
-static int termp_st_pre(DECL_ARGS);
static int termp_sx_pre(DECL_ARGS);
static int termp_sy_pre(DECL_ARGS);
static int termp_ud_pre(DECL_ARGS);
{ NULL, NULL }, /* Ot */
{ termp_pa_pre, NULL }, /* Pa */
{ termp_rv_pre, NULL }, /* Rv */
- { termp_st_pre, NULL }, /* St */
+ { NULL, NULL }, /* St */
{ termp_va_pre, NULL }, /* Va */
{ termp_vt_pre, termp_vt_post }, /* Vt */
{ termp_xr_pre, NULL }, /* Xr */
{ NULL, termp____post }, /* %O */
{ NULL, termp____post }, /* %P */
{ NULL, termp____post }, /* %R */
- { termp__t_pre, termp__t_post }, /* %T */
+ { termp__t_pre, termp____post }, /* %T */
{ NULL, termp____post }, /* %V */
{ NULL, NULL }, /* Ac */
{ termp_aq_pre, termp_aq_post }, /* Ao */
{ termp_aq_pre, termp_aq_post }, /* Aq */
- { termp_at_pre, NULL }, /* At */
+ { NULL, NULL }, /* At */
{ NULL, NULL }, /* Bc */
{ termp_bf_pre, NULL }, /* Bf */
{ termp_bq_pre, termp_bq_post }, /* Bo */
{ NULL, NULL }, /* Hf */
{ NULL, NULL }, /* Fr */
{ termp_ud_pre, NULL }, /* Ud */
- { termp_lb_pre, termp_lb_post }, /* Lb */
+ { NULL, termp_lb_post }, /* Lb */
{ termp_pp_pre, NULL }, /* Lp */
{ termp_lk_pre, NULL }, /* Lk */
{ termp_mt_pre, NULL }, /* Mt */
{ NULL, NULL }, /* En */
{ termp_xx_pre, NULL }, /* Dx */
{ NULL, NULL }, /* %Q */
+ { termp_br_pre, NULL }, /* br */
+ { termp_sp_pre, NULL }, /* sp */
};
#ifdef __linux__
static size_t arg_offset(const struct mdoc_argv *);
static size_t arg_width(const struct mdoc_argv *, int);
static int arg_listtype(const struct mdoc_node *);
-static int fmt_block_vspace(struct termp *,
+static void fmt_block_vspace(struct termp *,
const struct mdoc_node *,
const struct mdoc_node *);
static void print_node(DECL_ARGS);
npair.flag = 0;
npair.count = 0;
+ /*
+ * Note on termpair. This allows a pre function to set a termp
+ * flag that is automatically unset after the body, but before
+ * the post function. Thus, if a pre uses a termpair flag, it
+ * must be reapplied in the post for use.
+ */
+
if (MDOC_TEXT != node->type) {
if (termacts[node->tok].pre)
if ( ! (*termacts[node->tok].pre)(p, &npair, meta, node))
if (dochild && node->child)
print_body(p, &npair, meta, node->child);
+ p->flags &= ~npair.flag;
+
/* Post-processing. */
if (MDOC_TEXT != node->type)
p->offset = offset;
p->rmargin = rmargin;
- p->flags &= ~npair.flag;
}
tm = localtime(&meta->date);
- if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
+ if (0 == strftime(buf, p->rmargin, "%B %e, %Y", tm))
err(1, "strftime");
(void)strlcpy(os, meta->os, p->rmargin);
static size_t
arg_width(const struct mdoc_argv *arg, int pos)
{
- size_t v;
int i, len;
+ const char *p;
assert(pos < (int)arg->sz && pos >= 0);
assert(arg->value[pos]);
- if (0 == (len = (int)strlen(arg->value[pos])))
+ p = arg->value[pos];
+
+ if (0 == (len = (int)strlen(p)))
return(0);
for (i = 0; i < len - 1; i++)
- if ( ! isdigit((u_char)arg->value[pos][i]))
+ if ( ! isdigit((u_char)p[i]))
break;
- if (i == len - 1) {
- if ('n' == arg->value[pos][len - 1] ||
- 'm' == arg->value[pos][len - 1]) {
- v = (size_t)atoi(arg->value[pos]);
- return(v + 2);
- }
+ if (i == len - 1)
+ if ('n' == p[len - 1] || 'm' == p[len - 1])
+ return((size_t)atoi(p) + 2);
- }
- return(strlen(arg->value[pos]) + 2);
+ return((size_t)len + 2);
}
/* FALLTHROUGH */
case (MDOC_Column):
/* FALLTHROUGH */
+ case (MDOC_Hang):
+ /* FALLTHROUGH */
case (MDOC_Ohang):
return(n->args->argv[i].arg);
default:
break;
}
- /* FIXME: mandated by parser. */
-
- errx(1, "list type not supported");
- /* NOTREACHED */
+ return(-1);
}
static size_t
arg_offset(const struct mdoc_argv *arg)
{
+ int len, i;
+ const char *p;
assert(*arg->value);
- if (0 == strcmp(*arg->value, "left"))
+ p = *arg->value;
+
+ if (0 == strcmp(p, "left"))
return(0);
- if (0 == strcmp(*arg->value, "indent"))
+ if (0 == strcmp(p, "indent"))
return(INDENT + 1);
- if (0 == strcmp(*arg->value, "indent-two"))
+ if (0 == strcmp(p, "indent-two"))
return((INDENT + 1) * 2);
- /* FIXME: needs to support field-widths (10n, etc.). */
+ if (0 == (len = (int)strlen(p)))
+ return(0);
+
+ for (i = 0; i < len - 1; i++)
+ if ( ! isdigit((u_char)p[i]))
+ break;
+
+ if (i == len - 1)
+ if ('n' == p[len - 1] || 'm' == p[len - 1])
+ return((size_t)atoi(p));
- return(strlen(*arg->value));
+ return((size_t)len);
}
/* ARGSUSED */
-static int
+static void
fmt_block_vspace(struct termp *p,
const struct mdoc_node *bl,
const struct mdoc_node *node)
term_newln(p);
- if (arg_hasattr(MDOC_Compact, bl))
- return(1);
+ if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Compact, bl))
+ return;
+
+ /*
+ * Search through our prior nodes. If we follow a `Ss' or `Sh',
+ * then don't vspace.
+ */
for (n = node; n; n = n->parent) {
if (MDOC_BLOCK != n->type)
continue;
if (MDOC_Ss == n->tok)
- break;
+ return;
if (MDOC_Sh == n->tok)
- break;
+ return;
if (NULL == n->prev)
continue;
- term_vspace(p);
break;
}
- return(1);
+ /*
+ * XXX - not documented: a `-column' does not ever assert vspace
+ * within the list.
+ */
+
+ if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Column, bl))
+ if (node->prev && MDOC_It == node->prev->tok)
+ return;
+
+ /*
+ * XXX - not documented: a `-diag' without a body does not
+ * assert a vspace prior to the next element.
+ */
+ if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Diag, bl))
+ if (node->prev && MDOC_It == node->prev->tok) {
+ assert(node->prev->body);
+ if (NULL == node->prev->body->child)
+ return;
+ }
+
+ term_vspace(p);
}
{
const struct mdoc_node *bl, *n;
char buf[7];
- int i, type, keys[3], vals[3], sv;
+ int i, type, keys[3], vals[3];
size_t width, offset;
- if (MDOC_BLOCK == node->type)
- return(fmt_block_vspace(p, node->parent->parent, node));
+ if (MDOC_BLOCK == node->type) {
+ fmt_block_vspace(p, node->parent->parent, node);
+ return(1);
+ }
bl = node->parent->parent->parent;
(void)arg_getattrs(keys, vals, 3, bl);
type = arg_listtype(bl);
+ assert(-1 != type);
/* Calculate real width and offset. */
case (MDOC_Column):
if (MDOC_BODY == node->type)
break;
- for (i = 0, n = node->prev; n; n = n->prev, i++)
+ /*
+ * Work around groff's column handling. The offset is
+ * equal to the sum of all widths leading to the current
+ * column (plus the -offset value). If this column
+ * exceeds the stated number of columns, the width is
+ * set as 0, else it's the stated column width (later
+ * the 0 will be adjusted to default 10 or, if in the
+ * last column case, set to stretch to the margin).
+ */
+ for (i = 0, n = node->prev; n && n &&
+ i < (int)bl->args[vals[2]].argv->sz;
+ n = n->prev, i++)
offset += arg_width
(&bl->args->argv[vals[2]], i);
- assert(i < (int)bl->args->argv[vals[2]].sz);
- width = arg_width(&bl->args->argv[vals[2]], i);
+
+ /* Whether exceeds maximum column. */
+ if (i < (int)bl->args[vals[2]].argv->sz)
+ width = arg_width(&bl->args->argv[vals[2]], i);
+ else
+ width = 0;
+
if (vals[1] >= 0)
offset += arg_offset(&bl->args->argv[vals[1]]);
break;
/*
* List-type can override the width in the case of fixed-head
* values (bullet, dash/hyphen, enum). Tags need a non-zero
- * offset.
+ * offset. FIXME: double-check that correct.
*/
switch (type) {
if (width < 5)
width = 5;
break;
+ case (MDOC_Hang):
+ if (0 == width)
+ width = 8;
+ break;
+ case (MDOC_Column):
+ /* FALLTHROUGH */
case (MDOC_Tag):
if (0 == width)
width = 10;
* while diagonal bodies need two.
*/
+ p->flags |= TERMP_NOSPACE;
+
switch (type) {
case (MDOC_Diag):
- term_word(p, "\\ ");
- /* FALLTHROUGH */
+ term_word(p, "\\ \\ ");
+ break;
case (MDOC_Inset):
if (MDOC_BODY == node->type)
- p->flags &= ~TERMP_NOSPACE;
- else
- p->flags |= TERMP_NOSPACE;
+ term_word(p, "\\ ");
break;
default:
- p->flags |= TERMP_NOSPACE;
break;
}
+ p->flags |= TERMP_NOSPACE;
+
/*
* Style flags. Diagnostic heads need TTYPE_DIAG.
*/
case (MDOC_Enum):
/* FALLTHROUGH */
case (MDOC_Hyphen):
- /* FALLTHROUGH */
- case (MDOC_Tag):
if (MDOC_HEAD == node->type)
p->flags |= TERMP_NOBREAK;
else
p->flags |= TERMP_NOLPAD;
- if (MDOC_HEAD == node->type && MDOC_Tag == type)
- if (NULL == node->next ||
- NULL == node->next->child)
- p->flags |= TERMP_NONOBREAK;
+ break;
+ case (MDOC_Hang):
+ if (MDOC_HEAD == node->type)
+ p->flags |= TERMP_NOBREAK;
+ else
+ p->flags |= TERMP_NOLPAD;
+
+ if (MDOC_HEAD != node->type)
+ break;
+
+ /*
+ * This is ugly. If `-hang' is specified and the body
+ * is a `Bl' or `Bd', then we want basically to nullify
+ * the "overstep" effect in term_flushln() and treat
+ * this as a `-ohang' list instead.
+ */
+ if (node->next->child &&
+ (MDOC_Bl == node->next->child->tok ||
+ MDOC_Bd == node->next->child->tok)) {
+ p->flags &= ~TERMP_NOBREAK;
+ p->flags &= ~TERMP_NOLPAD;
+ } else
+ p->flags |= TERMP_HANG;
+ break;
+ case (MDOC_Tag):
+ if (MDOC_HEAD == node->type)
+ p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
+ else
+ p->flags |= TERMP_NOLPAD;
+
+ if (MDOC_HEAD != node->type)
+ break;
+ if (NULL == node->next || NULL == node->next->child)
+ p->flags |= TERMP_DANGLE;
break;
case (MDOC_Column):
if (MDOC_HEAD == node->type) {
p->offset += offset;
switch (type) {
+ case (MDOC_Hang):
+ /*
+ * Same stipulation as above, regarding `-hang'. We
+ * don't want to recalculate rmargin and offsets when
+ * using `Bd' or `Bl' within `-hang' overstep lists.
+ */
+ if (MDOC_HEAD == node->type && node->next->child &&
+ (MDOC_Bl == node->next->child->tok ||
+ MDOC_Bd == node->next->child->tok))
+ break;
+ /* FALLTHROUGH */
case (MDOC_Bullet):
/* FALLTHROUGH */
case (MDOC_Dash):
case (MDOC_Hyphen):
/* FALLTHROUGH */
case (MDOC_Tag):
+ assert(width);
if (MDOC_HEAD == node->type)
p->rmargin = p->offset + width;
else
p->offset += width;
break;
case (MDOC_Column):
+ assert(width);
p->rmargin = p->offset + width;
+ /*
+ * XXX - this behaviour is not documented: the
+ * right-most column is filled to the right margin.
+ */
+ if (MDOC_HEAD == node->type &&
+ MDOC_BODY == node->next->type)
+ p->rmargin = p->maxrmargin;
break;
default:
break;
* HEAD character (temporarily bold, in some cases).
*/
- sv = p->flags;
if (MDOC_HEAD == node->type)
switch (type) {
case (MDOC_Bullet):
p->flags |= TERMP_BOLD;
term_word(p, "\\[bu]");
+ p->flags &= ~TERMP_BOLD;
break;
case (MDOC_Dash):
/* FALLTHROUGH */
case (MDOC_Hyphen):
p->flags |= TERMP_BOLD;
term_word(p, "\\(hy");
+ p->flags &= ~TERMP_BOLD;
break;
case (MDOC_Enum):
(pair->ppair->ppair->count)++;
break;
}
- p->flags = sv; /* Restore saved flags. */
-
/*
* If we're not going to process our children, indicate so here.
*/
return;
type = arg_listtype(node->parent->parent->parent);
+ assert(-1 != type);
switch (type) {
- case (MDOC_Diag):
- /* FALLTHROUGH */
case (MDOC_Item):
/* FALLTHROUGH */
+ case (MDOC_Diag):
+ /* FALLTHROUGH */
case (MDOC_Inset):
if (MDOC_BODY == node->type)
term_flushln(p);
}
-/* ARGSUSED */
-static int
-termp_st_pre(DECL_ARGS)
-{
- const char *cp;
-
- if (node->child && (cp = mdoc_a2st(node->child->string)))
- term_word(p, cp);
- return(0);
-}
-
-
/* ARGSUSED */
static int
termp_rs_pre(DECL_ARGS)
if (MDOC_BODY != node->type)
return(1);
- /*
- * XXX: signed off by jmc@openbsd.org. This technically
- * produces a minus sign after the Nd, which is wrong, but is
- * consistent with the historic OpenBSD tmac file.
- */
#if defined(__OpenBSD__) || defined(__linux__)
- term_word(p, "\\-");
+ term_word(p, "\\(en");
#else
term_word(p, "\\(em");
#endif
}
-/* ARGSUSED */
-static int
-termp_lb_pre(DECL_ARGS)
-{
- const char *lb;
-
- assert(node->child && MDOC_TEXT == node->child->type);
- lb = mdoc_a2lib(node->child->string);
- if (lb) {
- term_word(p, lb);
- return(0);
- }
- term_word(p, "library");
- return(1);
-}
-
-
/* ARGSUSED */
static void
termp_lb_post(DECL_ARGS)
* line. Blank lines are allowed.
*/
- if (MDOC_BLOCK == node->type)
- return(fmt_block_vspace(p, node, node));
- else if (MDOC_BODY != node->type)
+ if (MDOC_BLOCK == node->type) {
+ fmt_block_vspace(p, node, node);
+ return(1);
+ } else if (MDOC_BODY != node->type)
return(1);
/* FIXME: display type should be mandated by parser. */
* Ew.
*/
- p->flags |= TERMP_LITERAL;
ln = node->child ? node->child->line : 0;
for (node = node->child; node; node = node->next) {
if (MDOC_BODY != node->type)
return;
-
term_flushln(p);
- p->flags &= ~TERMP_LITERAL;
- p->flags |= TERMP_NOSPACE;
}
}
+/* ARGSUSED */
+static int
+termp_pa_pre(DECL_ARGS)
+{
+
+ pair->flag |= ttypes[TTYPE_FILE];
+ return(1);
+}
+
+
/* ARGSUSED */
static int
termp_pf_pre(DECL_ARGS)
}
-/* ARGSUSED */
-static int
-termp_pa_pre(DECL_ARGS)
-{
-
- pair->flag |= ttypes[TTYPE_FILE];
- return(1);
-}
-
-
/* ARGSUSED */
static int
termp_em_pre(DECL_ARGS)
termp_in_post(DECL_ARGS)
{
- p->flags |= TERMP_NOSPACE;
+ p->flags |= TERMP_NOSPACE | ttypes[TTYPE_INCLUDE];
term_word(p, ">");
+ p->flags &= ~ttypes[TTYPE_INCLUDE];
if (SEC_SYNOPSIS != node->sec)
return;
/* ARGSUSED */
static int
-termp_at_pre(DECL_ARGS)
+termp_sp_pre(DECL_ARGS)
{
- const char *att;
+ int i, len;
- att = NULL;
+ if (NULL == node->child) {
+ term_vspace(p);
+ return(0);
+ }
- if (node->child)
- att = mdoc_a2att(node->child->string);
- if (NULL == att)
- att = "AT&T UNIX";
+ len = atoi(node->child->string);
+ if (0 == len)
+ term_newln(p);
+ for (i = 0; i < len; i++)
+ term_vspace(p);
- term_word(p, att);
return(0);
}
+/* ARGSUSED */
+static int
+termp_br_pre(DECL_ARGS)
+{
+
+ term_newln(p);
+ return(1);
+}
+
+
/* ARGSUSED */
static int
termp_brq_pre(DECL_ARGS)
termp__t_pre(DECL_ARGS)
{
- term_word(p, "\"");
- p->flags |= TERMP_NOSPACE;
+ pair->flag |= ttypes[TTYPE_REF_TITLE];
return(1);
}
-/* ARGSUSED */
-static void
-termp__t_post(DECL_ARGS)
-{
-
- p->flags |= TERMP_NOSPACE;
- term_word(p, "\"");
- termp____post(p, pair, meta, node);
-}
-
-
/* ARGSUSED */
static void
termp____post(DECL_ARGS)