=================================================================== RCS file: /cvs/mandoc/term.c,v retrieving revision 1.2 retrieving revision 1.7 diff -u -p -r1.2 -r1.7 --- mandoc/term.c 2009/02/20 23:35:36 1.2 +++ mandoc/term.c 2009/02/22 14:31:08 1.7 @@ -1,6 +1,6 @@ -/* $Id: term.c,v 1.2 2009/02/20 23:35:36 kristaps Exp $ */ +/* $Id: term.c,v 1.7 2009/02/22 14:31:08 kristaps Exp $ */ /* - * Copyright (c) 2008 Kristaps Dzonsons + * Copyright (c) 2008, 2009 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the @@ -18,44 +18,24 @@ */ #include #include -#include #include #include #include #include -#include #include -#include "private.h" +#ifdef __linux__ +#include +#endif +#include "term.h" -enum termesc { - ESC_CLEAR, - ESC_BOLD, - ESC_UNDERLINE +enum termstyle { + STYLE_CLEAR, + STYLE_BOLD, + STYLE_UNDERLINE }; -struct termp { - size_t maxvisible; - size_t maxcols; - size_t indent; - size_t col; - int flags; -#define TERMP_BOLD (1 << 0) -#define TERMP_UNDERLINE (1 << 1) -#define TERMP_NOSPACE (1 << 2) - char *buf; -}; - -struct termact { - int (*pre)(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); - int (*post)(struct termp *, - const struct mdoc_meta *, - const struct mdoc_node *); -}; - static void termprint_r(struct termp *, const struct mdoc_meta *, const struct mdoc_node *); @@ -64,176 +44,52 @@ static void termprint_header(struct termp *, static void termprint_footer(struct termp *, const struct mdoc_meta *); -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 void pescape(struct termp *, + const char *, size_t *, size_t); +static void chara(struct termp *, char); +static void style(struct termp *, enum termstyle); -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 *); +#ifdef __linux__ +extern size_t strlcat(char *, const char *, size_t); +extern size_t strlcpy(char *, const char *, size_t); +#endif -const struct termact termacts[MDOC_MAX] = { - { NULL, NULL }, /* \" */ - { NULL, NULL }, /* Dd */ - { NULL, NULL }, /* Dt */ - { NULL, NULL }, /* Os */ - { termp_sh_pre, termp_sh_post }, /* Sh */ - { NULL, NULL }, /* Ss */ - { termp_pp_pre, NULL }, /* Pp */ - { NULL, NULL }, /* D1 */ - { NULL, NULL }, /* Dl */ - { NULL, NULL }, /* Bd */ - { NULL, NULL }, /* Ed */ - { NULL, termp_bl_post }, /* Bl */ - { NULL, NULL }, /* El */ - { termp_it_pre, NULL }, /* It */ - { NULL, NULL }, /* Ad */ - { NULL, NULL }, /* An */ - { termp_under_pre, termp_under_post }, /* Ar */ - { NULL, NULL }, /* Cd */ - { NULL, NULL }, /* Cm */ - { NULL, NULL }, /* Dv */ - { NULL, NULL }, /* Er */ - { NULL, NULL }, /* Ev */ - { NULL, NULL }, /* Ex */ - { NULL, NULL }, /* Fa */ - { NULL, NULL }, /* Fd */ - { termp_fl_pre, termp_bold_post }, /* Fl */ - { NULL, NULL }, /* Fn */ - { NULL, NULL }, /* Ft */ - { NULL, NULL }, /* Ic */ - { NULL, NULL }, /* In */ - { NULL, NULL }, /* Li */ - { termp_nd_pre, NULL }, /* Nd */ - { termp_bold_pre, termp_bold_post }, /* Nm */ - { termp_op_pre, termp_op_post }, /* Op */ - { NULL, NULL }, /* Ot */ - { NULL, NULL }, /* Pa */ - { NULL, NULL }, /* Rv */ - { NULL, NULL }, /* St */ - { NULL, NULL }, /* Va */ - { NULL, NULL }, /* Vt */ - { NULL, 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, NULL }, /* Ac */ - { NULL, NULL }, /* Ao */ - { NULL, NULL }, /* Aq */ - { NULL, NULL }, /* At */ - { NULL, NULL }, /* Bc */ - { NULL, NULL }, /* Bf */ - { NULL, NULL }, /* Bo */ - { NULL, NULL }, /* Bq */ - { NULL, NULL }, /* Bsx */ - { NULL, NULL }, /* Bx */ - { NULL, NULL }, /* Db */ - { NULL, NULL }, /* Dc */ - { NULL, NULL }, /* Do */ - { NULL, NULL }, /* Dq */ - { NULL, NULL }, /* Ec */ - { NULL, NULL }, /* Ef */ - { NULL, NULL }, /* Em */ - { NULL, NULL }, /* Eo */ - { NULL, NULL }, /* Fx */ - { NULL, NULL }, /* Ms */ - { NULL, NULL }, /* No */ - { termp_ns_pre, NULL }, /* Ns */ - { NULL, NULL }, /* Nx */ - { NULL, NULL }, /* Ox */ - { NULL, NULL }, /* Pc */ - { NULL, NULL }, /* Pf */ - { NULL, NULL }, /* Po */ - { NULL, NULL }, /* Pq */ - { NULL, NULL }, /* Qc */ - { NULL, NULL }, /* Ql */ - { NULL, NULL }, /* Qo */ - { NULL, NULL }, /* Qq */ - { NULL, NULL }, /* Re */ - { NULL, NULL }, /* Rs */ - { NULL, NULL }, /* Sc */ - { NULL, NULL }, /* So */ - { NULL, NULL }, /* Sq */ - { NULL, NULL }, /* Sm */ - { NULL, NULL }, /* Sx */ - { NULL, NULL }, /* Sy */ - { NULL, NULL }, /* Tn */ - { NULL, NULL }, /* Ux */ - { NULL, NULL }, /* Xc */ - { NULL, NULL }, /* Xo */ - { NULL, NULL }, /* Fo */ - { NULL, NULL }, /* Fc */ - { NULL, NULL }, /* Oo */ - { NULL, NULL }, /* Oc */ - { NULL, NULL }, /* Bk */ - { NULL, NULL }, /* Ek */ - { NULL, NULL }, /* Bt */ - { NULL, NULL }, /* Hf */ - { NULL, NULL }, /* Fr */ - { NULL, NULL }, /* Ud */ -}; - - -static void -flush(struct termp *p) +void +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 +101,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,26 +139,44 @@ 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; } -static void +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); } -static void +void vspace(struct termp *p) { + /* + * Asserts a vertical space (a full, empty line-break between + * lines). + */ newln(p); putchar('\n'); } @@ -293,15 +186,15 @@ 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; } static void -escape(struct termp *p, enum termesc esc) +style(struct termp *p, enum termstyle esc) { if (p->col + 4 >= p->maxcols) @@ -310,13 +203,13 @@ escape(struct termp *p, enum termesc esc) p->buf[(p->col)++] = 27; p->buf[(p->col)++] = '['; switch (esc) { - case (ESC_CLEAR): + case (STYLE_CLEAR): p->buf[(p->col)++] = '0'; break; - case (ESC_BOLD): + case (STYLE_BOLD): p->buf[(p->col)++] = '1'; break; - case (ESC_UNDERLINE): + case (STYLE_UNDERLINE): p->buf[(p->col)++] = '4'; break; default: @@ -328,6 +221,48 @@ escape(struct termp *p, enum termesc esc) static void +pescape(struct termp *p, const char *word, size_t *i, size_t len) +{ + + (*i)++; + assert(*i < len); + + if ('(' == word[*i]) { + /* Two-character escapes. */ + (*i)++; + assert(*i + 1 < len); + + if ('r' == word[*i] && 'B' == word[*i + 1]) + chara(p, ']'); + else if ('l' == word[*i] && 'B' == word[*i + 1]) + chara(p, '['); + + (*i)++; + return; + + } else if ('[' != word[*i]) { + /* One-character escapes. */ + switch (word[*i]) { + case ('\\'): + /* FALLTHROUGH */ + case ('\''): + /* FALLTHROUGH */ + case ('`'): + /* FALLTHROUGH */ + case ('-'): + /* FALLTHROUGH */ + case ('.'): + chara(p, word[*i]); + default: + break; + } + return; + } + /* n-character escapes. */ +} + + +static void pword(struct termp *p, const char *word, size_t len) { size_t i; @@ -340,29 +275,36 @@ pword(struct termp *p, const char *word, size_t len) p->flags &= ~TERMP_NOSPACE; if (p->flags & TERMP_BOLD) - escape(p, ESC_BOLD); + style(p, STYLE_BOLD); if (p->flags & TERMP_UNDERLINE) - escape(p, ESC_UNDERLINE); + style(p, STYLE_UNDERLINE); - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { + if ('\\' == word[i]) { + pescape(p, word, &i, len); + continue; + } chara(p, word[i]); + } if (p->flags & TERMP_BOLD || p->flags & TERMP_UNDERLINE) - escape(p, ESC_CLEAR); + style(p, STYLE_CLEAR); } -static void +void 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,242 +323,35 @@ word(struct termp *p, const char *word) } -static int -termp_it_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_HEAD): - /* TODO: only print one, if compat. */ - vspace(p); - break; - default: - break; - } - return(1); -} - - -static int -termp_bold_post(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags &= ~TERMP_BOLD; - return(1); -} - - -static int -termp_under_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags |= TERMP_UNDERLINE; - return(1); -} - - -static int -termp_bold_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags |= TERMP_BOLD; - return(1); -} - - -static int -termp_ns_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags |= TERMP_NOSPACE; - return(1); -} - - -static int -termp_pp_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - vspace(p); - return(1); -} - - -static int -termp_under_post(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags &= ~TERMP_UNDERLINE; - return(1); -} - - -static int -termp_nd_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - word(p, "-"); - return(1); -} - - -static int -termp_bl_post(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_BLOCK): - newln(p); - break; - default: - break; - } - return(1); -} - - -static int -termp_op_post(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_BODY): - p->flags |= TERMP_NOSPACE; - word(p, "\\(rB"); - break; - default: - break; - } - return(1); -} - - -static int -termp_sh_post(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_HEAD): - p->flags &= ~TERMP_BOLD; - newln(p); - break; - case (MDOC_BODY): - newln(p); - (p->indent)--; - break; - default: - break; - } - return(1); -} - - -static int -termp_sh_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_HEAD): - vspace(p); - p->flags |= TERMP_BOLD; - break; - case (MDOC_BODY): - (p->indent)++; - break; - default: - break; - } - return(1); -} - - -static int -termp_op_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - switch (node->type) { - case (MDOC_BODY): - word(p, "\\(lB"); - p->flags |= TERMP_NOSPACE; - break; - default: - break; - } - return(1); -} - - -static int -termp_fl_pre(struct termp *p, const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - - p->flags |= TERMP_BOLD; - word(p, "-"); - p->flags |= TERMP_NOSPACE; - return(1); -} - - static void termprint_r(struct termp *p, const struct mdoc_meta *meta, const struct mdoc_node *node) { + int dochild; - /* Pre-processing ----------------- */ + /* Pre-processing. */ + dochild = 1; + if (MDOC_TEXT != node->type) { if (termacts[node->tok].pre) if ( ! (*termacts[node->tok].pre)(p, meta, node)) - return; + dochild = 0; } else /* MDOC_TEXT == node->type */ word(p, node->data.text.string); - /* Children ---------------------- */ + /* Children. */ - if (NULL == node->child) { - /* No-child processing. */ - switch (node->type) { - case (MDOC_ELEM): - switch (node->tok) { - case (MDOC_Nm): - word(p, "progname"); /* TODO */ - break; - case (MDOC_Ar): - word(p, "..."); - break; - default: - break; - } - break; - default: - break; - } - } else + if (dochild && node->child) termprint_r(p, meta, node->child); - /* Post-processing --------------- */ + /* Post-processing. */ - if (MDOC_TEXT != node->type) { + if (MDOC_TEXT != node->type) if (termacts[node->tok].post) - if ( ! (*termacts[node->tok].post)(p, meta, node)) - return; - } + (*termacts[node->tok].post)(p, meta, node); - /* Siblings ---------------------- */ + /* Siblings. */ if (node->next) termprint_r(p, meta, node->next); @@ -630,27 +365,32 @@ 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)) + +#ifdef __linux__ + if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#else + if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#endif 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); @@ -668,12 +408,13 @@ termprint_footer(struct termp *p, const struct mdoc_me static void termprint_header(struct termp *p, const struct mdoc_meta *meta) { - char *msec, *buf, *title, *pp; + char *buf, *title; + const char *pp, *msec; 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 +449,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 +477,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); @@ -756,18 +497,16 @@ termprint_header(struct termp *p, const struct mdoc_me } -int +void termprint(const struct mdoc_node *node, const struct mdoc_meta *meta) { struct termp p; - if (ERR == setupterm(NULL, STDOUT_FILENO, NULL)) - return(0); - - p.maxvisible = columns < 60 ? 60 : (size_t)columns; + p.maxrmargin = 80; /* XXX */ + 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))) @@ -778,8 +517,6 @@ termprint(const struct mdoc_node *node, termprint_footer(&p, meta); free(p.buf); - - return(1); }