blob: eb2654761ad81c01ad5ac7cb734ee1da613b1098 [file] [log] [blame]
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "ezxml.h"
#include "read_xml_util.h"
#include "read_xml_arch_file.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 bool 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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Parent->line,
"Element '%s' not found within element '%s'.\n", Name, Parent->name);
}
}
/* Look at next tag with same name and error out if exists */
if (Cur != NULL && Cur->next) {
vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), Parent->line ,
"Element '%s' found twice within element '%s'.\n", Name, Parent->name);
}
return Cur;
}
/* Finds child element with given name and returns it. */
ezxml_t FindFirstElement(INP ezxml_t Parent, INP const char *Name,
INP bool 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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Parent->line,
"Element '%s' not found within element '%s'.\n", Name, Parent->name);
}
}
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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Node->line,
"Element '%s' within element '%s' does match expected element type of '%s'\n", Node->name,
(Node->parent ? (Node->parent->name ? Node->parent->name : Node->name) : "ROOT_TAG") ,Name);
}
}
/* 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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Node->line,
"Node '%s' has invalid property %s=\"%s\".\n", Node->name, Node->attr[0], Node->attr[1]);
}
/* Shouldn't have non-whitespace text */
Txt = Node->txt;
while (*Txt) {
if (!IsWhitespace(*Txt)) {
vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), Node->line,
"Node '%s' has unexpected text '%s' within it.\n", Node->name, Node->txt);
}
++Txt;
}
/* We shouldn't have child items left */
Cur = Node->child;
if (Cur) {
vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), Node->line,
"Node '%s' has invalid child node '%s'.\n", Node->name, Cur->name);
}
/* Now actually unlink and free the node */
ezxml_remove(Node);
}
/* Returns true if character is whatspace between tokens */
bool 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 bool Required) {
const char *Res;
Res = ezxml_attr(Parent, Name);
if (Required) {
if (NULL == Res) {
vpr_throw(VPR_ERROR_ARCH, get_arch_file_name(), Parent->line,
"Required property '%s' not found for element '%s'.\n", Name, Parent->name);
}
}
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) {
bool 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;
bool 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;
bool 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 bool 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 bool 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 bool attribute matching Name in XML tag Parent and return it if exists.
Removes attribute from Parent */
extern bool GetboolProperty(INP ezxml_t Parent, INP char *Name,
INP bool Required, INP bool default_value) {
const char * Prop;
bool 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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Parent->line,
"Unknown value %s for bool attribute %s in %s", Prop, Name, Parent->name);
}
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_throw(VPR_ERROR_ARCH, get_arch_file_name(), Node->line,
"Expected node '%s' to have %d child elements, but none found.\n", Node->name, min_count);
}
return Count;
}