mystring.c had a little (non-)error.
[xmlparser.git] / xmlparser.c
blob5f2f4035c1521ae20cda9b1f7bca98a04cb56afe
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5 #include "xmlparser.h"
6 #include "myarray.h"
7 #include "mystring.h"
9 const char *XML_E_VALUE_NO_PARENT = "Value without a parent!";
10 const char *XML_E_INVALID_CHAR = "Invalid character in current parsing status!";
11 const char *XML_E_INVALID_CLOSING = "Trying to close a tag that is not the current one!";
12 const char *XML_E_UNNAMED_TAG = "Trying to open a tag that has no name!";
13 const char *XML_E_UNCLOSED_TAGS = "Not all tags have been closed!";
15 // Creates an empty XmlTag
16 // Arguments:
17 // * parent: the parent of the tag being created (may be NULL)
18 // Returns:
19 // An XmlTag, with all the fields ready for usage
20 XmlTag xml_tag_new(XmlTag parent) {
21 XmlTag tag = malloc(sizeof(struct XmlTag));
22 tag->name = mystring_new(1,1);
23 tag->children = myarray_new(1, 1, sizeof(XmlTag));
24 tag->parent = parent;
25 tag->value = mystring_new(1,1);
27 return tag;
30 // Frees an XmlTag and all its fields
31 // Arguments:
32 // * tag: the tag to free
33 void xml_tag_free(XmlTag tag) {
34 mystring_free(tag->name);
35 myarray_free(tag->children);
36 mystring_free(tag->value);
37 free(tag);
40 // Frees an entire tree of XmlTags
41 // Arguments:
42 // * root: the root of the tree you wish to free
43 void xml_tree_free(XmlTag root) {
44 int i;
45 for (i = 0; i < root->children->len; i++) {
46 xml_tree_free(myarray_get(root->children, XmlTag, i));
48 xml_tag_free(root);
51 void xml_print_tree_helper(XmlTag tag) {
52 static int depth = 0;
53 int i;
54 printf("%*s%s; ", depth*4, "\0", tag->name->str);
55 if (tag->children->len > 0) {
56 printf("children: %d; ", tag->children->len);
58 if (tag->value->len > 0) {
59 printf("value: %s; ", tag->value->str);
61 printf("\n");
62 depth++;
63 for (i = 0; i < tag->children->len; i++) {
64 xml_print_tree_helper(myarray_get(tag->children, XmlTag, i));
66 depth--;
69 // Prints the tag tree
70 // Arguments:
71 // * tag: This should be the root of the tree, and it won't be printed.
72 void xml_tree_print(XmlTag tag) {
73 int i;
74 for (i = 0; i < tag->children->len; i++) {
75 xml_print_tree_helper(myarray_get(tag->children, XmlTag, i));
79 // Prints an error
80 // Arguments:
81 // * message: the error message to print
82 // * line: the line at which the error occurred
83 // * col: the column at which the error occurred
84 void xml_error(const char *message, unsigned int line, int col) {
85 printf("ERROR (%u,%d): %s\n", line, col + mystring_strip(NULL) + 1, message);
88 // Checks a tag for validity
89 // Arguments:
90 // * tag: the tag to check
91 // * line the line at which the tag was defined
92 // * the column at which the tag was defined
93 // Returns:
94 // TRUE if the tag has a nonzero length name
95 // FALSE if the tag has a zero length name
96 int xml_tag_check(XmlTag tag, int line, int col) {
97 if (tag->name->len == 0) {
98 XML_STATUS = XML_UNNAMED_TAG;
99 xml_error(XML_E_UNNAMED_TAG, line, col);
100 return FALSE;
102 return TRUE;
105 // This will parse the tagname to extract attributes from it
106 void xml_tag_parse_attributes(XmlTag tag) {
109 // Parses an XML file
110 // Arguments:
111 // * path_to_file: path to the XML file to parse
112 // Returns:
113 // The root of the XML tree, or NULL if an error occurred.
114 // In the latter case, XML_STATUS will be set according to the
115 // error.
116 XmlTag xml_parse(char *path_to_file) {
117 FILE *input = fopen(path_to_file, "r");
118 if (input == NULL) {
119 XML_STATUS = XML_FILE_ERROR;
120 return NULL;
122 MString buffer = mystring_new(1,1);
124 // The tag we're currently processing children/values for
125 XmlTag head = xml_tag_new(NULL);
126 mystring_str_append(head->name, "_ROOT_");
127 XmlTag currenttag = head;
128 // Are we parsing a tag definition?
129 unsigned short opentag = FALSE;
130 // Is the current character escaped?
131 unsigned short escape = FALSE;
133 int i;
134 unsigned int line = 1;
135 char c;
136 while (mystring_getline(buffer, input) != -1) {
137 mystring_strip(buffer);
138 for (i = 0; i < buffer->len; i++) {
139 c = buffer->str[i];
140 switch (c) {
141 case '\\':
142 if (escape) escape = FALSE;
143 else {
144 escape = TRUE;
145 break;
147 case '<':
148 if (!escape) {
149 if (opentag) {
150 XML_STATUS = XML_INVALID_CHAR;
151 xml_error(XML_E_INVALID_CHAR, line, i);
152 mystring_free(buffer);
153 fclose(input);
154 xml_tree_free(head);
155 return NULL;
157 opentag = TRUE;
158 currenttag = xml_tag_new(currenttag);
159 myarray_append(currenttag->parent->children, currenttag);
160 break;
162 case '>':
163 if (opentag) opentag = FALSE;
164 else {
165 XML_STATUS = XML_INVALID_CLOSING;
166 xml_error(XML_E_INVALID_CLOSING, line, i);
167 mystring_free(buffer);
168 fclose(input);
169 xml_tree_free(head);
170 return NULL;
172 if (mystring_has_prefix(currenttag->name, "/")) {
173 if (strcmp(currenttag->name->str + 1, currenttag->parent->name->str)) {
174 printf("%s != %s\n", currenttag->name->str + 1, currenttag->parent->name->str);
175 XML_STATUS = XML_INVALID_CLOSING;
176 xml_error(XML_E_INVALID_CLOSING, line, i-currenttag->name->len - 1);
177 mystring_free(buffer);
178 fclose(input);
179 xml_tree_free(head);
180 return NULL;
182 myarray_remove_index(currenttag->parent->children, currenttag->parent->children->len - 1);
183 XmlTag temp = currenttag;
184 currenttag = temp->parent->parent;
185 xml_tag_free(temp);
186 } else if (mystring_has_suffix(currenttag->name, " /")) {
187 mystring_truncate(currenttag->name, currenttag->name->len - 2);
188 if (!xml_tag_check(currenttag, line, i)) {
189 mystring_free(buffer);
190 fclose(input);
191 xml_tree_free(head);
192 return NULL;
194 currenttag = currenttag->parent;
195 } else {
196 if (!xml_tag_check(currenttag, line, i)) {
197 mystring_free(buffer);
198 fclose(input);
199 xml_tree_free(head);
200 return NULL;
203 break;
204 default:
205 if (currenttag == head) {
206 XML_STATUS = XML_VALUE_NO_PARENT;
207 xml_error(XML_E_VALUE_NO_PARENT, line, i);
208 printf("%s\n", buffer->str);
209 mystring_free(buffer);
210 fclose(input);
211 xml_tree_free(head);
212 return NULL;
214 if (opentag) {
215 mystring_char_append(currenttag->name, c);
216 } else {
217 mystring_char_append(currenttag->value, c);
221 line++;
222 mystring_clear(buffer);
225 if (currenttag != head) {
226 XML_STATUS = XML_UNCLOSED_TAGS;
227 xml_error(XML_E_UNCLOSED_TAGS, 0, 0);
228 mystring_free(buffer);
229 fclose(input);
230 xml_tree_free(head);
231 return NULL;
234 fclose(input);
235 mystring_free(buffer);
236 return head;