version 1.45, 2019/04/16 14:58:19 |
version 1.54, 2019/04/28 15:32:05 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
*/ |
*/ |
|
#include <sys/types.h> |
|
|
#include <assert.h> |
#include <assert.h> |
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
|
|
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
|
|
#include "xmalloc.h" |
#include "node.h" |
#include "node.h" |
#include "parse.h" |
#include "parse.h" |
|
|
|
|
int nofill; /* Levels of open no-fill displays. */ |
int nofill; /* Levels of open no-fill displays. */ |
int flags; |
int flags; |
#define PFLAG_WARN (1 << 0) /* Print warning messages. */ |
#define PFLAG_WARN (1 << 0) /* Print warning messages. */ |
#define PFLAG_SPC (1 << 1) /* Whitespace before the next element. */ |
#define PFLAG_LINE (1 << 1) /* New line before the next element. */ |
#define PFLAG_ATTR (1 << 2) /* The most recent attribute is valid. */ |
#define PFLAG_SPC (1 << 2) /* Whitespace before the next element. */ |
#define PFLAG_EEND (1 << 3) /* This element is self-closing. */ |
#define PFLAG_ATTR (1 << 3) /* The most recent attribute is valid. */ |
|
#define PFLAG_EEND (1 << 4) /* This element is self-closing. */ |
}; |
}; |
|
|
struct alias { |
struct alias { |
Line 103 static const struct alias aliases[] = { |
|
Line 107 static const struct alias aliases[] = { |
|
{ "phrase", NODE_IGNORE }, |
{ "phrase", NODE_IGNORE }, |
{ "primary", NODE_DELETE }, |
{ "primary", NODE_DELETE }, |
{ "property", NODE_PARAMETER }, |
{ "property", NODE_PARAMETER }, |
|
{ "reference", NODE_SECTION }, |
{ "refsect1", NODE_SECTION }, |
{ "refsect1", NODE_SECTION }, |
{ "refsect2", NODE_SECTION }, |
{ "refsect2", NODE_SECTION }, |
{ "refsect3", NODE_SECTION }, |
{ "refsect3", NODE_SECTION }, |
Line 112 static const struct alias aliases[] = { |
|
Line 117 static const struct alias aliases[] = { |
|
{ "secondary", NODE_DELETE }, |
{ "secondary", NODE_DELETE }, |
{ "sect1", NODE_SECTION }, |
{ "sect1", NODE_SECTION }, |
{ "sect2", NODE_SECTION }, |
{ "sect2", NODE_SECTION }, |
|
{ "sect3", NODE_SECTION }, |
|
{ "sect4", NODE_SECTION }, |
{ "sgmltag", NODE_MARKUP }, |
{ "sgmltag", NODE_MARKUP }, |
{ "simpara", NODE_PARA }, |
{ "simpara", NODE_PARA }, |
{ "structfield", NODE_PARAMETER }, |
{ "structfield", NODE_PARAMETER }, |
{ "structname", NODE_TYPE }, |
{ "structname", NODE_TYPE }, |
{ "surname", NODE_PERSONNAME }, |
{ "surname", NODE_PERSONNAME }, |
{ "symbol", NODE_CONSTANT }, |
{ "symbol", NODE_CONSTANT }, |
|
{ "tag", NODE_MARKUP }, |
{ "trademark", NODE_IGNORE }, |
{ "trademark", NODE_IGNORE }, |
{ "ulink", NODE_LINK }, |
{ "ulink", NODE_LINK }, |
{ "userinput", NODE_LITERAL }, |
{ "userinput", NODE_LITERAL }, |
Line 187 static void parse_fd(struct parse *, int); |
|
Line 195 static void parse_fd(struct parse *, int); |
|
|
|
|
|
static void |
static void |
fatal(struct parse *p) |
|
{ |
|
fprintf(stderr, "%s:%d:%d: FATAL: ", p->fname, p->line, p->col); |
|
perror(NULL); |
|
exit(6); |
|
} |
|
|
|
static void |
|
error_msg(struct parse *p, const char *fmt, ...) |
error_msg(struct parse *p, const char *fmt, ...) |
{ |
{ |
va_list ap; |
va_list ap; |
Line 252 xml_text(struct parse *p, const char *word, int sz) |
|
Line 252 xml_text(struct parse *p, const char *word, int sz) |
|
newsz = oldsz + sz; |
newsz = oldsz + sz; |
if (oldsz && (p->flags & PFLAG_SPC)) |
if (oldsz && (p->flags & PFLAG_SPC)) |
newsz++; |
newsz++; |
if ((n->b = realloc(n->b, newsz + 1)) == NULL) |
n->b = xrealloc(n->b, newsz + 1); |
fatal(p); |
|
if (oldsz && (p->flags & PFLAG_SPC)) |
if (oldsz && (p->flags & PFLAG_SPC)) |
n->b[oldsz++] = ' '; |
n->b[oldsz++] = ' '; |
memcpy(n->b + oldsz, word, sz); |
memcpy(n->b + oldsz, word, sz); |
n->b[newsz] = '\0'; |
n->b[newsz] = '\0'; |
p->flags &= ~PFLAG_SPC; |
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
return; |
return; |
} |
} |
|
|
Line 267 xml_text(struct parse *p, const char *word, int sz) |
|
Line 266 xml_text(struct parse *p, const char *word, int sz) |
|
|
|
/* Create a new text node. */ |
/* Create a new text node. */ |
|
|
if ((n = pnode_alloc(p->cur)) == NULL) |
n = pnode_alloc(p->cur); |
fatal(p); |
|
n->node = NODE_TEXT; |
n->node = NODE_TEXT; |
n->spc = (p->flags & PFLAG_SPC) != 0; |
n->flags = ((p->flags & PFLAG_LINE) ? NFLAG_LINE : 0) | |
p->flags &= ~PFLAG_SPC; |
((p->flags & PFLAG_SPC) ? NFLAG_SPC : 0); |
|
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
|
|
/* |
/* |
* If this node follows an in-line macro without intervening |
* If this node follows an in-line macro without intervening |
Line 279 xml_text(struct parse *p, const char *word, int sz) |
|
Line 278 xml_text(struct parse *p, const char *word, int sz) |
|
* and do not keep it open. |
* and do not keep it open. |
*/ |
*/ |
|
|
np = n->spc ? NULL : TAILQ_PREV(n, pnodeq, child); |
np = n->flags & NFLAG_SPC ? NULL : TAILQ_PREV(n, pnodeq, child); |
while (np != NULL) { |
while (np != NULL) { |
switch (pnode_class(np->node)) { |
switch (pnode_class(np->node)) { |
case CLASS_VOID: |
case CLASS_VOID: |
Line 301 xml_text(struct parse *p, const char *word, int sz) |
|
Line 300 xml_text(struct parse *p, const char *word, int sz) |
|
i = 0; |
i = 0; |
while (i < sz && !isspace((unsigned char)word[i])) |
while (i < sz && !isspace((unsigned char)word[i])) |
i++; |
i++; |
if ((n->b = strndup(word, i)) == NULL) |
n->b = xstrndup(word, i); |
fatal(p); |
|
if (i == sz) |
if (i == sz) |
return; |
return; |
while (i < sz && isspace((unsigned char)word[i])) |
while (i < sz && isspace((unsigned char)word[i])) |
Line 314 xml_text(struct parse *p, const char *word, int sz) |
|
Line 312 xml_text(struct parse *p, const char *word, int sz) |
|
|
|
/* Put any remaining text into a second node. */ |
/* Put any remaining text into a second node. */ |
|
|
if ((n = pnode_alloc(p->cur)) == NULL) |
n = pnode_alloc(p->cur); |
fatal(p); |
|
n->node = NODE_TEXT; |
n->node = NODE_TEXT; |
n->spc = 1; |
n->flags |= NFLAG_SPC; |
word += i; |
word += i; |
sz -= i; |
sz -= i; |
} |
} |
if ((n->b = strndup(word, sz)) == NULL) |
n->b = xstrndup(word, sz); |
fatal(p); |
|
|
|
/* The new node remains open for later pnode_closetext(). */ |
/* The new node remains open for later pnode_closetext(). */ |
|
|
Line 365 pnode_closetext(struct parse *p, int check_last_word) |
|
Line 361 pnode_closetext(struct parse *p, int check_last_word) |
|
|
|
/* Move the last word into its own node, for use with .Pf. */ |
/* Move the last word into its own node, for use with .Pf. */ |
|
|
if ((n = pnode_alloc(p->cur)) == NULL) |
n = pnode_alloc_text(p->cur, last_word); |
fatal(p); |
n->flags |= NFLAG_SPC; |
n->node = NODE_TEXT; |
|
n->spc = 1; |
|
if ((n->b = strdup(last_word)) == NULL) |
|
fatal(p); |
|
} |
} |
|
|
static void |
static void |
Line 380 xml_entity(struct parse *p, const char *name) |
|
Line 372 xml_entity(struct parse *p, const char *name) |
|
struct pnode *n; |
struct pnode *n; |
const char *ccp; |
const char *ccp; |
char *cp; |
char *cp; |
|
unsigned int codepoint; |
enum pstate pstate; |
enum pstate pstate; |
|
|
if (p->del > 0) |
if (p->del > 0) |
Line 409 xml_entity(struct parse *p, const char *name) |
|
Line 402 xml_entity(struct parse *p, const char *name) |
|
if ((ccp = pnode_getattr_raw(n, |
if ((ccp = pnode_getattr_raw(n, |
ATTRKEY_SYSTEM, NULL)) != NULL) { |
ATTRKEY_SYSTEM, NULL)) != NULL) { |
parse_file(p, -1, ccp); |
parse_file(p, -1, ccp); |
p->flags &= ~PFLAG_SPC; |
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
return; |
return; |
} |
} |
if ((ccp = pnode_getattr_raw(n, |
if ((ccp = pnode_getattr_raw(n, |
ATTRKEY_DEFINITION, NULL)) == NULL) |
ATTRKEY_DEFINITION, NULL)) == NULL) |
continue; |
continue; |
if ((cp = strdup(ccp)) == NULL) |
cp = xstrdup(ccp); |
fatal(p); |
|
pstate = PARSE_ELEM; |
pstate = PARSE_ELEM; |
parse_string(p, cp, strlen(cp), &pstate, 0); |
parse_string(p, cp, strlen(cp), &pstate, 0); |
p->flags &= ~PFLAG_SPC; |
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
free(cp); |
free(cp); |
return; |
return; |
} |
} |
} |
} |
|
if (*name == '#') { |
|
codepoint = strtonum(name + 1, 0, 0x10ffff, &ccp); |
|
if (ccp == NULL) { |
|
n = pnode_alloc(p->cur); |
|
xasprintf(&n->b, "\\[u%4.4X]", codepoint); |
|
goto done; |
|
} |
|
} |
error_msg(p, "unknown entity &%s;", name); |
error_msg(p, "unknown entity &%s;", name); |
return; |
return; |
} |
} |
|
|
/* Create, append, and close out an entity node. */ |
/* Create, append, and close out an entity node. */ |
if ((n = pnode_alloc(p->cur)) == NULL || |
n = pnode_alloc(p->cur); |
(n->b = strdup(entity->roff)) == NULL) |
n->b = xstrdup(entity->roff); |
fatal(p); |
done: |
n->node = NODE_ESCAPE; |
n->node = NODE_ESCAPE; |
n->spc = (p->flags & PFLAG_SPC) != 0; |
n->flags = ((p->flags & PFLAG_LINE) ? NFLAG_LINE : 0) | |
p->flags &= ~PFLAG_SPC; |
((p->flags & PFLAG_SPC) ? NFLAG_SPC : 0); |
|
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
} |
} |
|
|
/* |
/* |
Line 504 xml_elem_start(struct parse *p, const char *name) |
|
Line 505 xml_elem_start(struct parse *p, const char *name) |
|
break; |
break; |
} |
} |
|
|
if ((n = pnode_alloc(p->cur)) == NULL) |
n = pnode_alloc(p->cur); |
fatal(p); |
|
|
|
/* |
/* |
* Some elements are self-closing. |
* Some elements are self-closing. |
Line 517 xml_elem_start(struct parse *p, const char *name) |
|
Line 517 xml_elem_start(struct parse *p, const char *name) |
|
case NODE_DOCTYPE: |
case NODE_DOCTYPE: |
case NODE_ENTITY: |
case NODE_ENTITY: |
case NODE_SBR: |
case NODE_SBR: |
|
case NODE_VOID: |
p->flags |= PFLAG_EEND; |
p->flags |= PFLAG_EEND; |
break; |
break; |
default: |
default: |
Line 525 xml_elem_start(struct parse *p, const char *name) |
|
Line 526 xml_elem_start(struct parse *p, const char *name) |
|
switch (pnode_class(p->ncur)) { |
switch (pnode_class(p->ncur)) { |
case CLASS_LINE: |
case CLASS_LINE: |
case CLASS_ENCL: |
case CLASS_ENCL: |
n->spc = (p->flags & PFLAG_SPC) != 0; |
n->flags = ((p->flags & PFLAG_LINE) ? NFLAG_LINE : 0) | |
|
((p->flags & PFLAG_SPC) ? NFLAG_SPC : 0); |
break; |
break; |
case CLASS_NOFILL: |
case CLASS_NOFILL: |
p->nofill++; |
p->nofill++; |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
default: |
default: |
n->spc = 1; |
n->flags |= NFLAG_SPC; |
break; |
break; |
} |
} |
p->cur = n; |
p->cur = n; |
Line 551 xml_attrkey(struct parse *p, const char *name) |
|
Line 553 xml_attrkey(struct parse *p, const char *name) |
|
const char *value; |
const char *value; |
enum attrkey key; |
enum attrkey key; |
|
|
if (p->del > 0 || p->ncur == NODE_IGNORE || *name == '\0') |
if (p->del > 0 || p->ncur >= NODE_UNKNOWN || *name == '\0') |
return; |
return; |
|
|
if ((p->ncur == NODE_DOCTYPE || p->ncur == NODE_ENTITY) && |
if ((p->ncur == NODE_DOCTYPE || p->ncur == NODE_ENTITY) && |
Line 565 xml_attrkey(struct parse *p, const char *name) |
|
Line 567 xml_attrkey(struct parse *p, const char *name) |
|
p->flags &= ~PFLAG_ATTR; |
p->flags &= ~PFLAG_ATTR; |
return; |
return; |
} |
} |
if ((a = calloc(1, sizeof(*a))) == NULL) |
a = xcalloc(1, sizeof(*a)); |
fatal(p); |
|
|
|
a->key = key; |
a->key = key; |
a->val = ATTRVAL__MAX; |
a->val = ATTRVAL__MAX; |
if (value == NULL) { |
if (value == NULL) { |
a->rawval = NULL; |
a->rawval = NULL; |
p->flags |= PFLAG_ATTR; |
p->flags |= PFLAG_ATTR; |
} else { |
} else { |
if ((a->rawval = strdup(value)) == NULL) |
a->rawval = xstrdup(value); |
fatal(p); |
|
p->flags &= ~PFLAG_ATTR; |
p->flags &= ~PFLAG_ATTR; |
} |
} |
TAILQ_INSERT_TAIL(&p->cur->attrq, a, child); |
TAILQ_INSERT_TAIL(&p->cur->attrq, a, child); |
Line 588 xml_attrval(struct parse *p, const char *name) |
|
Line 587 xml_attrval(struct parse *p, const char *name) |
|
{ |
{ |
struct pattr *a; |
struct pattr *a; |
|
|
if (p->del > 0 || p->ncur == NODE_IGNORE || |
if (p->del > 0 || p->ncur >= NODE_UNKNOWN || |
(p->flags & PFLAG_ATTR) == 0) |
(p->flags & PFLAG_ATTR) == 0) |
return; |
return; |
if ((a = TAILQ_LAST(&p->cur->attrq, pattrq)) == NULL) |
if ((a = TAILQ_LAST(&p->cur->attrq, pattrq)) == NULL) |
return; |
return; |
if ((a->val = attrval_parse(name)) == ATTRVAL__MAX && |
if ((a->val = attrval_parse(name)) == ATTRVAL__MAX) |
(a->rawval = strdup(name)) == NULL) |
a->rawval = xstrdup(name); |
fatal(p); |
|
p->flags &= ~PFLAG_ATTR; |
p->flags &= ~PFLAG_ATTR; |
} |
} |
|
|
Line 622 xml_elem_end(struct parse *p, const char *name) |
|
Line 620 xml_elem_end(struct parse *p, const char *name) |
|
if (p->del == 0) |
if (p->del == 0) |
pnode_closetext(p, 0); |
pnode_closetext(p, 0); |
|
|
|
n = p->cur; |
node = name == NULL ? p->ncur : xml_name2node(p, name); |
node = name == NULL ? p->ncur : xml_name2node(p, name); |
|
|
switch (node) { |
switch (node) { |
Line 634 xml_elem_end(struct parse *p, const char *name) |
|
Line 633 xml_elem_end(struct parse *p, const char *name) |
|
case NODE_UNKNOWN: |
case NODE_UNKNOWN: |
break; |
break; |
case NODE_INCLUDE: |
case NODE_INCLUDE: |
n = p->cur; |
p->cur = n->parent; |
p->cur = p->cur->parent; |
|
cp = pnode_getattr_raw(n, ATTRKEY_HREF, NULL); |
cp = pnode_getattr_raw(n, ATTRKEY_HREF, NULL); |
if (cp == NULL) |
if (cp == NULL) |
error_msg(p, "<xi:include> element " |
error_msg(p, "<xi:include> element " |
Line 643 xml_elem_end(struct parse *p, const char *name) |
|
Line 641 xml_elem_end(struct parse *p, const char *name) |
|
else |
else |
parse_file(p, -1, cp); |
parse_file(p, -1, cp); |
pnode_unlink(n); |
pnode_unlink(n); |
p->flags &= ~PFLAG_SPC; |
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
break; |
break; |
case NODE_DOCTYPE: |
case NODE_DOCTYPE: |
case NODE_SBR: |
case NODE_SBR: |
|
case NODE_VOID: |
p->flags &= ~PFLAG_EEND; |
p->flags &= ~PFLAG_EEND; |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
default: |
default: |
if (p->cur == NULL || node != p->cur->node) { |
if (n == NULL || node != n->node) { |
warn_msg(p, "element not open: </%s>", name); |
warn_msg(p, "element not open: </%s>", name); |
break; |
break; |
} |
} |
Line 664 xml_elem_end(struct parse *p, const char *name) |
|
Line 663 xml_elem_end(struct parse *p, const char *name) |
|
* obviously better than discarding it or crashing. |
* obviously better than discarding it or crashing. |
*/ |
*/ |
|
|
if (p->cur->parent != NULL || node == NODE_DOCTYPE) { |
if (n->parent != NULL || node == NODE_DOCTYPE) { |
p->cur = p->cur->parent; |
p->cur = n->parent; |
if (p->cur != NULL) |
if (p->cur != NULL) |
p->ncur = p->cur->node; |
p->ncur = p->cur->node; |
} else |
} else |
p->tree->flags |= TREE_CLOSED; |
p->tree->flags |= TREE_CLOSED; |
p->flags &= ~PFLAG_SPC; |
p->flags &= ~(PFLAG_LINE | PFLAG_SPC); |
|
|
|
/* Include a file containing entity declarations. */ |
|
|
|
if (node == NODE_ENTITY && strcmp("%", |
|
pnode_getattr_raw(n, ATTRKEY_NAME, "")) == 0 && |
|
(cp = pnode_getattr_raw(n, ATTRKEY_SYSTEM, NULL)) != NULL) |
|
parse_file(p, -1, cp); |
|
|
break; |
break; |
} |
} |
assert(p->del == 0); |
assert(p->del == 0); |
Line 681 parse_alloc(int warn) |
|
Line 688 parse_alloc(int warn) |
|
{ |
{ |
struct parse *p; |
struct parse *p; |
|
|
if ((p = calloc(1, sizeof(*p))) == NULL) |
p = xcalloc(1, sizeof(*p)); |
return NULL; |
p->tree = xcalloc(1, sizeof(*p->tree)); |
|
|
if ((p->tree = calloc(1, sizeof(*p->tree))) == NULL) { |
|
free(p); |
|
return NULL; |
|
} |
|
if (warn) |
if (warn) |
p->flags |= PFLAG_WARN; |
p->flags |= PFLAG_WARN; |
else |
else |
Line 781 parse_string(struct parse *p, char *b, size_t rlen, |
|
Line 783 parse_string(struct parse *p, char *b, size_t rlen, |
|
break; |
break; |
if (isspace((unsigned char)b[pend])) { |
if (isspace((unsigned char)b[pend])) { |
p->flags |= PFLAG_SPC; |
p->flags |= PFLAG_SPC; |
if (b[pend] == '\n') |
if (b[pend] == '\n') { |
|
p->flags |= PFLAG_LINE; |
pws = pend + 1; |
pws = pend + 1; |
|
} |
increment(p, b, &pend, refill); |
increment(p, b, &pend, refill); |
continue; |
continue; |
} |
} |