-/* $Id: term.c,v 1.19 2009/02/25 12:27:37 kristaps Exp $ */
+/* $Id: term.c,v 1.29 2009/02/28 20:13:06 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se>
*
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "term.h"
-#define INDENT 4
+#define INDENT 6
/*
* Performs actions on nodes of the abstract syntax tree. Both pre- and
*/
/* FIXME: indent/tab. */
-/* FIXME: handle nested lists. */
+/* FIXME: macro arguments can be escaped. */
#define TTYPE_PROG 0
#define TTYPE_CMD_FLAG 1
DECL_PRE(name); \
DECL_POST(name);
+DECL_PREPOST(termp__t);
DECL_PREPOST(termp_aq);
DECL_PREPOST(termp_bd);
DECL_PREPOST(termp_bq);
DECL_PRE(termp_ox);
DECL_PRE(termp_pa);
DECL_PRE(termp_pp);
+DECL_PRE(termp_rs);
DECL_PRE(termp_rv);
+DECL_PRE(termp_sm);
DECL_PRE(termp_st);
DECL_PRE(termp_sx);
DECL_PRE(termp_sy);
DECL_PRE(termp_va);
DECL_PRE(termp_xr);
+DECL_POST(termp___);
DECL_POST(termp_bl);
const struct termact __termacts[MDOC_MAX] = {
{ termp_ss_pre, termp_ss_post }, /* Ss */
{ termp_pp_pre, NULL }, /* Pp */
{ termp_d1_pre, termp_d1_post }, /* D1 */
- { NULL, NULL }, /* Dl */
+ { termp_d1_pre, termp_d1_post }, /* Dl */
{ termp_bd_pre, termp_bd_post }, /* Bd */
{ NULL, NULL }, /* Ed */
{ NULL, termp_bl_post }, /* Bl */
{ termp_va_pre, NULL }, /* Va */
{ termp_vt_pre, termp_vt_post }, /* Vt */
{ termp_xr_pre, NULL }, /* Xr */
- { NULL, NULL }, /* %A */
- { NULL, NULL }, /* %B */
- { NULL, NULL }, /* %D */
- { NULL, NULL }, /* %I */
- { NULL, NULL }, /* %J */
- { NULL, NULL }, /* %N */
- { NULL, NULL }, /* %O */
- { NULL, NULL }, /* %P */
- { NULL, NULL }, /* %R */
- { NULL, NULL }, /* %T */
- { NULL, NULL }, /* %V */
+ { NULL, termp____post }, /* %A */
+ { NULL, termp____post }, /* %B */
+ { NULL, termp____post }, /* %D */
+ { NULL, termp____post }, /* %I */
+ { NULL, termp____post }, /* %J */
+ { NULL, termp____post }, /* %N */
+ { NULL, termp____post }, /* %O */
+ { NULL, termp____post }, /* %P */
+ { NULL, termp____post }, /* %R */
+ { termp__t_pre, termp__t_post }, /* %T */
+ { NULL, termp____post }, /* %V */
{ NULL, NULL }, /* Ac */
{ termp_aq_pre, termp_aq_post }, /* Ao */
{ termp_aq_pre, termp_aq_post }, /* Aq */
{ termp_qq_pre, termp_qq_post }, /* Qo */
{ termp_qq_pre, termp_qq_post }, /* Qq */
{ NULL, NULL }, /* Re */
- { NULL, NULL }, /* Rs */
+ { termp_rs_pre, NULL }, /* Rs */
{ NULL, NULL }, /* Sc */
{ termp_sq_pre, termp_sq_post }, /* So */
{ termp_sq_pre, termp_sq_post }, /* Sq */
- { NULL, NULL }, /* Sm */
+ { termp_sm_pre, NULL }, /* Sm */
{ termp_sx_pre, NULL }, /* Sx */
{ termp_sy_pre, NULL }, /* Sy */
{ NULL, NULL }, /* Tn */
static size_t
arg_width(const struct mdoc_arg *arg)
{
+ size_t v;
+ int i, len;
- /* TODO */
assert(*arg->value);
- return(strlen(*arg->value));
+ if (0 == strcmp(*arg->value, "indent"))
+ return(INDENT);
+ if (0 == strcmp(*arg->value, "indent-two"))
+ return(INDENT * 2);
+
+ len = (int)strlen(*arg->value);
+ assert(len > 0);
+
+ for (i = 0; i < len - 1; i++)
+ if ( ! isdigit((int)(*arg->value)[i]))
+ break;
+
+ if (i == len - 1) {
+ if ('n' == (*arg->value)[len - 1]) {
+ v = (size_t)atoi(*arg->value);
+ return(v);
+ }
+
+ }
+ return(strlen(*arg->value) + 2);
}
return(INDENT);
if (0 == strcmp(*arg->value, "indent-two"))
return(INDENT * 2);
-
return(strlen(*arg->value));
}
/* ARGSUSED */
-static void
-termp_it_post(DECL_ARGS)
+static int
+termp_it_pre(DECL_ARGS)
{
const struct mdoc_node *n, *it;
const struct mdoc_block *bl;
- int i;
+ char buf[7], *tp;
+ int i, type;
size_t width, offset;
- /*
- * This (and termp_it_pre()) are the most complicated functions
- * here. They must account for a considerable number of
- * switches that completely change the output behaviour, like
- * -tag versus -column. Yech.
- */
-
switch (node->type) {
case (MDOC_BODY):
/* FALLTHROUGH */
case (MDOC_HEAD):
+ it = node->parent;
+ break;
+ case (MDOC_BLOCK):
+ it = node;
break;
default:
- return;
+ return(1);
}
- it = node->parent;
- assert(MDOC_BLOCK == it->type);
- assert(MDOC_It == it->tok);
-
- n = it->parent;
- assert(MDOC_BODY == n->type);
- assert(MDOC_Bl == n->tok);
- n = n->parent;
+ n = it->parent->parent;
bl = &n->data.block;
- /* If `-tag', adjust our margins accordingly. */
+ if (MDOC_BLOCK == node->type) {
+ newln(p);
+ if ( ! arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
+ if (node->prev || n->prev)
+ vspace(p);
+ return(1);
+ }
- if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
- flushln(p);
+ /* Get our list type. */
+
+ for (type = -1, i = 0; i < (int)bl->argc; i++)
+ switch (bl->argv[i].arg) {
+ case (MDOC_Bullet):
+ /* FALLTHROUGH */
+ case (MDOC_Dash):
+ /* FALLTHROUGH */
+ case (MDOC_Enum):
+ /* FALLTHROUGH */
+ case (MDOC_Hyphen):
+ /* FALLTHROUGH */
+ case (MDOC_Tag):
+ /* FALLTHROUGH */
+ case (MDOC_Ohang):
+ type = bl->argv[i].arg;
+ i = (int)bl->argc;
+ break;
+ default:
+ errx(1, "list type not supported");
+ /* NOTREACHED */
+ }
- /* FIXME: this should auto-size. */
- i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
- width = i >= 0 ? arg_width(&bl->argv[i]) : 10;
+ assert(-1 != type);
- /* FIXME: nesting! Should happen at block. */
- i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
- offset = i >= 0 ? arg_width(&bl->argv[i]) : 0;
+ /* Save our existing (inherited) margin and offset. */
- if (MDOC_HEAD == node->type) {
- p->rmargin = p->maxrmargin;
- p->offset -= offset;
- p->flags &= ~TERMP_NOBREAK;
- } else {
- p->offset -= width;
- p->flags &= ~TERMP_NOLPAD;
- }
- }
-
- if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
- i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
- offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
+ pair->offset = p->offset;
+ pair->rmargin = p->rmargin;
- flushln(p);
- p->offset -= offset;
- return;
- }
-}
+ /* Get list width and offset. */
+ i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
+ width = i >= 0 ? arg_width(&bl->argv[i]) : 0;
-/* ARGSUSED */
-static int
-termp_it_pre(DECL_ARGS)
-{
- const struct mdoc_node *n, *it;
- const struct mdoc_block *bl;
- int i;
- size_t width, offset;
+ i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
+ offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
- /*
- * Also see termp_it_post() for general comments.
- */
+ /* Override the width. */
- switch (node->type) {
- case (MDOC_BODY):
+ switch (type) {
+ case (MDOC_Bullet):
/* FALLTHROUGH */
- case (MDOC_HEAD):
- it = node->parent;
+ case (MDOC_Dash):
+ /* FALLTHROUGH */
+ case (MDOC_Enum):
+ /* FALLTHROUGH */
+ case (MDOC_Hyphen):
+ width = width > 6 ? width : 6;
break;
- case (MDOC_BLOCK):
- it = node;
+ case (MDOC_Tag):
+ /* FIXME: auto-size. */
+ if (0 == width)
+ errx(1, "need non-zero -width");
break;
default:
- return(1);
+ break;
}
- assert(MDOC_BLOCK == it->type);
- assert(MDOC_It == it->tok);
-
- n = it->parent;
- assert(MDOC_BODY == n->type);
- assert(MDOC_Bl == n->tok);
- n = n->parent;
- bl = &n->data.block;
+ /* Word-wrap control. */
- /* If `-compact', don't assert vertical space. */
+ p->flags |= TERMP_NOSPACE;
- if (MDOC_BLOCK == node->type) {
- if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv))
- newln(p);
- else
- vspace(p);
- return(1);
+ switch (type) {
+ case (MDOC_Bullet):
+ /* FALLTHROUGH */
+ case (MDOC_Dash):
+ /* FALLTHROUGH */
+ case (MDOC_Enum):
+ /* FALLTHROUGH */
+ case (MDOC_Hyphen):
+ /* FALLTHROUGH */
+ case (MDOC_Tag):
+ if (MDOC_HEAD == node->type)
+ p->flags |= TERMP_NOBREAK;
+ else if (MDOC_BODY == node->type)
+ p->flags |= TERMP_NOLPAD;
+ break;
+ default:
+ break;
}
- assert(MDOC_HEAD == node->type
- || MDOC_BODY == node->type);
-
- /* FIXME: see termp_it_post(). */
-
- /* If `-tag', adjust our margins accordingly. */
+ /*
+ * Get a token to use as the HEAD lead-in. If NULL, we use the
+ * HEAD child.
+ */
- if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) {
- p->flags |= TERMP_NOSPACE;
+ tp = NULL;
+
+ if (MDOC_HEAD == node->type) {
+ if (arg_hasattr(MDOC_Bullet, bl->argc, bl->argv))
+ tp = "\\[bu]";
+ if (arg_hasattr(MDOC_Dash, bl->argc, bl->argv))
+ tp = "\\-";
+ if (arg_hasattr(MDOC_Enum, bl->argc, bl->argv)) {
+ (pair->ppair->ppair->count)++;
+ (void)snprintf(buf, sizeof(buf), "%d.",
+ pair->ppair->ppair->count);
+ tp = buf;
+ }
+ if (arg_hasattr(MDOC_Hyphen, bl->argc, bl->argv))
+ tp = "\\-";
+ }
- i = arg_getattr(MDOC_Width, bl->argc, bl->argv);
- width = i >= 0 ? arg_width(&bl->argv[i]) : 10;
+ /* Margin control. */
- i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
- offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
+ p->offset += offset;
- if (MDOC_HEAD == node->type) {
- p->flags |= TERMP_NOBREAK;
- p->offset += offset;
+ switch (type) {
+ case (MDOC_Bullet):
+ /* FALLTHROUGH */
+ case (MDOC_Dash):
+ /* FALLTHROUGH */
+ case (MDOC_Enum):
+ /* FALLTHROUGH */
+ case (MDOC_Hyphen):
+ /* FALLTHROUGH */
+ case (MDOC_Tag):
+ if (MDOC_HEAD == node->type)
p->rmargin = p->offset + width;
- } else {
- p->flags |= TERMP_NOSPACE;
- p->flags |= TERMP_NOLPAD;
+ else if (MDOC_BODY == node->type)
p->offset += width;
- }
- return(1);
+ break;
+ default:
+ break;
}
- /* If `-ohang', adjust left-margin. */
+ if (NULL == tp)
+ return(1);
- if (arg_hasattr(MDOC_Ohang, bl->argc, bl->argv)) {
- i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
- offset = i >= 0 ? arg_offset(&bl->argv[i]) : 0;
+ word(p, tp);
+ return(0);
+}
- p->flags |= TERMP_NOSPACE;
- p->offset += offset;
- return(1);
- }
- return(1);
+/* ARGSUSED */
+static void
+termp_it_post(DECL_ARGS)
+{
+
+ if (MDOC_BODY != node->type && MDOC_HEAD != node->type)
+ return;
+
+ flushln(p);
+
+ p->offset = pair->offset;
+ p->rmargin = pair->rmargin;
+
+ if (MDOC_HEAD == node->type) {
+ p->flags &= ~TERMP_NOBREAK;
+ p->flags &= ~TERMP_NORPAD;
+ } else if (MDOC_BODY == node->type)
+ p->flags &= ~TERMP_NOLPAD;
}
termp_nm_pre(DECL_ARGS)
{
+ if (SEC_SYNOPSIS == node->sec)
+ newln(p);
+
TERMPAIR_SETFLAG(pair, ttypes[TTYPE_PROG]);
if (NULL == node->child)
word(p, meta->name);
termp_ar_pre(DECL_ARGS)
{
- TERMPAIR_SETFLAG(pair, ttypes[TTYPE_CMD_ARG]);
- if (NULL == node->child)
- word(p, "...");
+ if (node->child) {
+ TERMPAIR_SETFLAG(pair, ttypes[TTYPE_CMD_ARG]);
+ return(1);
+ }
+ p->flags |= ttypes[TTYPE_CMD_ARG];
+ word(p, "file");
+ word(p, "...");
+ p->flags &= ~ttypes[TTYPE_CMD_ARG];
return(1);
}
}
+/* ARGSUSED */
+static int
+termp_rs_pre(DECL_ARGS)
+{
+
+ if (MDOC_BLOCK == node->type)
+ vspace(p);
+ return(1);
+}
+
+
/* ARGSUSED */
static int
termp_rv_pre(DECL_ARGS)
return(1);
assert(MDOC_BLOCK == node->parent->type);
+ pair->offset = p->offset;
bl = &node->parent->data.block;
+
i = arg_getattr(MDOC_Offset, bl->argc, bl->argv);
if (-1 != i) {
assert(1 == bl->argv[i].sz);
- pair->offset = arg_offset(&bl->argv[i]);
- p->offset += pair->offset;
+ p->offset += arg_offset(&bl->argv[i]);
}
if ( ! arg_hasattr(MDOC_Literal, bl->argc, bl->argv))
termp_bd_post(DECL_ARGS)
{
- if (MDOC_BODY == node->type)
- p->offset -= pair->offset;
+ if (MDOC_BODY != node->type)
+ return;
+ newln(p);
+ p->offset = pair->offset;
}
if (MDOC_BODY != node->type)
return(1);
- word(p, "\'");
+ word(p, "`");
p->flags |= TERMP_NOSPACE;
return(1);
}
if (MDOC_BODY != node->type)
return(1);
- word(p, "[");
+ word(p, "\\[");
p->flags |= TERMP_NOSPACE;
return(1);
}
if (MDOC_BODY != node->type)
return(1);
- word(p, "(");
+ word(p, "\\&(");
p->flags |= TERMP_NOSPACE;
return(1);
}
return(1);
}
+
+
+/* ARGSUSED */
+static int
+termp_sm_pre(DECL_ARGS)
+{
+
+#if notyet
+ assert(node->child);
+ if (0 == strcmp("off", node->child->data.text.string)) {
+ p->flags &= ~TERMP_NONOSPACE;
+ p->flags &= ~TERMP_NOSPACE;
+ } else {
+ p->flags |= TERMP_NONOSPACE;
+ p->flags |= TERMP_NOSPACE;
+ }
+#endif
+
+ return(0);
+}
+
+
+/* ARGSUSED */
+static int
+termp__t_pre(DECL_ARGS)
+{
+
+ word(p, "\"");
+ p->flags |= TERMP_NOSPACE;
+ return(1);
+}
+
+
+/* ARGSUSED */
+static void
+termp__t_post(DECL_ARGS)
+{
+
+ p->flags |= TERMP_NOSPACE;
+ word(p, "\"");
+ word(p, node->next ? "," : ".");
+}
+
+
+/* ARGSUSED */
+static void
+termp____post(DECL_ARGS)
+{
+
+ p->flags |= TERMP_NOSPACE;
+ word(p, node->next ? "," : ".");
+}