version 1.2, 2009/03/19 16:40:49 |
version 1.12, 2009/03/26 16:47:13 |
|
|
#include <string.h> |
#include <string.h> |
|
|
#include "term.h" |
#include "term.h" |
|
#include "man.h" |
|
#include "mdoc.h" |
|
|
#ifdef __linux__ |
extern int man_run(struct termp *, |
extern size_t strlcpy(char *, const char *, size_t); |
const struct man *); |
extern size_t strlcat(char *, const char *, size_t); |
extern int mdoc_run(struct termp *, |
#endif |
const struct mdoc *); |
|
|
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_pword(struct termp *, const char *, int); |
const struct mdoc_meta *, |
static void term_pescape(struct termp *, |
const struct mdoc_node *); |
|
static void termp_head(struct termp *, |
|
const struct mdoc_meta *); |
|
static void termp_foot(struct termp *, |
|
const struct mdoc_meta *); |
|
static void termp_pword(struct termp *, const char *, int); |
|
static void termp_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 void sanity(const struct mdoc_node *); /* XXX */ |
static int term_isopendelim(const char *, int); |
|
static int term_isclosedelim(const char *, int); |
|
|
|
|
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; |
|
|
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)); |
if (man) |
termp_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc)); |
return(man_run(p, man)); |
termp_foot(p, mdoc_meta(mdoc)); |
if (mdoc) |
|
return(mdoc_run(p, 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 108 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 199 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 260 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 323 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 331 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 343 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 358 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 385 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); |
} |
} |
} |
} |
|
|
|
|
/* |
/* |
* This is the main function for printing out nodes. It's constituted |
|
* of PRE and POST functions, which correspond to prefix and infix |
|
* processing. The termpair structure allows data to persist between |
|
* prefix and postfix invocations. |
|
*/ |
|
static void |
|
termp_body(struct termp *p, struct termpair *ppair, |
|
const struct mdoc_meta *meta, |
|
const struct mdoc_node *node) |
|
{ |
|
int dochild; |
|
struct termpair pair; |
|
|
|
/* Some quick sanity-checking. */ |
|
|
|
sanity(node); |
|
|
|
/* Pre-processing. */ |
|
|
|
dochild = 1; |
|
pair.ppair = ppair; |
|
pair.type = 0; |
|
pair.offset = pair.rmargin = 0; |
|
pair.flag = 0; |
|
pair.count = 0; |
|
|
|
if (MDOC_TEXT != node->type) { |
|
if (termacts[node->tok].pre) |
|
if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) |
|
dochild = 0; |
|
} else /* MDOC_TEXT == node->type */ |
|
word(p, node->string); |
|
|
|
/* Children. */ |
|
|
|
if (TERMPAIR_FLAG & pair.type) |
|
p->flags |= pair.flag; |
|
|
|
if (dochild && node->child) |
|
termp_body(p, &pair, meta, node->child); |
|
|
|
if (TERMPAIR_FLAG & pair.type) |
|
p->flags &= ~pair.flag; |
|
|
|
/* Post-processing. */ |
|
|
|
if (MDOC_TEXT != node->type) |
|
if (termacts[node->tok].post) |
|
(*termacts[node->tok].post)(p, &pair, meta, node); |
|
|
|
/* Siblings. */ |
|
|
|
if (node->next) |
|
termp_body(p, ppair, meta, node->next); |
|
} |
|
|
|
|
|
static void |
|
termp_foot(struct termp *p, const struct mdoc_meta *meta) |
|
{ |
|
struct tm *tm; |
|
char *buf, *os; |
|
|
|
if (NULL == (buf = malloc(p->rmargin))) |
|
err(1, "malloc"); |
|
if (NULL == (os = malloc(p->rmargin))) |
|
err(1, "malloc"); |
|
|
|
tm = localtime(&meta->date); |
|
|
|
#ifdef __OpenBSD__ |
|
if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) |
|
#else |
|
if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) |
|
#endif |
|
err(1, "strftime"); |
|
|
|
(void)strlcpy(os, meta->os, p->rmargin); |
|
|
|
/* |
|
* This is /slightly/ different from regular groff output |
|
* because we don't have page numbers. Print the following: |
|
* |
|
* OS MDOCDATE |
|
*/ |
|
|
|
vspace(p); |
|
|
|
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; |
|
p->rmargin = p->maxrmargin - strlen(buf); |
|
p->offset = 0; |
|
|
|
word(p, os); |
|
flushln(p); |
|
|
|
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
|
p->offset = p->rmargin; |
|
p->rmargin = p->maxrmargin; |
|
p->flags &= ~TERMP_NOBREAK; |
|
|
|
word(p, buf); |
|
flushln(p); |
|
|
|
free(buf); |
|
free(os); |
|
} |
|
|
|
|
|
static void |
|
termp_head(struct termp *p, const struct mdoc_meta *meta) |
|
{ |
|
char *buf, *title; |
|
|
|
p->rmargin = p->maxrmargin; |
|
p->offset = 0; |
|
|
|
if (NULL == (buf = malloc(p->rmargin))) |
|
err(1, "malloc"); |
|
if (NULL == (title = malloc(p->rmargin))) |
|
err(1, "malloc"); |
|
|
|
/* |
|
* The header is strange. It has three components, which are |
|
* really two with the first duplicated. It goes like this: |
|
* |
|
* IDENTIFIER TITLE IDENTIFIER |
|
* |
|
* The IDENTIFIER is NAME(SECTION), which is the command-name |
|
* (if given, or "unknown" if not) followed by the manual page |
|
* section. These are given in `Dt'. The TITLE is a free-form |
|
* string depending on the manual volume. If not specified, it |
|
* switches on the manual section. |
|
*/ |
|
|
|
assert(meta->vol); |
|
(void)strlcpy(buf, meta->vol, p->rmargin); |
|
|
|
if (meta->arch) { |
|
(void)strlcat(buf, " (", p->rmargin); |
|
(void)strlcat(buf, meta->arch, p->rmargin); |
|
(void)strlcat(buf, ")", p->rmargin); |
|
} |
|
|
|
(void)snprintf(title, p->rmargin, "%s(%d)", |
|
meta->title, meta->msec); |
|
|
|
p->offset = 0; |
|
p->rmargin = (p->maxrmargin - strlen(buf)) / 2; |
|
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; |
|
|
|
word(p, title); |
|
flushln(p); |
|
|
|
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
|
p->offset = p->rmargin; |
|
p->rmargin = p->maxrmargin - strlen(title); |
|
|
|
word(p, buf); |
|
flushln(p); |
|
|
|
p->offset = p->rmargin; |
|
p->rmargin = p->maxrmargin; |
|
p->flags &= ~TERMP_NOBREAK; |
|
p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; |
|
|
|
word(p, title); |
|
flushln(p); |
|
|
|
p->rmargin = p->maxrmargin; |
|
p->offset = 0; |
|
p->flags &= ~TERMP_NOSPACE; |
|
|
|
free(title); |
|
free(buf); |
|
} |
|
|
|
|
|
/* |
|
* Determine the symbol indicated by an escape sequences, that is, one |
* Determine the symbol indicated by an escape sequences, that is, one |
* starting with a backslash. Once done, we pass this value into the |
* starting with a backslash. Once done, we pass this value into the |
* 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 418 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 430 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 445 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 ('f' == word[*i]) { |
|
(*i)++; |
|
if (*i >= len) |
|
return; |
|
switch (word[*i]) { |
|
case ('B'): |
|
p->flags |= TERMP_BOLD; |
|
break; |
|
case ('I'): |
|
p->flags |= TERMP_UNDER; |
|
break; |
|
case ('P'): |
|
/* FALLTHROUGH */ |
|
case ('R'): |
|
p->flags &= ~TERMP_STYLE; |
|
break; |
|
default: |
|
break; |
|
} |
|
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 488 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 498 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 519 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 577 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; |
|
|
Line 722 termp_chara(struct termp *p, char c) |
|
Line 593 termp_chara(struct termp *p, char c) |
|
p->buf[(int)(p->col)++] = c; |
p->buf[(int)(p->col)++] = c; |
} |
} |
|
|
|
|
static void |
|
sanity(const struct mdoc_node *n) |
|
{ |
|
|
|
switch (n->type) { |
|
case (MDOC_TEXT): |
|
if (n->child) |
|
errx(1, "regular form violated (1)"); |
|
if (NULL == n->parent) |
|
errx(1, "regular form violated (2)"); |
|
if (NULL == n->string) |
|
errx(1, "regular form violated (3)"); |
|
switch (n->parent->type) { |
|
case (MDOC_TEXT): |
|
/* FALLTHROUGH */ |
|
case (MDOC_ROOT): |
|
errx(1, "regular form violated (4)"); |
|
/* NOTREACHED */ |
|
default: |
|
break; |
|
} |
|
break; |
|
case (MDOC_ELEM): |
|
if (NULL == n->parent) |
|
errx(1, "regular form violated (5)"); |
|
switch (n->parent->type) { |
|
case (MDOC_TAIL): |
|
/* FALLTHROUGH */ |
|
case (MDOC_BODY): |
|
/* FALLTHROUGH */ |
|
case (MDOC_HEAD): |
|
break; |
|
default: |
|
errx(1, "regular form violated (6)"); |
|
/* NOTREACHED */ |
|
} |
|
if (n->child) switch (n->child->type) { |
|
case (MDOC_TEXT): |
|
break; |
|
default: |
|
errx(1, "regular form violated (7("); |
|
/* NOTREACHED */ |
|
} |
|
break; |
|
case (MDOC_HEAD): |
|
/* FALLTHROUGH */ |
|
case (MDOC_BODY): |
|
/* FALLTHROUGH */ |
|
case (MDOC_TAIL): |
|
if (NULL == n->parent) |
|
errx(1, "regular form violated (8)"); |
|
if (MDOC_BLOCK != n->parent->type) |
|
errx(1, "regular form violated (9)"); |
|
if (n->child) switch (n->child->type) { |
|
case (MDOC_BLOCK): |
|
/* FALLTHROUGH */ |
|
case (MDOC_ELEM): |
|
/* FALLTHROUGH */ |
|
case (MDOC_TEXT): |
|
break; |
|
default: |
|
errx(1, "regular form violated (a)"); |
|
/* NOTREACHED */ |
|
} |
|
break; |
|
case (MDOC_BLOCK): |
|
if (NULL == n->parent) |
|
errx(1, "regular form violated (b)"); |
|
if (NULL == n->child) |
|
errx(1, "regular form violated (c)"); |
|
switch (n->parent->type) { |
|
case (MDOC_ROOT): |
|
/* FALLTHROUGH */ |
|
case (MDOC_HEAD): |
|
/* FALLTHROUGH */ |
|
case (MDOC_BODY): |
|
/* FALLTHROUGH */ |
|
case (MDOC_TAIL): |
|
break; |
|
default: |
|
errx(1, "regular form violated (d)"); |
|
/* NOTREACHED */ |
|
} |
|
switch (n->child->type) { |
|
case (MDOC_ROOT): |
|
/* FALLTHROUGH */ |
|
case (MDOC_ELEM): |
|
errx(1, "regular form violated (e)"); |
|
/* NOTREACHED */ |
|
default: |
|
break; |
|
} |
|
break; |
|
case (MDOC_ROOT): |
|
if (n->parent) |
|
errx(1, "regular form violated (f)"); |
|
if (NULL == n->child) |
|
errx(1, "regular form violated (10)"); |
|
switch (n->child->type) { |
|
case (MDOC_BLOCK): |
|
break; |
|
default: |
|
errx(1, "regular form violated (11)"); |
|
/* NOTREACHED */ |
|
} |
|
break; |
|
} |
|
} |
|