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