1 // MyXml.cpp
2
3 #include "StdAfx.h"
4
5 #include "MyXml.h"
6 #include "StringToInt.h"
7
IsValidChar(char c)8 static bool IsValidChar(char c)
9 {
10 return
11 (c >= 'a' && c <= 'z') ||
12 (c >= 'A' && c <= 'Z') ||
13 (c >= '0' && c <= '9') ||
14 c == '-';
15 }
16
IsSpaceChar(char c)17 static bool IsSpaceChar(char c)
18 {
19 return (c == ' ' || c == '\t' || c == 0x0D || c == 0x0A);
20 }
21
22 #define SKIP_SPACES(s) while (IsSpaceChar(*s)) s++;
23
FindProp(const char * propName) const24 int CXmlItem::FindProp(const char *propName) const throw()
25 {
26 FOR_VECTOR (i, Props)
27 if (Props[i].Name == propName)
28 return (int)i;
29 return -1;
30 }
31
GetPropVal(const char * propName) const32 AString CXmlItem::GetPropVal(const char *propName) const
33 {
34 const int index = FindProp(propName);
35 if (index >= 0)
36 return Props[(unsigned)index].Value;
37 return AString();
38 }
39
IsTagged(const char * tag) const40 bool CXmlItem::IsTagged(const char *tag) const throw()
41 {
42 return (IsTag && Name == tag);
43 }
44
FindSubTag(const char * tag) const45 int CXmlItem::FindSubTag(const char *tag) const throw()
46 {
47 FOR_VECTOR (i, SubItems)
48 if (SubItems[i].IsTagged(tag))
49 return (int)i;
50 return -1;
51 }
52
FindSubTag_GetPtr(const char * tag) const53 const CXmlItem *CXmlItem::FindSubTag_GetPtr(const char *tag) const throw()
54 {
55 FOR_VECTOR (i, SubItems)
56 {
57 const CXmlItem *p = &SubItems[i];
58 if (p->IsTagged(tag))
59 return p;
60 }
61 return NULL;
62 }
63
GetSubString() const64 AString CXmlItem::GetSubString() const
65 {
66 if (SubItems.Size() == 1)
67 {
68 const CXmlItem &item = SubItems[0];
69 if (!item.IsTag)
70 return item.Name;
71 }
72 return AString();
73 }
74
GetSubStringPtr() const75 const AString * CXmlItem::GetSubStringPtr() const throw()
76 {
77 if (SubItems.Size() == 1)
78 {
79 const CXmlItem &item = SubItems[0];
80 if (!item.IsTag)
81 return &item.Name;
82 }
83 return NULL;
84 }
85
GetSubStringForTag(const char * tag) const86 AString CXmlItem::GetSubStringForTag(const char *tag) const
87 {
88 const CXmlItem *item = FindSubTag_GetPtr(tag);
89 if (item)
90 return item->GetSubString();
91 return AString();
92 }
93
ParseItem(const char * s,int numAllowedLevels)94 const char * CXmlItem::ParseItem(const char *s, int numAllowedLevels)
95 {
96 SKIP_SPACES(s)
97
98 const char *beg = s;
99 for (;;)
100 {
101 char c;
102 c = *s; if (c == 0 || c == '<') break; s++;
103 c = *s; if (c == 0 || c == '<') break; s++;
104 }
105 if (*s == 0)
106 return NULL;
107 {
108 const size_t num = (size_t)(s - beg);
109 if (num)
110 {
111 IsTag = false;
112 Name.SetFrom_Chars_SizeT(beg, num);
113 return s;
114 }
115 }
116
117 IsTag = true;
118
119 s++;
120 SKIP_SPACES(s)
121
122 beg = s;
123 for (;; s++)
124 if (!IsValidChar(*s))
125 break;
126 if (s == beg || *s == 0)
127 return NULL;
128 Name.SetFrom_Chars_SizeT(beg, (size_t)(s - beg));
129
130 for (;;)
131 {
132 beg = s;
133 SKIP_SPACES(s)
134 if (*s == '/')
135 {
136 s++;
137 // SKIP_SPACES(s)
138 if (*s != '>')
139 return NULL;
140 return s + 1;
141 }
142 if (*s == '>')
143 {
144 s++;
145 if (numAllowedLevels == 0)
146 return NULL;
147 SubItems.Clear();
148 for (;;)
149 {
150 SKIP_SPACES(s)
151 if (s[0] == '<' && s[1] == '/')
152 break;
153 CXmlItem &item = SubItems.AddNew();
154 s = item.ParseItem(s, numAllowedLevels - 1);
155 if (!s)
156 return NULL;
157 }
158
159 s += 2;
160 const unsigned len = Name.Len();
161 const char *name = Name.Ptr();
162 for (unsigned i = 0; i < len; i++)
163 if (*s++ != *name++)
164 return NULL;
165 // s += len;
166 if (s[0] != '>')
167 return NULL;
168 return s + 1;
169 }
170 if (beg == s)
171 return NULL;
172
173 // ReadProperty
174 CXmlProp &prop = Props.AddNew();
175
176 beg = s;
177 for (;; s++)
178 {
179 char c = *s;
180 if (!IsValidChar(c))
181 break;
182 }
183 if (s == beg)
184 return NULL;
185 prop.Name.SetFrom_Chars_SizeT(beg, (size_t)(s - beg));
186
187 SKIP_SPACES(s)
188 if (*s != '=')
189 return NULL;
190 s++;
191 SKIP_SPACES(s)
192 if (*s != '\"')
193 return NULL;
194 s++;
195
196 beg = s;
197 for (;;)
198 {
199 char c = *s;
200 if (c == 0)
201 return NULL;
202 if (c == '\"')
203 break;
204 s++;
205 }
206 prop.Value.SetFrom_Chars_SizeT(beg, (size_t)(s - beg));
207 s++;
208 }
209 }
210
SkipHeader(const char * s,const char * startString,const char * endString)211 static const char * SkipHeader(const char *s, const char *startString, const char *endString)
212 {
213 SKIP_SPACES(s)
214 if (IsString1PrefixedByString2(s, startString))
215 {
216 s = strstr(s, endString);
217 if (!s)
218 return NULL;
219 s += strlen(endString);
220 }
221 return s;
222 }
223
AppendTo(AString & s) const224 void CXmlItem::AppendTo(AString &s) const
225 {
226 if (IsTag)
227 s += '<';
228 s += Name;
229 if (IsTag)
230 {
231 FOR_VECTOR (i, Props)
232 {
233 const CXmlProp &prop = Props[i];
234 s.Add_Space();
235 s += prop.Name;
236 s += '=';
237 s += '\"';
238 s += prop.Value;
239 s += '\"';
240 }
241 s += '>';
242 }
243 FOR_VECTOR (i, SubItems)
244 {
245 const CXmlItem &item = SubItems[i];
246 if (i != 0 && !SubItems[i - 1].IsTag)
247 s.Add_Space();
248 item.AppendTo(s);
249 }
250 if (IsTag)
251 {
252 s += '<';
253 s += '/';
254 s += Name;
255 s += '>';
256 }
257 }
258
Parse(const char * s)259 bool CXml::Parse(const char *s)
260 {
261 s = SkipHeader(s, "<?xml", "?>"); if (!s) return false;
262 s = SkipHeader(s, "<!DOCTYPE", ">"); if (!s) return false;
263
264 s = Root.ParseItem(s, 1000);
265 if (!s || !Root.IsTag)
266 return false;
267 SKIP_SPACES(s)
268 return *s == 0;
269 }
270
271 /*
272 void CXml::AppendTo(AString &s) const
273 {
274 Root.AppendTo(s);
275 }
276 */
277
278
z7_xml_DecodeString(AString & temp)279 void z7_xml_DecodeString(AString &temp)
280 {
281 char * const beg = temp.GetBuf();
282 char *dest = beg;
283 const char *p = beg;
284 for (;;)
285 {
286 char c = *p++;
287 if (c == 0)
288 break;
289 if (c == '&')
290 {
291 if (p[0] == '#')
292 {
293 const char *end;
294 const UInt32 number = ConvertStringToUInt32(p + 1, &end);
295 if (*end == ';' && number != 0 && number <= 127)
296 {
297 p = end + 1;
298 c = (char)number;
299 }
300 }
301 else if (
302 p[0] == 'a' &&
303 p[1] == 'm' &&
304 p[2] == 'p' &&
305 p[3] == ';')
306 {
307 p += 4;
308 }
309 else if (
310 p[0] == 'l' &&
311 p[1] == 't' &&
312 p[2] == ';')
313 {
314 p += 3;
315 c = '<';
316 }
317 else if (
318 p[0] == 'g' &&
319 p[1] == 't' &&
320 p[2] == ';')
321 {
322 p += 3;
323 c = '>';
324 }
325 else if (
326 p[0] == 'a' &&
327 p[1] == 'p' &&
328 p[2] == 'o' &&
329 p[3] == 's' &&
330 p[4] == ';')
331 {
332 p += 5;
333 c = '\'';
334 }
335 else if (
336 p[0] == 'q' &&
337 p[1] == 'u' &&
338 p[2] == 'o' &&
339 p[3] == 't' &&
340 p[4] == ';')
341 {
342 p += 5;
343 c = '\"';
344 }
345 }
346 *dest++ = c;
347 }
348 temp.ReleaseBuf_SetEnd((unsigned)(dest - beg));
349 }
350