1 // BML parser/serializer/accessor which doesn't use STL
2 // Developed for DeaDBeeF Player by Alexey Yakovenko
3 // Permission is granted to use this source code under the same license, as the Game Music Emu
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8
9 #include "Bml_Parser.h"
10
Bml_Parser()11 Bml_Parser::Bml_Parser() {
12 nodes = NULL;
13 tail = NULL;
14 }
15
~Bml_Parser()16 Bml_Parser::~Bml_Parser() {
17 clearDocument();
18 }
19
clearDocument()20 void Bml_Parser::clearDocument ()
21 {
22 while (nodes) {
23 if (nodes->key) {
24 free (nodes->key);
25 }
26 if (nodes->value) {
27 free (nodes->value);
28 }
29 nodes = nodes->next;
30 }
31 tail = NULL;
32 }
33
addNode(const char * path,const char * value)34 void Bml_Parser::addNode (const char *path, const char *value)
35 {
36 Bml_Node *node = new Bml_Node;
37 memset (node, 0, sizeof (Bml_Node));
38 node->key = strdup (path);
39 if (value) {
40 node->value = strdup (value);
41 }
42 if (tail) {
43 tail->next = node;
44 }
45 else {
46 nodes = node;
47 }
48 tail = node;
49 }
50
parseDocument(const char * source,size_t max_length)51 void Bml_Parser::parseDocument( const char * source, size_t max_length )
52 {
53 clearDocument();
54
55 const char *p = source;
56 const char *end = p + max_length;
57
58 char path[200] = "";
59
60 int indents[100];
61 int num_indents = 0;
62
63 while (p < end) {
64 int indent = 0;
65 while (p < end && *p == 0x20) {
66 indent++;
67 p++;
68 }
69 while (num_indents > 0 && indents[num_indents-1] >= indent) {
70 char *colon = strrchr (path, ':');
71 if (colon) {
72 *colon = 0;
73 }
74 num_indents--;
75 }
76 indents[num_indents++] = indent;
77
78 const char *e = p;
79 while (e < end && *e != '\n') {
80 e++;
81 }
82 if (e == p || indent == 0) {
83 path[0] = 0;
84 }
85 if (e != p) {
86 char name[e-p+1];
87 memcpy (name, p, e-p);
88 name[e-p] = 0;
89
90 char *colon = strrchr (name, ':');
91 if (colon) {
92 *colon = 0;
93 }
94 if (indent != 0) {
95 strcat (path, ":");
96 }
97
98 strcat (path, name);
99 if (colon) {
100 addNode (path, colon+1);
101 }
102 else {
103 addNode (path, NULL);
104 }
105 }
106 p = e;
107 p++;
108 }
109 }
110
walkToNode(const char * _path) const111 Bml_Parser::Bml_Node *Bml_Parser::walkToNode (const char *_path) const
112 {
113 Bml_Node *node = nodes;
114
115 char *path = strdup(_path);
116
117 // split on : or [
118 char *p = path;
119 while (*p) {
120 if (*p == '[') {
121 int n = atoi(p+1) + 1;
122 char *e = p;
123 while (*e && *e != ':') {
124 e++;
125 }
126 memmove (p, e, strlen (e)+1);
127 // find n'th node starting with [p..e]
128 for (; n && node; node = node->next) {
129 size_t l = p-path;
130 if (!strncmp (node->key, path, l) && !node->key[l]) {
131 n--;
132 }
133 }
134 }
135 p++;
136 }
137 for (; node; node = node->next) {
138 if (!strcmp (node->key, path)) {
139 free (path);
140 return node;
141 }
142 }
143 free (path);
144 return NULL;
145 }
146
enumValue(const char * path) const147 const char * Bml_Parser::enumValue(const char *path) const
148 {
149 const Bml_Node *node = walkToNode (path);
150 if (node) {
151 return node->value;
152 }
153 return NULL;
154 }
155
setValue(const char * path,const char * value)156 void Bml_Parser::setValue(const char *path, const char *value)
157 {
158 Bml_Node *node = walkToNode (path);
159 if (node) {
160 free (node->value);
161 node->value = strdup (value);
162 return;
163 }
164 addNode(path, value);
165 }
166
setValue(const char * path,long value)167 void Bml_Parser::setValue(const char *path, long value)
168 {
169 char str[15];
170 snprintf (str, sizeof (str), "%ld", value);
171 setValue( path, str );
172 }
173
serialize(char * buffer,int size) const174 void Bml_Parser::serialize(char *buffer, int size) const
175 {
176 size_t l = 0;
177 #define APPEND(x) l = strlen(x); if (l > size) return; strcat (buffer, x); buffer += l; size -= l;
178 int first = 1;
179 for (Bml_Node *node = nodes; node; node = node->next) {
180 int indent = 0;
181 const char *p = node->key;
182 const char *colon = strchr (node->key, ':');
183 while (colon) {
184 indent++;
185 p = colon + 1;
186 colon = strchr (p, ':');
187 }
188 for (int i = 0; i < indent; i++) {
189 APPEND(" ");
190 }
191 if (!indent && !first) {
192 APPEND("\n");
193 }
194 APPEND(p);
195 if (node->value) {
196 APPEND(":");
197 APPEND(node->value);
198 }
199 APPEND("\n");
200 first = 0;
201 }
202 }
203