version 1.2, 2009/03/19 16:40:49 |
version 1.8, 2009/03/25 21:46:24 |
Line 29 extern size_t strlcpy(char *, const char *, size_t) |
|
Line 29 extern size_t strlcpy(char *, const char *, size_t) |
|
extern size_t strlcat(char *, const char *, size_t); |
extern size_t strlcat(char *, const char *, size_t); |
#endif |
#endif |
|
|
static struct termp *termp_alloc(enum termenc); |
static struct termp *term_alloc(enum termenc); |
static void termp_free(struct termp *); |
static void term_free(struct termp *); |
static void termp_body(struct termp *, struct termpair *, |
static void term_body(struct termp *, struct termpair *, |
const struct mdoc_meta *, |
const struct mdoc_meta *, |
const struct mdoc_node *); |
const struct mdoc_node *); |
static void termp_head(struct termp *, |
static void term_head(struct termp *, |
const struct mdoc_meta *); |
const struct mdoc_meta *); |
static void termp_foot(struct termp *, |
static void term_foot(struct termp *, |
const struct mdoc_meta *); |
const struct mdoc_meta *); |
static void termp_pword(struct termp *, const char *, int); |
static void term_pword(struct termp *, const char *, int); |
static void termp_pescape(struct termp *, |
static void term_pescape(struct termp *, |
const char *, int *, int); |
const char *, int *, int); |
static void termp_nescape(struct termp *, |
static void term_nescape(struct termp *, |
const char *, size_t); |
const char *, size_t); |
static void termp_chara(struct termp *, char); |
static void term_chara(struct termp *, char); |
static void termp_stringa(struct termp *, |
static void term_stringa(struct termp *, |
const char *, size_t); |
const char *, size_t); |
|
static int term_isopendelim(const char *, int); |
|
static int term_isclosedelim(const char *, int); |
static void sanity(const struct mdoc_node *); /* XXX */ |
static void sanity(const struct mdoc_node *); /* XXX */ |
|
|
|
|
void * |
void * |
latin1_alloc(void) |
|
{ |
|
|
|
return(termp_alloc(TERMENC_LATIN1)); |
|
} |
|
|
|
|
|
void * |
|
utf8_alloc(void) |
|
{ |
|
|
|
return(termp_alloc(TERMENC_UTF8)); |
|
} |
|
|
|
|
|
void * |
|
ascii_alloc(void) |
ascii_alloc(void) |
{ |
{ |
|
|
return(termp_alloc(TERMENC_ASCII)); |
return(term_alloc(TERMENC_ASCII)); |
} |
} |
|
|
|
|
int |
int |
terminal_run(void *arg, const struct mdoc *mdoc) |
terminal_run(void *arg, const struct man *man, |
|
const struct mdoc *mdoc) |
{ |
{ |
struct termp *p; |
struct termp *p; |
|
|
|
if (NULL == mdoc) |
|
return(1); |
|
|
p = (struct termp *)arg; |
p = (struct termp *)arg; |
|
|
if (NULL == p->symtab) |
if (NULL == p->symtab) |
p->symtab = ascii2htab(); |
p->symtab = term_ascii2htab(); |
|
|
termp_head(p, mdoc_meta(mdoc)); |
term_head(p, mdoc_meta(mdoc)); |
termp_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc)); |
term_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc)); |
termp_foot(p, mdoc_meta(mdoc)); |
term_foot(p, mdoc_meta(mdoc)); |
|
|
return(1); |
return(1); |
} |
} |
|
|
terminal_free(void *arg) |
terminal_free(void *arg) |
{ |
{ |
|
|
termp_free((struct termp *)arg); |
term_free((struct termp *)arg); |
} |
} |
|
|
|
|
static void |
static void |
termp_free(struct termp *p) |
term_free(struct termp *p) |
{ |
{ |
|
|
if (p->buf) |
if (p->buf) |
free(p->buf); |
free(p->buf); |
if (TERMENC_ASCII == p->enc && p->symtab) |
if (TERMENC_ASCII == p->enc && p->symtab) |
asciifree(p->symtab); |
term_asciifree(p->symtab); |
|
|
free(p); |
free(p); |
} |
} |
|
|
|
|
static struct termp * |
static struct termp * |
termp_alloc(enum termenc enc) |
term_alloc(enum termenc enc) |
{ |
{ |
struct termp *p; |
struct termp *p; |
|
|
Line 126 termp_alloc(enum termenc enc) |
|
Line 116 termp_alloc(enum termenc enc) |
|
} |
} |
|
|
|
|
|
static int |
|
term_isclosedelim(const char *p, int len) |
|
{ |
|
|
|
if (1 != len) |
|
return(0); |
|
|
|
switch (*p) { |
|
case('.'): |
|
/* FALLTHROUGH */ |
|
case(','): |
|
/* FALLTHROUGH */ |
|
case(';'): |
|
/* FALLTHROUGH */ |
|
case(':'): |
|
/* FALLTHROUGH */ |
|
case('?'): |
|
/* FALLTHROUGH */ |
|
case('!'): |
|
/* FALLTHROUGH */ |
|
case(')'): |
|
/* FALLTHROUGH */ |
|
case(']'): |
|
/* FALLTHROUGH */ |
|
case('}'): |
|
return(1); |
|
default: |
|
break; |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
|
|
static int |
|
term_isopendelim(const char *p, int len) |
|
{ |
|
|
|
if (1 != len) |
|
return(0); |
|
|
|
switch (*p) { |
|
case('('): |
|
/* FALLTHROUGH */ |
|
case('['): |
|
/* FALLTHROUGH */ |
|
case('{'): |
|
return(1); |
|
default: |
|
break; |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
|
/* |
/* |
* Flush a line of text. A "line" is loosely defined as being something |
* Flush a line of text. A "line" is loosely defined as being something |
* that should be followed by a newline, regardless of whether it's |
* that should be followed by a newline, regardless of whether it's |
Line 161 termp_alloc(enum termenc enc) |
|
Line 207 termp_alloc(enum termenc enc) |
|
* possible). |
* possible). |
*/ |
*/ |
void |
void |
flushln(struct termp *p) |
term_flushln(struct termp *p) |
{ |
{ |
int i, j; |
int i, j; |
size_t vsz, vis, maxvis, mmax, bp; |
size_t vsz, vis, maxvis, mmax, bp; |
Line 222 flushln(struct termp *p) |
|
Line 268 flushln(struct termp *p) |
|
for (j = 0; j < (int)p->offset; j++) |
for (j = 0; j < (int)p->offset; j++) |
putchar(' '); |
putchar(' '); |
vis = 0; |
vis = 0; |
} else if (vis + vsz > bp) |
} |
warnx("word breaks right margin"); |
} else if (vis && vis + vsz > bp) { |
|
putchar('\n'); |
/* TODO: hyphenate. */ |
for (j = 0; j < (int)p->rmargin; j++) |
|
putchar(' '); |
} else { |
vis = p->rmargin - p->offset; |
if (vis && vis + vsz > bp) { |
|
putchar('\n'); |
|
for (j = 0; j < (int)p->rmargin; j++) |
|
putchar(' '); |
|
vis = p->rmargin - p->offset; |
|
} else if (vis + vsz > bp) |
|
warnx("word breaks right margin"); |
|
|
|
/* TODO: hyphenate. */ |
|
} |
} |
|
|
/* |
/* |
Line 294 flushln(struct termp *p) |
|
Line 331 flushln(struct termp *p) |
|
* assertion. |
* assertion. |
*/ |
*/ |
void |
void |
newln(struct termp *p) |
term_newln(struct termp *p) |
{ |
{ |
|
|
p->flags |= TERMP_NOSPACE; |
p->flags |= TERMP_NOSPACE; |
Line 302 newln(struct termp *p) |
|
Line 339 newln(struct termp *p) |
|
p->flags &= ~TERMP_NOLPAD; |
p->flags &= ~TERMP_NOLPAD; |
return; |
return; |
} |
} |
flushln(p); |
term_flushln(p); |
p->flags &= ~TERMP_NOLPAD; |
p->flags &= ~TERMP_NOLPAD; |
} |
} |
|
|
Line 314 newln(struct termp *p) |
|
Line 351 newln(struct termp *p) |
|
* assertion. |
* assertion. |
*/ |
*/ |
void |
void |
vspace(struct termp *p) |
term_vspace(struct termp *p) |
{ |
{ |
|
|
newln(p); |
term_newln(p); |
putchar('\n'); |
putchar('\n'); |
} |
} |
|
|
Line 329 vspace(struct termp *p) |
|
Line 366 vspace(struct termp *p) |
|
* the word and put it verbatim into the output buffer. |
* the word and put it verbatim into the output buffer. |
*/ |
*/ |
void |
void |
word(struct termp *p, const char *word) |
term_word(struct termp *p, const char *word) |
{ |
{ |
int i, j, len; |
int i, j, len; |
|
|
|
len = (int)strlen(word); |
|
|
if (p->flags & TERMP_LITERAL) { |
if (p->flags & TERMP_LITERAL) { |
termp_pword(p, word, (int)strlen(word)); |
term_pword(p, word, len); |
return; |
return; |
} |
} |
|
|
if (0 == (len = (int)strlen(word))) |
|
errx(1, "blank line not in literal context"); |
|
|
|
if (mdoc_isdelim(word)) { |
|
if ( ! (p->flags & TERMP_IGNDELIM)) |
|
p->flags |= TERMP_NOSPACE; |
|
p->flags &= ~TERMP_IGNDELIM; |
|
} |
|
|
|
/* LINTED */ |
/* LINTED */ |
for (j = i = 0; i < len; i++) { |
for (j = i = 0; i < len; i++) { |
if (' ' != word[i]) { |
if (' ' != word[i]) { |
Line 363 word(struct termp *p, const char *word) |
|
Line 393 word(struct termp *p, const char *word) |
|
if (0 == j) |
if (0 == j) |
continue; |
continue; |
assert(i >= j); |
assert(i >= j); |
termp_pword(p, &word[i - j], j); |
term_pword(p, &word[i - j], j); |
j = 0; |
j = 0; |
} |
} |
if (j > 0) { |
if (j > 0) { |
assert(i >= j); |
assert(i >= j); |
termp_pword(p, &word[i - j], j); |
term_pword(p, &word[i - j], j); |
} |
} |
} |
} |
|
|
|
|
|
static void |
|
term_body(struct termp *p, struct termpair *ppair, |
|
const struct mdoc_meta *meta, |
|
const struct mdoc_node *node) |
|
{ |
|
|
|
term_node(p, ppair, meta, node); |
|
if (node->next) |
|
term_body(p, ppair, meta, node->next); |
|
} |
|
|
|
|
/* |
/* |
* This is the main function for printing out nodes. It's constituted |
* This is the main function for printing out nodes. It's constituted |
* of PRE and POST functions, which correspond to prefix and infix |
* of PRE and POST functions, which correspond to prefix and infix |
* processing. The termpair structure allows data to persist between |
* processing. The termpair structure allows data to persist between |
* prefix and postfix invocations. |
* prefix and postfix invocations. |
*/ |
*/ |
static void |
void |
termp_body(struct termp *p, struct termpair *ppair, |
term_node(struct termp *p, struct termpair *ppair, |
const struct mdoc_meta *meta, |
const struct mdoc_meta *meta, |
const struct mdoc_node *node) |
const struct mdoc_node *node) |
{ |
{ |
Line 405 termp_body(struct termp *p, struct termpair *ppair, |
|
Line 447 termp_body(struct termp *p, struct termpair *ppair, |
|
if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) |
if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) |
dochild = 0; |
dochild = 0; |
} else /* MDOC_TEXT == node->type */ |
} else /* MDOC_TEXT == node->type */ |
word(p, node->string); |
term_word(p, node->string); |
|
|
/* Children. */ |
/* Children. */ |
|
|
Line 413 termp_body(struct termp *p, struct termpair *ppair, |
|
Line 455 termp_body(struct termp *p, struct termpair *ppair, |
|
p->flags |= pair.flag; |
p->flags |= pair.flag; |
|
|
if (dochild && node->child) |
if (dochild && node->child) |
termp_body(p, &pair, meta, node->child); |
term_body(p, &pair, meta, node->child); |
|
|
if (TERMPAIR_FLAG & pair.type) |
if (TERMPAIR_FLAG & pair.type) |
p->flags &= ~pair.flag; |
p->flags &= ~pair.flag; |
Line 423 termp_body(struct termp *p, struct termpair *ppair, |
|
Line 465 termp_body(struct termp *p, struct termpair *ppair, |
|
if (MDOC_TEXT != node->type) |
if (MDOC_TEXT != node->type) |
if (termacts[node->tok].post) |
if (termacts[node->tok].post) |
(*termacts[node->tok].post)(p, &pair, meta, node); |
(*termacts[node->tok].post)(p, &pair, meta, node); |
|
|
/* Siblings. */ |
|
|
|
if (node->next) |
|
termp_body(p, ppair, meta, node->next); |
|
} |
} |
|
|
|
|
static void |
static void |
termp_foot(struct termp *p, const struct mdoc_meta *meta) |
term_foot(struct termp *p, const struct mdoc_meta *meta) |
{ |
{ |
struct tm *tm; |
struct tm *tm; |
char *buf, *os; |
char *buf, *os; |
Line 460 termp_foot(struct termp *p, const struct mdoc_meta *me |
|
Line 497 termp_foot(struct termp *p, const struct mdoc_meta *me |
|
* OS MDOCDATE |
* OS MDOCDATE |
*/ |
*/ |
|
|
vspace(p); |
term_vspace(p); |
|
|
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; |
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; |
p->rmargin = p->maxrmargin - strlen(buf); |
p->rmargin = p->maxrmargin - strlen(buf); |
p->offset = 0; |
p->offset = 0; |
|
|
word(p, os); |
term_word(p, os); |
flushln(p); |
term_flushln(p); |
|
|
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
p->offset = p->rmargin; |
p->offset = p->rmargin; |
p->rmargin = p->maxrmargin; |
p->rmargin = p->maxrmargin; |
p->flags &= ~TERMP_NOBREAK; |
p->flags &= ~TERMP_NOBREAK; |
|
|
word(p, buf); |
term_word(p, buf); |
flushln(p); |
term_flushln(p); |
|
|
free(buf); |
free(buf); |
free(os); |
free(os); |
Line 483 termp_foot(struct termp *p, const struct mdoc_meta *me |
|
Line 520 termp_foot(struct termp *p, const struct mdoc_meta *me |
|
|
|
|
|
static void |
static void |
termp_head(struct termp *p, const struct mdoc_meta *meta) |
term_head(struct termp *p, const struct mdoc_meta *meta) |
{ |
{ |
char *buf, *title; |
char *buf, *title; |
|
|
Line 524 termp_head(struct termp *p, const struct mdoc_meta *me |
|
Line 561 termp_head(struct termp *p, const struct mdoc_meta *me |
|
p->rmargin = (p->maxrmargin - strlen(buf)) / 2; |
p->rmargin = (p->maxrmargin - strlen(buf)) / 2; |
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; |
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; |
|
|
word(p, title); |
term_word(p, title); |
flushln(p); |
term_flushln(p); |
|
|
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
p->offset = p->rmargin; |
p->offset = p->rmargin; |
p->rmargin = p->maxrmargin - strlen(title); |
p->rmargin = p->maxrmargin - strlen(title); |
|
|
word(p, buf); |
term_word(p, buf); |
flushln(p); |
term_flushln(p); |
|
|
p->offset = p->rmargin; |
p->offset = p->rmargin; |
p->rmargin = p->maxrmargin; |
p->rmargin = p->maxrmargin; |
p->flags &= ~TERMP_NOBREAK; |
p->flags &= ~TERMP_NOBREAK; |
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
|
|
word(p, title); |
term_word(p, title); |
flushln(p); |
term_flushln(p); |
|
|
p->rmargin = p->maxrmargin; |
p->rmargin = p->maxrmargin; |
p->offset = 0; |
p->offset = 0; |
Line 557 termp_head(struct termp *p, const struct mdoc_meta *me |
|
Line 594 termp_head(struct termp *p, const struct mdoc_meta *me |
|
* output buffer by way of the symbol table. |
* output buffer by way of the symbol table. |
*/ |
*/ |
static void |
static void |
termp_nescape(struct termp *p, const char *word, size_t len) |
term_nescape(struct termp *p, const char *word, size_t len) |
{ |
{ |
const char *rhs; |
const char *rhs; |
size_t sz; |
size_t sz; |
|
|
if (NULL == (rhs = a2ascii(p->symtab, word, len, &sz))) |
if (NULL == (rhs = term_a2ascii(p->symtab, word, len, &sz))) |
return; |
return; |
termp_stringa(p, rhs, sz); |
term_stringa(p, rhs, sz); |
} |
} |
|
|
|
|
Line 574 termp_nescape(struct termp *p, const char *word, size_ |
|
Line 611 termp_nescape(struct termp *p, const char *word, size_ |
|
* the escape sequence (we assert upon badly-formed escape sequences). |
* the escape sequence (we assert upon badly-formed escape sequences). |
*/ |
*/ |
static void |
static void |
termp_pescape(struct termp *p, const char *word, int *i, int len) |
term_pescape(struct termp *p, const char *word, int *i, int len) |
{ |
{ |
int j; |
int j; |
|
|
Line 586 termp_pescape(struct termp *p, const char *word, int * |
|
Line 623 termp_pescape(struct termp *p, const char *word, int * |
|
if (*i + 1 >= len) |
if (*i + 1 >= len) |
return; |
return; |
|
|
termp_nescape(p, &word[*i], 2); |
term_nescape(p, &word[*i], 2); |
(*i)++; |
(*i)++; |
return; |
return; |
|
|
Line 601 termp_pescape(struct termp *p, const char *word, int * |
|
Line 638 termp_pescape(struct termp *p, const char *word, int * |
|
if (*i + 1 >= len) |
if (*i + 1 >= len) |
return; |
return; |
|
|
termp_nescape(p, &word[*i], 2); |
term_nescape(p, &word[*i], 2); |
(*i)++; |
(*i)++; |
return; |
return; |
case ('['): |
case ('['): |
break; |
break; |
default: |
default: |
termp_nescape(p, &word[*i], 1); |
term_nescape(p, &word[*i], 1); |
return; |
return; |
} |
} |
|
|
} else if ('[' != word[*i]) { |
} else if ('[' != word[*i]) { |
termp_nescape(p, &word[*i], 1); |
term_nescape(p, &word[*i], 1); |
return; |
return; |
} |
} |
|
|
Line 623 termp_pescape(struct termp *p, const char *word, int * |
|
Line 660 termp_pescape(struct termp *p, const char *word, int * |
|
if (0 == word[*i]) |
if (0 == word[*i]) |
return; |
return; |
|
|
termp_nescape(p, &word[*i - j], (size_t)j); |
term_nescape(p, &word[*i - j], (size_t)j); |
} |
} |
|
|
|
|
Line 633 termp_pescape(struct termp *p, const char *word, int * |
|
Line 670 termp_pescape(struct termp *p, const char *word, int * |
|
* handles word styling. |
* handles word styling. |
*/ |
*/ |
static void |
static void |
termp_pword(struct termp *p, const char *word, int len) |
term_pword(struct termp *p, const char *word, int len) |
{ |
{ |
int i; |
int i; |
|
|
if ( ! (TERMP_NOSPACE & p->flags) && |
if (term_isclosedelim(word, len)) |
! (TERMP_LITERAL & p->flags)) |
if ( ! (TERMP_IGNDELIM & p->flags)) |
termp_chara(p, ' '); |
p->flags |= TERMP_NOSPACE; |
|
|
|
if ( ! (TERMP_NOSPACE & p->flags)) |
|
term_chara(p, ' '); |
|
|
if ( ! (p->flags & TERMP_NONOSPACE)) |
if ( ! (p->flags & TERMP_NONOSPACE)) |
p->flags &= ~TERMP_NOSPACE; |
p->flags &= ~TERMP_NOSPACE; |
|
|
Line 651 termp_pword(struct termp *p, const char *word, int len |
|
Line 691 termp_pword(struct termp *p, const char *word, int len |
|
|
|
for (i = 0; i < len; i++) { |
for (i = 0; i < len; i++) { |
if ('\\' == word[i]) { |
if ('\\' == word[i]) { |
termp_pescape(p, word, &i, len); |
term_pescape(p, word, &i, len); |
continue; |
continue; |
} |
} |
|
|
if (TERMP_STYLE & p->flags) { |
if (TERMP_STYLE & p->flags) { |
if (TERMP_BOLD & p->flags) { |
if (TERMP_BOLD & p->flags) { |
termp_chara(p, word[i]); |
term_chara(p, word[i]); |
termp_chara(p, 8); |
term_chara(p, 8); |
} |
} |
if (TERMP_UNDER & p->flags) { |
if (TERMP_UNDER & p->flags) { |
termp_chara(p, '_'); |
term_chara(p, '_'); |
termp_chara(p, 8); |
term_chara(p, 8); |
} |
} |
} |
} |
|
|
termp_chara(p, word[i]); |
term_chara(p, word[i]); |
} |
} |
|
|
|
if (term_isopendelim(word, len)) |
|
p->flags |= TERMP_NOSPACE; |
} |
} |
|
|
|
|
/* |
/* |
* Like termp_chara() but for arbitrary-length buffers. Resize the |
* Like term_chara() but for arbitrary-length buffers. Resize the |
* buffer by a factor of two (if the buffer is less than that) or the |
* buffer by a factor of two (if the buffer is less than that) or the |
* buffer's size. |
* buffer's size. |
*/ |
*/ |
static void |
static void |
termp_stringa(struct termp *p, const char *c, size_t sz) |
term_stringa(struct termp *p, const char *c, size_t sz) |
{ |
{ |
size_t s; |
size_t s; |
|
|
Line 706 termp_stringa(struct termp *p, const char *c, size_t s |
|
Line 749 termp_stringa(struct termp *p, const char *c, size_t s |
|
* size. |
* size. |
*/ |
*/ |
static void |
static void |
termp_chara(struct termp *p, char c) |
term_chara(struct termp *p, char c) |
{ |
{ |
size_t s; |
size_t s; |
|
|