1#include <vector>
2#include <iostream>
3#include <fstream>
4#include <stack>
5#include <stdio.h>
6
7#include "port.h"
8#include "bml.h"
9
10bml_node::bml_node()
11{
12 type = CHILD;
13 depth = -1;
14}
15
16static inline int islf(char c)
17{
18 return (c == '\r' || c == '\n');
19}
20
21static inline int isblank(char c)
22{
23 return (c == ' ' || c == '\t');
24}
25
26static inline int isblankorlf(char c)
27{
28 return (islf(c) || isblank(c));
29}
30
31static inline int isalnum(char c)
32{
33 return ((c >= 'a' && c <= 'z') ||
34 (c >= 'A' && c <= 'Z') ||
35 (c >= '0' && c <= '9'));
36}
37
38static inline int bml_valid(char c)
39{
40 return (isalnum(c) || c == '-');
41}
42
43static std::string trim(std::string str)
44{
45 int start;
46 int end;
47 for (start = 0; str[start] && start != (int)str.length() && isblank(str[start]); start++) {}
48 if (start >= (int)str.length())
49 return std::string("");
50 for (end = str.length() - 1; isblankorlf(str[end]); end--) {}
51 return str.substr(start, end - start + 1);
52}
53
54static std::string trimcomments(std::string str)
55{
56 int end = str.length();
57 size_t comment = str.find("//");
58 if (comment != std::string::npos)
59 end = comment;
60
61 for (int i = end - 1; i >= 0; i--)
62 {
63 if (!isblankorlf(str[i]))
64 {
65 end = i + 1;
66 break;
67 }
68 }
69
70 return str.substr(0, end);
71}
72
73static inline int bml_read_depth(std::string &data)
74{
75 size_t depth;
76 for (depth = 0; isblank(data[depth]) && depth < data.length(); depth++) {}
77 return depth == data.length() ? -1 : depth;
78}
79
80static void bml_parse_depth(bml_node &node, std::string &line)
81{
82 unsigned int depth = bml_read_depth(line);
83 line.erase(0, depth);
84 node.depth = depth;
85}
86
87static void bml_parse_name(bml_node &node, std::string &line)
88{
89 int len;
90
91 for (len = 0; bml_valid(line[len]); len++) {};
92
93 node.name = trim(line.substr(0, len));
94 line.erase(0, len);
95}
96
97static void bml_parse_data(bml_node &node, std::string &line)
98{
99 int len;
100
101 if (line[0] == '=' && line[1] == '\"')
102 {
103 len = 2;
104 while (line[len] && line[len] != '\"' && !islf(line[len]))
105 len++;
106 if (line[len] != '\"')
107 return;
108
109 node.data = line.substr(2, len - 2);
110 line.erase(0, len + 1);
111 }
112 else if (line[0] == '=')
113 {
114 len = 1;
115 while (line[len] && !islf(line[len]) && line[len] != '"' && line[len] != ' ')
116 len++;
117 if (line[len] == '\"')
118 return;
119 node.data = line.substr(1, len - 1);
120 line.erase(0, len);
121 }
122 else if (line[0] == ':')
123 {
124 len = 1;
125 while (line[len] && !islf(line[len]))
126 len++;
127 node.data = trim(line.substr(1, len - 1));
128 line.erase(0, len);
129 }
130
131 return;
132}
133
134static std::string bml_read_line(std::ifstream &fd)
135{
136 std::string line;
137
138 while (fd)
139 {
140 std::getline(fd, line);
141 line = trimcomments(line);
142 if (!line.empty())
143 {
144 return line;
145 }
146 }
147
148 return std::string("");
149}
150
151static void bml_parse_attr(bml_node &node, std::string &line)
152{
153 int len;
154
155 while (line.length() > 0)
156 {
157 if (!isblank(line[0]))
158 return;
159
160 while (isblank(line[0]))
161 line.erase(0, 1);
162
163 bml_node n;
164 len = 0;
165 while (bml_valid(line[len]))
166 len++;
167 if (len == 0)
168 return;
169 n.name = trim(line.substr(0, len));
170 line.erase(0, len);
171 bml_parse_data(n, line);
172 n.depth = node.depth + 1;
173 n.type = bml_node::ATTRIBUTE;
174 node.child.push_back(n);
175 }
176}
177
178static int contains_space(const char *str)
179{
180 for (int i = 0; str[i]; i++)
181 {
182 if (isblank(str[i]))
183 return 1;
184 }
185
186 return 0;
187}
188
189static void bml_print_node(bml_node &node, int depth)
190{
191 int i;
192
193 for (i = 0; i < depth * 2; i++)
194 {
195 printf(" ");
196 }
197
198 if (!node.name.empty())
199 printf("%s", node.name.c_str());
200
201 if (!node.data.empty())
202 {
203 if (contains_space(node.data.c_str()))
204 printf("=\"%s\"", node.data.c_str());
205 else
206 printf(": %s", node.data.c_str());
207 }
208 for (i = 0; i < (int)node.child.size() && node.child[i].type == bml_node::ATTRIBUTE; i++)
209 {
210 if (!node.child[i].name.empty())
211 {
212 printf(" %s", node.child[i].name.c_str());
213 if (!node.child[i].data.empty())
214 {
215 if (contains_space(node.child[i].data.c_str()))
216 printf("=\"%s\"", node.child[i].data.c_str());
217 else
218 printf("=%s", node.child[i].data.c_str());
219 }
220 }
221 }
222
223 if (depth >= 0)
224 printf("\n");
225
226 for (; i < (int)node.child.size(); i++)
227 {
228 bml_print_node(node.child[i], depth + 1);
229 }
230
231 if (depth == 0)
232 printf("\n");
233}
234
235void bml_node::print()
236{
237 bml_print_node(*this, -1);
238}
239
240void bml_node::parse(std::ifstream &fd)
241{
242 std::stack<bml_node *> nodestack;
243 nodestack.push(this);
244
245 while (fd)
246 {
247 bml_node newnode;
248 std::string line = bml_read_line(fd);
249 if (line.empty())
250 return;
251
252 int line_depth = bml_read_depth(line);
253 while (line_depth <= nodestack.top()->depth && nodestack.size() > 1)
254 nodestack.pop();
255
256 bml_parse_depth(newnode, line);
257 bml_parse_name(newnode, line);
258 bml_parse_data(newnode, line);
259 bml_parse_attr(newnode, line);
260
261 nodestack.top()->child.push_back(newnode);
262 nodestack.push(&nodestack.top()->child.back());
263 }
264
265 return;
266}
267
268bml_node *bml_node::find_subnode(std::string name)
269{
270 unsigned int i;
271
272 for (i = 0; i < child.size(); i++)
273 {
274 if (name.compare(child[i].name) == 0)
275 return &child[i];
276 }
277
278 return NULL;
279}
280
281bool bml_node::parse_file(std::string filename)
282{
283 std::ifstream file(filename.c_str(), std::ios_base::binary);
284
285 if (!file)
286 return false;
287
288 parse(file);
289
290 return true;
291}
292