377 lines
8.8 KiB
C
377 lines
8.8 KiB
C
|
#include <assert.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "util.h"
|
||
|
#include "ezxml.h"
|
||
|
#include "read_xml_util.h"
|
||
|
|
||
|
/* Finds child element with given name and returns it. Errors out if
|
||
|
* more than one instance exists. */
|
||
|
ezxml_t FindElement(INP ezxml_t Parent, INP const char *Name,
|
||
|
INP boolean Required) {
|
||
|
ezxml_t Cur;
|
||
|
|
||
|
/* Find the first node of correct name */
|
||
|
Cur = ezxml_child(Parent, Name);
|
||
|
|
||
|
/* Error out if node isn't found but they required it */
|
||
|
if (Required) {
|
||
|
if (NULL == Cur) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"[LINE %d] Element '%s' not found within element '%s'.\n",
|
||
|
Parent->line, Name, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Look at next tag with same name and error out if exists */
|
||
|
if (Cur != NULL && Cur->next) {
|
||
|
vpr_printf(
|
||
|
TIO_MESSAGE_ERROR, "[LINE %d] Element '%s' found twice within element '%s'.\n",
|
||
|
Parent->line, Name, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
return Cur;
|
||
|
}
|
||
|
/* Finds child element with given name and returns it. */
|
||
|
ezxml_t FindFirstElement(INP ezxml_t Parent, INP const char *Name,
|
||
|
INP boolean Required) {
|
||
|
ezxml_t Cur;
|
||
|
|
||
|
/* Find the first node of correct name */
|
||
|
Cur = ezxml_child(Parent, Name);
|
||
|
|
||
|
/* Error out if node isn't found but they required it */
|
||
|
if (Required) {
|
||
|
if (NULL == Cur) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"[LINE %d] Element '%s' not found within element '%s'.\n",
|
||
|
Parent->line, Name, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Cur;
|
||
|
}
|
||
|
|
||
|
/* Checks the node is an element with name equal to one given */
|
||
|
void CheckElement(INP ezxml_t Node, INP const char *Name) {
|
||
|
assert(Node != NULL && Name != NULL);
|
||
|
if (0 != strcmp(Node->name, Name)) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"[LINE %d] Element '%s' within element '%s' does match expected "
|
||
|
"element type of '%s'\n", Node->line, Node->name, Node->parent->name,
|
||
|
Name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Checks that the node has no remaining child nodes or property nodes,
|
||
|
* then unlinks the node from the tree which updates pointers and
|
||
|
* frees the memory */
|
||
|
void FreeNode(INOUTP ezxml_t Node) {
|
||
|
ezxml_t Cur;
|
||
|
char *Txt;
|
||
|
|
||
|
/* Shouldn't have unprocessed properties */
|
||
|
if (Node->attr[0]) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[LINE %d] Node '%s' has invalid property %s=\"%s\".\n",
|
||
|
Node->line, Node->name, Node->attr[0], Node->attr[1]);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* Shouldn't have non-whitespace text */
|
||
|
Txt = Node->txt;
|
||
|
while (*Txt) {
|
||
|
if (!IsWhitespace(*Txt)) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"[LINE %d] Node '%s' has unexpected text '%s' within it.\n",
|
||
|
Node->line, Node->name, Node->txt);
|
||
|
exit(1);
|
||
|
}
|
||
|
++Txt;
|
||
|
}
|
||
|
|
||
|
/* We shouldn't have child items left */
|
||
|
Cur = Node->child;
|
||
|
if (Cur) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[LINE %d] Node '%s' has invalid child node '%s'.\n",
|
||
|
Node->line, Node->name, Cur->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
/* Now actually unlink and free the node */
|
||
|
ezxml_remove(Node);
|
||
|
}
|
||
|
|
||
|
/* Returns TRUE if character is whatspace between tokens */
|
||
|
boolean IsWhitespace(char c) {
|
||
|
switch (c) {
|
||
|
case ' ':
|
||
|
case '\t':
|
||
|
case '\r':
|
||
|
case '\n':
|
||
|
return TRUE;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
FindProperty(INP ezxml_t Parent, INP const char *Name, INP boolean Required) {
|
||
|
const char *Res;
|
||
|
|
||
|
Res = ezxml_attr(Parent, Name);
|
||
|
if (Required) {
|
||
|
if (NULL == Res) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR,
|
||
|
"[Line %d] Required property '%s' not found for element '%s'.\n",
|
||
|
Parent->line, Name, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
return Res;
|
||
|
}
|
||
|
|
||
|
/* Count tokens and length in all the Text children nodes of current node. */
|
||
|
extern void CountTokensInString(INP const char *Str, OUTP int *Num,
|
||
|
OUTP int *Len) {
|
||
|
boolean InToken;
|
||
|
*Num = 0;
|
||
|
*Len = 0;
|
||
|
InToken = FALSE;
|
||
|
while (*Str) {
|
||
|
if (IsWhitespace(*Str)) {
|
||
|
InToken = FALSE;
|
||
|
}
|
||
|
|
||
|
else
|
||
|
|
||
|
{
|
||
|
if (!InToken) {
|
||
|
++(*Num);
|
||
|
}
|
||
|
++(*Len);
|
||
|
InToken = TRUE;
|
||
|
}
|
||
|
++Str; /* Advance pointer */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Returns a token list of the text nodes of a given node. This
|
||
|
* unlinks all the text nodes from the document */
|
||
|
extern char **
|
||
|
GetNodeTokens(INP ezxml_t Node) {
|
||
|
int Count, Len;
|
||
|
char *Cur, *Dst;
|
||
|
boolean InToken;
|
||
|
char **Tokens;
|
||
|
|
||
|
/* Count the tokens and find length of token data */
|
||
|
CountTokensInString(Node->txt, &Count, &Len);
|
||
|
|
||
|
/* Error out if no tokens found */
|
||
|
if (Count < 1) {
|
||
|
return NULL;
|
||
|
}
|
||
|
Len = (sizeof(char) * Len) + /* Length of actual data */
|
||
|
(sizeof(char) * Count); /* Null terminators */
|
||
|
|
||
|
/* Alloc the pointer list and data list. Count the final
|
||
|
* empty string we will use as list terminator */
|
||
|
Tokens = (char **) my_malloc(sizeof(char *) * (Count + 1));
|
||
|
Dst = (char *) my_malloc(sizeof(char) * Len);
|
||
|
Count = 0;
|
||
|
|
||
|
/* Copy data to tokens */
|
||
|
Cur = Node->txt;
|
||
|
InToken = FALSE;
|
||
|
while (*Cur) {
|
||
|
if (IsWhitespace(*Cur)) {
|
||
|
if (InToken) {
|
||
|
*Dst = '\0';
|
||
|
++Dst;
|
||
|
}
|
||
|
InToken = FALSE;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
if (!InToken) {
|
||
|
Tokens[Count] = Dst;
|
||
|
++Count;
|
||
|
}
|
||
|
*Dst = *Cur;
|
||
|
++Dst;
|
||
|
InToken = TRUE;
|
||
|
}
|
||
|
++Cur;
|
||
|
}
|
||
|
if (InToken) { /* Null term final token */
|
||
|
*Dst = '\0';
|
||
|
++Dst;
|
||
|
}
|
||
|
ezxml_set_txt(Node, "");
|
||
|
Tokens[Count] = NULL; /* End of tokens marker is a NULL */
|
||
|
|
||
|
/* Return the list */
|
||
|
return Tokens;
|
||
|
}
|
||
|
|
||
|
/* Returns a token list of the text nodes of a given node. This
|
||
|
* does not unlink the text nodes from the document */
|
||
|
extern char **
|
||
|
LookaheadNodeTokens(INP ezxml_t Node) {
|
||
|
int Count, Len;
|
||
|
char *Cur, *Dst;
|
||
|
boolean InToken;
|
||
|
char **Tokens;
|
||
|
|
||
|
/* Count the tokens and find length of token data */
|
||
|
CountTokensInString(Node->txt, &Count, &Len);
|
||
|
|
||
|
/* Error out if no tokens found */
|
||
|
if (Count < 1) {
|
||
|
return NULL;
|
||
|
}
|
||
|
Len = (sizeof(char) * Len) + /* Length of actual data */
|
||
|
(sizeof(char) * Count); /* Null terminators */
|
||
|
|
||
|
/* Alloc the pointer list and data list. Count the final
|
||
|
* empty string we will use as list terminator */
|
||
|
Tokens = (char **) my_malloc(sizeof(char *) * (Count + 1));
|
||
|
Dst = (char *) my_malloc(sizeof(char) * Len);
|
||
|
Count = 0;
|
||
|
|
||
|
/* Copy data to tokens */
|
||
|
Cur = Node->txt;
|
||
|
InToken = FALSE;
|
||
|
while (*Cur) {
|
||
|
if (IsWhitespace(*Cur)) {
|
||
|
if (InToken) {
|
||
|
*Dst = '\0';
|
||
|
++Dst;
|
||
|
}
|
||
|
InToken = FALSE;
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
if (!InToken) {
|
||
|
Tokens[Count] = Dst;
|
||
|
++Count;
|
||
|
}
|
||
|
*Dst = *Cur;
|
||
|
++Dst;
|
||
|
InToken = TRUE;
|
||
|
}
|
||
|
++Cur;
|
||
|
}
|
||
|
if (InToken) { /* Null term final token */
|
||
|
*Dst = '\0';
|
||
|
++Dst;
|
||
|
}
|
||
|
Tokens[Count] = NULL; /* End of tokens marker is a NULL */
|
||
|
|
||
|
/* Return the list */
|
||
|
return Tokens;
|
||
|
}
|
||
|
|
||
|
/* Find integer attribute matching Name in XML tag Parent and return it if exists.
|
||
|
Removes attribute from Parent */
|
||
|
extern int GetIntProperty(INP ezxml_t Parent, INP char *Name,
|
||
|
INP boolean Required, INP int default_value) {
|
||
|
const char * Prop;
|
||
|
int property_value;
|
||
|
|
||
|
property_value = default_value;
|
||
|
Prop = FindProperty(Parent, Name, Required);
|
||
|
if (Prop) {
|
||
|
property_value = my_atoi(Prop);
|
||
|
ezxml_set_attr(Parent, Name, NULL);
|
||
|
}
|
||
|
return property_value;
|
||
|
}
|
||
|
|
||
|
/* Find floating-point attribute matching Name in XML tag Parent and return it if exists.
|
||
|
Removes attribute from Parent */
|
||
|
extern float GetFloatProperty(INP ezxml_t Parent, INP char *Name,
|
||
|
INP boolean Required, INP float default_value) {
|
||
|
|
||
|
const char * Prop;
|
||
|
float property_value;
|
||
|
|
||
|
property_value = default_value;
|
||
|
Prop = FindProperty(Parent, Name, Required);
|
||
|
if (Prop) {
|
||
|
property_value = (float)atof(Prop);
|
||
|
ezxml_set_attr(Parent, Name, NULL);
|
||
|
}
|
||
|
return property_value;
|
||
|
}
|
||
|
|
||
|
/* Find boolean attribute matching Name in XML tag Parent and return it if exists.
|
||
|
Removes attribute from Parent */
|
||
|
extern boolean GetBooleanProperty(INP ezxml_t Parent, INP char *Name,
|
||
|
INP boolean Required, INP boolean default_value) {
|
||
|
|
||
|
const char * Prop;
|
||
|
boolean property_value;
|
||
|
|
||
|
property_value = default_value;
|
||
|
Prop = FindProperty(Parent, Name, Required);
|
||
|
if (Prop) {
|
||
|
if ((strcmp(Prop, "false") == 0) || (strcmp(Prop, "FALSE") == 0)
|
||
|
|| (strcmp(Prop, "False") == 0)) {
|
||
|
property_value = FALSE;
|
||
|
} else if ((strcmp(Prop, "true") == 0) || (strcmp(Prop, "TRUE") == 0)
|
||
|
|| (strcmp(Prop, "True") == 0)) {
|
||
|
property_value = TRUE;
|
||
|
} else {
|
||
|
vpr_printf(
|
||
|
TIO_MESSAGE_ERROR, "[LINE %d] Unknown value %s for boolean attribute %s in %s",
|
||
|
Parent->line, Prop, Name, Parent->name);
|
||
|
exit(1);
|
||
|
}
|
||
|
ezxml_set_attr(Parent, Name, NULL);
|
||
|
}
|
||
|
return property_value;
|
||
|
}
|
||
|
|
||
|
/* Counts number of child elements in a container element.
|
||
|
* Name is the name of the child element.
|
||
|
* Errors if no occurances found. */
|
||
|
extern int CountChildren(INP ezxml_t Node, INP const char *Name,
|
||
|
INP int min_count) {
|
||
|
ezxml_t Cur, sibling;
|
||
|
int Count;
|
||
|
|
||
|
Count = 0;
|
||
|
Cur = Node->child;
|
||
|
sibling = NULL;
|
||
|
|
||
|
if (Cur) {
|
||
|
sibling = Cur->sibling;
|
||
|
}
|
||
|
|
||
|
while (Cur) {
|
||
|
if (strcmp(Cur->name, Name) == 0) {
|
||
|
++Count;
|
||
|
}
|
||
|
Cur = Cur->next;
|
||
|
if (Cur == NULL) {
|
||
|
Cur = sibling;
|
||
|
if (Cur != NULL) {
|
||
|
sibling = Cur->sibling;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* Error if no occurances found */
|
||
|
if (Count < min_count) {
|
||
|
vpr_printf(TIO_MESSAGE_ERROR, "[Line %d] Expected node '%s' to have %d "
|
||
|
"child elements, but none found.\n", Node->line, Node->name, min_count);
|
||
|
exit(1);
|
||
|
}
|
||
|
return Count;
|
||
|
}
|
||
|
|