=================================================================== RCS file: /cvs/mandoc/term.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -p -r1.2 -r1.3 --- mandoc/term.c 2009/02/20 23:35:36 1.2 +++ mandoc/term.c 2009/02/21 14:56:58 1.3 @@ -1,4 +1,4 @@ -/* $Id: term.c,v 1.2 2009/02/20 23:35:36 kristaps Exp $ */ +/* $Id: term.c,v 1.3 2009/02/21 14:56:58 kristaps Exp $ */ /* * Copyright (c) 2008 Kristaps Dzonsons * @@ -28,7 +28,6 @@ #include "private.h" - enum termesc { ESC_CLEAR, ESC_BOLD, @@ -36,14 +35,17 @@ enum termesc { }; struct termp { - size_t maxvisible; + size_t rmargin; + size_t maxrmargin; size_t maxcols; - size_t indent; + size_t offset; size_t col; int flags; -#define TERMP_BOLD (1 << 0) -#define TERMP_UNDERLINE (1 << 1) -#define TERMP_NOSPACE (1 << 2) +#define TERMP_BOLD (1 << 0) /* Embolden words. */ +#define TERMP_UNDERLINE (1 << 1) /* Underline words. */ +#define TERMP_NOSPACE (1 << 2) /* No space before words. */ +#define TERMP_NOLPAD (1 << 3) /* No left-padding. */ +#define TERMP_NOBREAK (1 << 4) /* No break after line */ char *buf; }; @@ -64,54 +66,43 @@ static void termprint_header(struct termp *, static void termprint_footer(struct termp *, const struct mdoc_meta *); +static int arg_hasattr(int, size_t, + const struct mdoc_arg *); +static int arg_getattr(int, size_t, + const struct mdoc_arg *); + static void newln(struct termp *); static void vspace(struct termp *); static void pword(struct termp *, const char *, size_t); static void word(struct termp *, const char *); -static int termp_it_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_ns_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_pp_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_fl_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_op_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_op_post(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_bl_post(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_sh_post(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_sh_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_nd_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_bold_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_under_pre(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_bold_post(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -static int termp_under_post(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); +#define decl_prepost(name, suffix) \ +static int name##_##suffix(struct termp *, \ + const struct mdoc_meta *, \ + const struct mdoc_node *) +#define decl_pre(name) decl_prepost(name, pre) +#define decl_post(name) decl_prepost(name, post) + +decl_pre(termp_fl); +decl_pre(termp_it); +decl_pre(termp_nd); +decl_pre(termp_ns); +decl_pre(termp_op); +decl_pre(termp_pp); +decl_pre(termp_sh); + +decl_post(termp_bl); +decl_post(termp_it); +decl_post(termp_op); +decl_post(termp_sh); + +decl_pre(termp_bold); +decl_pre(termp_under); + +decl_post(termp_bold); +decl_post(termp_under); + const struct termact termacts[MDOC_MAX] = { { NULL, NULL }, /* \" */ { NULL, NULL }, /* Dd */ @@ -126,7 +117,7 @@ const struct termact termacts[MDOC_MAX] = { { NULL, NULL }, /* Ed */ { NULL, termp_bl_post }, /* Bl */ { NULL, NULL }, /* El */ - { termp_it_pre, NULL }, /* It */ + { termp_it_pre, termp_it_post }, /* It */ { NULL, NULL }, /* Ad */ { NULL, NULL }, /* An */ { termp_under_pre, termp_under_post }, /* Ar */ @@ -222,18 +213,61 @@ const struct termact termacts[MDOC_MAX] = { }; +static int +arg_hasattr(int arg, size_t argc, const struct mdoc_arg *argv) +{ + + return(-1 != arg_getattr(arg, argc, argv)); +} + + +static int +arg_getattr(int arg, size_t argc, const struct mdoc_arg *argv) +{ + int i; + + for (i = 0; i < (int)argc; i++) + if (argv[i].arg == arg) + return(i); + return(-1); +} + + static void -flush(struct termp *p) +flushln(struct termp *p) { size_t i, j, vsz, vis, maxvis; - maxvis = p->maxvisible - (p->indent * 4); + /* + * First, establish the maximum columns of "visible" content. + * This is usually the difference between the right-margin and + * an indentation, but can be, for tagged lists or columns, a + * small set of values. + */ + + assert(p->offset < p->rmargin); + maxvis = p->rmargin - p->offset; vis = 0; - for (j = 0; j < (p->indent * 4); j++) - putchar(' '); + /* + * If in the standard case (left-justified), then begin with our + * indentation, otherwise (columns, etc.) just start spitting + * out text. + */ + if ( ! (p->flags & TERMP_NOLPAD)) + /* LINTED */ + for (j = 0; j < p->offset; j++) + putchar(' '); + for (i = 0; i < p->col; i++) { + /* + * Count up visible word characters. Control sequences + * (starting with the CSI) aren't counted. + */ + assert( ! isspace(p->buf[i])); + + /* LINTED */ for (j = i, vsz = 0; j < p->col; j++) { if (isspace(p->buf[j])) break; @@ -245,13 +279,32 @@ flush(struct termp *p) } assert(vsz > 0); + /* + * If a word is too long and we're within a line, put it + * on the next line. Puke if we're being asked to write + * something that will exceed the right margin (i.e., + * from a fresh line or when we're not allowed to break + * the line with TERMP_NOBREAK). + */ + if (vis && vis + vsz >= maxvis) { + /* FIXME */ + if (p->flags & TERMP_NOBREAK) + errx(1, "word breaks right margin"); putchar('\n'); - for (j = 0; j < (p->indent * 4); j++) + for (j = 0; j < p->offset; j++) putchar(' '); vis = 0; - } + } else if (vis + vsz >= maxvis) { + /* FIXME */ + errx(1, "word breaks right margin"); + } + /* + * Write out the word and a trailing space. Omit the + * space if we're the last word in the line. + */ + for ( ; i < p->col; i++) { if (isspace(p->buf[i])) break; @@ -264,7 +317,17 @@ flush(struct termp *p) } } - putchar('\n'); + /* + * If we're not to right-marginalise it (newline), then instead + * pad to the right margin and stay off. + */ + + if (p->flags & TERMP_NOBREAK) { + for ( ; vis <= maxvis; vis++) + putchar(' '); + } else + putchar('\n'); + p->col = 0; } @@ -273,10 +336,14 @@ static void newln(struct termp *p) { + /* + * A newline only breaks an existing line; it won't assert + * vertical space. + */ p->flags |= TERMP_NOSPACE; if (0 == p->col) return; - flush(p); + flushln(p); } @@ -284,6 +351,10 @@ static void vspace(struct termp *p) { + /* + * Asserts a vertical space (a full, empty line-break between + * lines). + */ newln(p); putchar('\n'); } @@ -293,9 +364,9 @@ static void chara(struct termp *p, char c) { + /* TODO: dynamically expand the buffer. */ if (p->col + 1 >= p->maxcols) errx(1, "line overrun"); - p->buf[(p->col)++] = c; } @@ -344,6 +415,8 @@ pword(struct termp *p, const char *word, size_t len) if (p->flags & TERMP_UNDERLINE) escape(p, ESC_UNDERLINE); + /* TODO: escape patterns. */ + for (i = 0; i < len; i++) chara(p, word[i]); @@ -358,11 +431,13 @@ word(struct termp *p, const char *word) { size_t i, j, len; - /* TODO: delimiters? */ + if (mdoc_isdelim(word)) + p->flags |= TERMP_NOSPACE; len = strlen(word); assert(len > 0); + /* LINTED */ for (j = i = 0; i < len; i++) { if ( ! isspace(word[i])) { j++; @@ -381,23 +456,130 @@ word(struct termp *p, const char *word) } +/* ARGSUSED */ static int -termp_it_pre(struct termp *p, const struct mdoc_meta *meta, +termp_it_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) { + const struct mdoc_node *n, *it; + const struct mdoc_block *bl; + int i; + size_t width; switch (node->type) { + case (MDOC_BODY): + /* FALLTHROUGH */ case (MDOC_HEAD): - /* TODO: only print one, if compat. */ - vspace(p); break; default: + 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; + bl = &n->data.block; + + /* If `-tag', adjust our margins accordingly. */ + + if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) { + i = arg_getattr(MDOC_Width, bl->argc, bl->argv); + assert(i >= 0); + assert(1 == bl->argv[i].sz); + width = strlen(*bl->argv[i].value); /* XXX */ + + if (MDOC_HEAD == node->type) { + flushln(p); + /* FIXME: nested lists. */ + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + } else { + flushln(p); + p->offset -= width + 1; + p->flags &= ~TERMP_NOLPAD; + } + } + + return(1); +} + + +/* ARGSUSED */ +static int +termp_it_pre(struct termp *p, const struct mdoc_meta *meta, + const struct mdoc_node *node) +{ + const struct mdoc_node *n, *it; + const struct mdoc_block *bl; + int i; + size_t width; + + switch (node->type) { + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_HEAD): + it = node->parent; break; + case (MDOC_BLOCK): + it = node; + break; + default: + return(1); } + + 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; + + /* If `-compact', don't assert vertical space. */ + + if (MDOC_BLOCK == node->type) { + if (arg_hasattr(MDOC_Compact, bl->argc, bl->argv)) + newln(p); + else + vspace(p); + return(1); + } + + assert(MDOC_HEAD == node->type + || MDOC_BODY == node->type); + + /* If `-tag', adjust our margins accordingly. */ + + if (arg_hasattr(MDOC_Tag, bl->argc, bl->argv)) { + i = arg_getattr(MDOC_Width, bl->argc, bl->argv); + assert(i >= 0); /* XXX */ + assert(1 == bl->argv[i].sz); + width = strlen(*bl->argv[i].value); /* XXX */ + + /* FIXME: nested lists. */ + + if (MDOC_HEAD == node->type) { + p->flags |= TERMP_NOBREAK; + p->flags |= TERMP_NOSPACE; + p->rmargin = p->offset + width; + } else { + p->flags |= TERMP_NOSPACE; + p->flags |= TERMP_NOLPAD; + p->offset += width + 1; + } + } + return(1); } +/* ARGSUSED */ static int termp_bold_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -408,6 +590,7 @@ termp_bold_post(struct termp *p, const struct mdoc_met } +/* ARGSUSED */ static int termp_under_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -418,6 +601,7 @@ termp_under_pre(struct termp *p, const struct mdoc_met } +/* ARGSUSED */ static int termp_bold_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -428,6 +612,7 @@ termp_bold_pre(struct termp *p, const struct mdoc_meta } +/* ARGSUSED */ static int termp_ns_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -438,6 +623,7 @@ termp_ns_pre(struct termp *p, const struct mdoc_meta * } +/* ARGSUSED */ static int termp_pp_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -448,6 +634,7 @@ termp_pp_pre(struct termp *p, const struct mdoc_meta * } +/* ARGSUSED */ static int termp_under_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -458,6 +645,7 @@ termp_under_post(struct termp *p, const struct mdoc_me } +/* ARGSUSED */ static int termp_nd_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -468,6 +656,7 @@ termp_nd_pre(struct termp *p, const struct mdoc_meta * } +/* ARGSUSED */ static int termp_bl_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -484,6 +673,7 @@ termp_bl_post(struct termp *p, const struct mdoc_meta } +/* ARGSUSED */ static int termp_op_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -501,6 +691,7 @@ termp_op_post(struct termp *p, const struct mdoc_meta } +/* ARGSUSED */ static int termp_sh_post(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -513,7 +704,7 @@ termp_sh_post(struct termp *p, const struct mdoc_meta break; case (MDOC_BODY): newln(p); - (p->indent)--; + p->offset -= 4; break; default: break; @@ -522,6 +713,7 @@ termp_sh_post(struct termp *p, const struct mdoc_meta } +/* ARGSUSED */ static int termp_sh_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -533,7 +725,7 @@ termp_sh_pre(struct termp *p, const struct mdoc_meta * p->flags |= TERMP_BOLD; break; case (MDOC_BODY): - (p->indent)++; + p->offset += 4; break; default: break; @@ -542,6 +734,7 @@ termp_sh_pre(struct termp *p, const struct mdoc_meta * } +/* ARGSUSED */ static int termp_op_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -559,6 +752,7 @@ termp_op_pre(struct termp *p, const struct mdoc_meta * } +/* ARGSUSED */ static int termp_fl_pre(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) @@ -630,27 +824,27 @@ termprint_footer(struct termp *p, const struct mdoc_me char *buf, *os; size_t sz, osz, ssz, i; - if (NULL == (buf = malloc(p->maxvisible))) + if (NULL == (buf = malloc(p->rmargin))) err(1, "malloc"); - if (NULL == (os = malloc(p->maxvisible))) + if (NULL == (os = malloc(p->rmargin))) err(1, "malloc"); tm = localtime(&meta->date); - if (NULL == strftime(buf, p->maxvisible, "%B %d, %Y", tm)) + if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) err(1, "strftime"); - osz = strlcpy(os, meta->os, p->maxvisible); + osz = strlcpy(os, meta->os, p->rmargin); sz = strlen(buf); ssz = sz + osz + 1; - if (ssz > p->maxvisible) { - ssz -= p->maxvisible; + if (ssz > p->rmargin) { + ssz -= p->rmargin; assert(ssz <= osz); os[osz - ssz] = 0; ssz = 1; } else - ssz = p->maxvisible - ssz + 1; + ssz = p->rmargin - ssz + 1; printf("\n"); printf("%s", os); @@ -671,9 +865,9 @@ termprint_header(struct termp *p, const struct mdoc_me char *msec, *buf, *title, *pp; size_t ssz, tsz, ttsz, i;; - if (NULL == (buf = malloc(p->maxvisible))) + if (NULL == (buf = malloc(p->rmargin))) err(1, "malloc"); - if (NULL == (title = malloc(p->maxvisible))) + if (NULL == (title = malloc(p->rmargin))) err(1, "malloc"); if (NULL == (pp = mdoc_vol2a(meta->vol))) @@ -708,27 +902,27 @@ termprint_header(struct termp *p, const struct mdoc_me } assert(pp); - tsz = strlcpy(buf, pp, p->maxvisible); - assert(tsz < p->maxvisible); + tsz = strlcpy(buf, pp, p->rmargin); + assert(tsz < p->rmargin); if ((pp = mdoc_arch2a(meta->arch))) { - tsz = strlcat(buf, " (", p->maxvisible); - assert(tsz < p->maxvisible); - tsz = strlcat(buf, pp, p->maxvisible); - assert(tsz < p->maxvisible); - tsz = strlcat(buf, ")", p->maxvisible); - assert(tsz < p->maxvisible); + tsz = strlcat(buf, " (", p->rmargin); + assert(tsz < p->rmargin); + tsz = strlcat(buf, pp, p->rmargin); + assert(tsz < p->rmargin); + tsz = strlcat(buf, ")", p->rmargin); + assert(tsz < p->rmargin); } - ttsz = strlcpy(title, meta->title, p->maxvisible); + ttsz = strlcpy(title, meta->title, p->rmargin); if (NULL == (msec = mdoc_msec2a(meta->msec))) msec = ""; ssz = (2 * (ttsz + 2 + strlen(msec))) + tsz + 2; - if (ssz > p->maxvisible) { - if ((ssz -= p->maxvisible) % 2) + if (ssz > p->rmargin) { + if ((ssz -= p->rmargin) % 2) ssz++; ssz /= 2; @@ -736,7 +930,7 @@ termprint_header(struct termp *p, const struct mdoc_me title[ttsz - ssz] = 0; ssz = 1; } else - ssz = ((p->maxvisible - ssz) / 2) + 1; + ssz = ((p->rmargin - ssz) / 2) + 1; printf("%s(%s)", title, msec); @@ -765,9 +959,10 @@ termprint(const struct mdoc_node *node, if (ERR == setupterm(NULL, STDOUT_FILENO, NULL)) return(0); - p.maxvisible = columns < 60 ? 60 : (size_t)columns; + p.maxrmargin = columns < 60 ? 60 : (size_t)columns; + p.rmargin = p.maxrmargin; p.maxcols = 1024; - p.indent = p.col = 0; + p.offset = p.col = 0; p.flags = TERMP_NOSPACE; if (NULL == (p.buf = malloc(p.maxcols)))