-/* $Id: mdoc_term.c,v 1.294 2014/11/27 16:20:31 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.310 2015/02/12 12:24:33 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
* Permission to use, copy, modify, and distribute this software for any
#include <assert.h>
#include <ctype.h>
+#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
void (*post)(DECL_ARGS);
};
-static size_t a2width(const struct termp *, const char *);
-static size_t a2height(const struct termp *, const char *);
+static int a2width(const struct termp *, const char *);
static void print_bvspace(struct termp *,
const struct mdoc_node *,
static void termp_bd_post(DECL_ARGS);
static void termp_bk_post(DECL_ARGS);
static void termp_bl_post(DECL_ARGS);
+static void termp_eo_post(DECL_ARGS);
static void termp_fd_post(DECL_ARGS);
static void termp_fo_post(DECL_ARGS);
static void termp_in_post(DECL_ARGS);
static int termp_bx_pre(DECL_ARGS);
static int termp_cd_pre(DECL_ARGS);
static int termp_d1_pre(DECL_ARGS);
+static int termp_eo_pre(DECL_ARGS);
static int termp_ex_pre(DECL_ARGS);
static int termp_fa_pre(DECL_ARGS);
static int termp_fd_pre(DECL_ARGS);
{ NULL, NULL }, /* Ec */ /* FIXME: no space */
{ NULL, NULL }, /* Ef */
{ termp_under_pre, NULL }, /* Em */
- { termp_quote_pre, termp_quote_post }, /* Eo */
+ { termp_eo_pre, termp_eo_post }, /* Eo */
{ termp_xx_pre, NULL }, /* Fx */
{ termp_bold_pre, NULL }, /* Ms */
{ termp_li_pre, NULL }, /* No */
print_mdoc_nodelist(DECL_ARGS)
{
- print_mdoc_node(p, pair, meta, n);
- if (n->next)
- print_mdoc_nodelist(p, pair, meta, n->next);
+ while (n != NULL) {
+ print_mdoc_node(p, pair, meta, n);
+ n = n->next;
+ }
}
static void
chld = 1;
offset = p->offset;
rmargin = p->rmargin;
- n->prev_font = term_fontq(p);
+ n->flags &= ~MDOC_ENDED;
+ n->prev_font = p->fonti;
memset(&npair, 0, sizeof(struct termpair));
npair.ppair = pair;
* invoked in a prior line, revert it to PREKEEP.
*/
- if (TERMP_KEEP & p->flags) {
- if (n->prev ? (n->prev->lastline != n->line) :
- (n->parent && n->parent->line != n->line)) {
- p->flags &= ~TERMP_KEEP;
- p->flags |= TERMP_PREKEEP;
- }
+ if (p->flags & TERMP_KEEP && n->flags & MDOC_LINE) {
+ p->flags &= ~TERMP_KEEP;
+ p->flags |= TERMP_PREKEEP;
}
/*
term_tbl(p, n->span);
break;
default:
- if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
+ if (termacts[n->tok].pre &&
+ (n->end == ENDBODY_NOT || n->nchild))
chld = (*termacts[n->tok].pre)
(p, &npair, meta, n);
break;
print_mdoc_nodelist(p, &npair, meta, n->child);
term_fontpopq(p,
- (ENDBODY_NOT == n->end ? n : n->pending)->prev_font);
+ (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
switch (n->type) {
case MDOC_TEXT:
* that it must not call the post handler again.
*/
if (ENDBODY_NOT != n->end)
- n->pending->flags |= MDOC_ENDED;
+ n->body->flags |= MDOC_ENDED;
/*
* End of line terminating an implicit block
free(volume);
}
-static size_t
-a2height(const struct termp *p, const char *v)
-{
- struct roffsu su;
-
-
- assert(v);
- if ( ! a2roffsu(v, &su, SCALE_VS))
- SCALE_VS_INIT(&su, atoi(v));
-
- return(term_vspan(p, &su));
-}
-
-static size_t
+static int
a2width(const struct termp *p, const char *v)
{
struct roffsu su;
- assert(v);
- if ( ! a2roffsu(v, &su, SCALE_MAX)) {
+ if (a2roffsu(v, &su, SCALE_MAX) < 2) {
SCALE_HS_INIT(&su, term_strlen(p, v));
su.scale /= term_strlen(p, "0");
}
-
return(term_hspan(p, &su));
}
static int
termp_it_pre(DECL_ARGS)
{
- const struct mdoc_node *bl, *nn;
char buf[24];
- int i;
- size_t width, offset, ncols, dcol;
+ const struct mdoc_node *bl, *nn;
+ size_t ncols, dcol;
+ int i, offset, width;
enum mdoc_list type;
if (MDOC_BLOCK == n->type) {
bl = n->parent->parent->parent;
type = bl->norm->Bl.type;
+ /*
+ * Defaults for specific list types.
+ */
+
+ switch (type) {
+ case LIST_bullet:
+ /* FALLTHROUGH */
+ case LIST_dash:
+ /* FALLTHROUGH */
+ case LIST_hyphen:
+ /* FALLTHROUGH */
+ case LIST_enum:
+ width = term_len(p, 2);
+ break;
+ case LIST_hang:
+ width = term_len(p, 8);
+ break;
+ case LIST_column:
+ /* FALLTHROUGH */
+ case LIST_tag:
+ width = term_len(p, 10);
+ break;
+ default:
+ width = 0;
+ break;
+ }
+ offset = 0;
+
/*
* First calculate width and offset. This is pretty easy unless
* we're a -column list, in which case all prior columns must
* be accounted for.
*/
- width = offset = 0;
-
- if (bl->norm->Bl.offs)
+ if (bl->norm->Bl.offs != NULL) {
offset = a2width(p, bl->norm->Bl.offs);
+ if (offset < 0 && (size_t)(-offset) > p->offset)
+ offset = -p->offset;
+ else if (offset > SHRT_MAX)
+ offset = 0;
+ }
switch (type) {
case LIST_column:
* number for buffering single arguments. See the above
* handling for column for how this changes.
*/
- assert(bl->norm->Bl.width);
width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
- break;
- }
-
- /*
- * List-type can override the width in the case of fixed-head
- * values (bullet, dash/hyphen, enum). Tags need a non-zero
- * offset.
- */
-
- switch (type) {
- case LIST_bullet:
- /* FALLTHROUGH */
- case LIST_dash:
- /* FALLTHROUGH */
- case LIST_hyphen:
- /* FALLTHROUGH */
- case LIST_enum:
- if (width < term_len(p, 2))
- width = term_len(p, 2);
- break;
- case LIST_hang:
- if (0 == width)
- width = term_len(p, 8);
- break;
- case LIST_column:
- /* FALLTHROUGH */
- case LIST_tag:
- if (0 == width)
- width = term_len(p, 10);
- break;
- default:
+ if (width < 0 && (size_t)(-width) > p->offset)
+ width = -p->offset;
+ else if (width > SHRT_MAX)
+ width = 0;
break;
}
case LIST_enum:
/*
* Weird special case.
- * Very narrow enum lists actually hang.
+ * Some very narrow lists actually hang.
*/
- if (width == term_len(p, 2))
- p->flags |= TERMP_HANG;
/* FALLTHROUGH */
case LIST_bullet:
/* FALLTHROUGH */
case LIST_dash:
/* FALLTHROUGH */
case LIST_hyphen:
+ if (width <= (int)term_len(p, 2))
+ p->flags |= TERMP_HANG;
if (MDOC_HEAD != n->type)
break;
p->flags |= TERMP_NOBREAK;
case LIST_hyphen:
/* FALLTHROUGH */
case LIST_tag:
- assert(width);
if (MDOC_HEAD == n->type)
p->rmargin = p->offset + width;
else
static int
termp_nm_pre(DECL_ARGS)
{
+ const char *cp;
if (MDOC_BLOCK == n->type) {
p->flags |= TERMP_PREKEEP;
if (NULL == n->child)
return(0);
p->flags |= TERMP_NOSPACE;
- p->offset += term_len(p, 1) +
- (NULL == n->prev->child ?
- term_strlen(p, meta->name) :
- MDOC_TEXT == n->prev->child->type ?
- term_strlen(p, n->prev->child->string) :
- term_len(p, 5));
+ cp = NULL;
+ if (n->prev->child != NULL)
+ cp = n->prev->child->string;
+ if (cp == NULL)
+ cp = meta->name;
+ if (cp == NULL)
+ p->offset += term_len(p, 6);
+ else
+ p->offset += term_len(p, 1) + term_strlen(p, cp);
return(1);
}
return(0);
}
- if (n->child == NULL)
- return(0);
-
if (p->flags & TERMP_SPLIT)
term_newln(p);
termp_nd_pre(DECL_ARGS)
{
- if (MDOC_BODY != n->type)
- return(1);
-
-#if defined(__OpenBSD__) || defined(__linux__)
- term_word(p, "\\(en");
-#else
- term_word(p, "\\(em");
-#endif
+ if (n->type == MDOC_BODY)
+ term_word(p, "\\(en");
return(1);
}
{
size_t tabwidth, lm, len, rm, rmax;
struct mdoc_node *nn;
+ int offset;
if (MDOC_BLOCK == n->type) {
print_bvspace(p, n, n);
p->offset += term_len(p, p->defindent + 1);
else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
p->offset += term_len(p, (p->defindent + 1) * 2);
- else
- p->offset += a2width(p, n->norm->Bd.offs);
+ else {
+ offset = a2width(p, n->norm->Bd.offs);
+ if (offset < 0 && (size_t)(-offset) > p->offset)
+ p->offset = 0;
+ else if (offset < SHRT_MAX)
+ p->offset += offset;
+ }
/*
* If -ragged or -filled are specified, the block does nothing
default:
break;
}
- if (nn->next && nn->next->line == nn->line)
+ if (p->flags & TERMP_NONEWLINE ||
+ (nn->next && ! (nn->next->flags & MDOC_LINE)))
continue;
term_flushln(p);
p->flags |= TERMP_NOSPACE;
termp_pf_post(DECL_ARGS)
{
- p->flags |= TERMP_NOSPACE;
+ if ( ! (n->next == NULL || n->next->flags & MDOC_LINE))
+ p->flags |= TERMP_NOSPACE;
}
static int
static int
termp_sp_pre(DECL_ARGS)
{
+ struct roffsu su;
size_t i, len;
switch (n->tok) {
case MDOC_sp:
- len = n->child ? a2height(p, n->child->string) : 1;
+ if (n->child) {
+ if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
+ su.scale = 1.0;
+ len = term_vspan(p, &su);
+ } else
+ len = 1;
break;
case MDOC_br:
len = 0;
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
- term_word(p, n->parent->prev != NULL &&
- n->parent->prev->tok == MDOC_An ? "<" : "\\(la");
+ term_word(p, n->nchild == 1 &&
+ n->child->tok == MDOC_Mt ? "<" : "\\(la");
break;
case MDOC_Bro:
/* FALLTHROUGH */
return(1);
term_word(p, n->norm->Es->child->string);
break;
- case MDOC_Eo:
- break;
case MDOC_Po:
/* FALLTHROUGH */
case MDOC_Pq:
termp_quote_post(DECL_ARGS)
{
- if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
+ if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
return;
- if (MDOC_En != n->tok)
- p->flags |= TERMP_NOSPACE;
+ p->flags |= TERMP_NOSPACE;
switch (n->tok) {
case MDOC_Ao:
/* FALLTHROUGH */
case MDOC_Aq:
- term_word(p, n->parent->prev != NULL &&
- n->parent->prev->tok == MDOC_An ? ">" : "\\(ra");
+ term_word(p, n->nchild == 1 &&
+ n->child->tok == MDOC_Mt ? ">" : "\\(ra");
break;
case MDOC_Bro:
/* FALLTHROUGH */
term_word(p, "\\(rq");
break;
case MDOC_En:
- if (NULL != n->norm->Es &&
- NULL != n->norm->Es->child &&
- NULL != n->norm->Es->child->next) {
- p->flags |= TERMP_NOSPACE;
+ if (n->norm->Es == NULL ||
+ n->norm->Es->child == NULL ||
+ n->norm->Es->child->next == NULL)
+ p->flags &= ~TERMP_NOSPACE;
+ else
term_word(p, n->norm->Es->child->next->string);
- }
- break;
- case MDOC_Eo:
break;
case MDOC_Po:
/* FALLTHROUGH */
}
}
+static int
+termp_eo_pre(DECL_ARGS)
+{
+
+ if (n->type != MDOC_BODY)
+ return(1);
+
+ if (n->end == ENDBODY_NOT &&
+ n->parent->head->child == NULL &&
+ n->child != NULL &&
+ n->child->end != ENDBODY_NOT)
+ term_word(p, "\\&");
+ else if (n->end != ENDBODY_NOT ? n->child != NULL :
+ n->parent->head->child != NULL && (n->child != NULL ||
+ (n->parent->tail != NULL && n->parent->tail->child != NULL)))
+ p->flags |= TERMP_NOSPACE;
+
+ return(1);
+}
+
+static void
+termp_eo_post(DECL_ARGS)
+{
+ int body, tail;
+
+ if (n->type != MDOC_BODY)
+ return;
+
+ if (n->end != ENDBODY_NOT) {
+ p->flags &= ~TERMP_NOSPACE;
+ return;
+ }
+
+ body = n->child != NULL || n->parent->head->child != NULL;
+ tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
+
+ if (body && tail)
+ p->flags |= TERMP_NOSPACE;
+ else if ( ! (body || tail))
+ term_word(p, "\\&");
+ else if ( ! tail)
+ p->flags &= ~TERMP_NOSPACE;
+}
+
static int
termp_fo_pre(DECL_ARGS)
{