1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: xml.cpp 4352 2010-12-20 03:14:10Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2010 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "core.h"
29 
30 // MACROS ------------------------------------------------------------------
31 
32 // TYPES -------------------------------------------------------------------
33 
34 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
35 
36 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
37 
38 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
39 
40 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
41 
42 // PUBLIC DATA DEFINITIONS -------------------------------------------------
43 
44 // PRIVATE DATA DEFINITIONS ------------------------------------------------
45 
46 // CODE --------------------------------------------------------------------
47 
48 //==========================================================================
49 //
50 //	VXmlNode::VXmlNode
51 //
52 //==========================================================================
53 
VXmlNode()54 VXmlNode::VXmlNode()
55 : Parent(NULL)
56 , FirstChild(NULL)
57 , LastChild(NULL)
58 , PrevSibling(NULL)
59 , NextSibling(NULL)
60 {
61 }
62 
63 //==========================================================================
64 //
65 //	VXmlNode::~VXmlNode
66 //
67 //==========================================================================
68 
~VXmlNode()69 VXmlNode::~VXmlNode()
70 {
71 	guard(VXmlNode::~VXmlNode);
72 	while (FirstChild)
73 	{
74 		VXmlNode* Temp = FirstChild;
75 		FirstChild = FirstChild->NextSibling;
76 		delete Temp;
77 		Temp = NULL;
78 	}
79 	unguard;
80 }
81 
82 //==========================================================================
83 //
84 //	VXmlNode::FindChild
85 //
86 //==========================================================================
87 
FindChild(const char * AName) const88 VXmlNode* VXmlNode::FindChild(const char* AName) const
89 {
90 	guard(VXmlNode::FindChild);
91 	for (VXmlNode* N = FirstChild; N; N = N->NextSibling)
92 		if (N->Name == AName)
93 			return N;
94 	return NULL;
95 	unguard;
96 }
97 
98 //==========================================================================
99 //
100 //	VXmlNode::FindChild
101 //
102 //==========================================================================
103 
FindChild(const VStr & AName) const104 VXmlNode* VXmlNode::FindChild(const VStr& AName) const
105 {
106 	guard(VXmlNode::FindChild);
107 	for (VXmlNode* N = FirstChild; N; N = N->NextSibling)
108 		if (N->Name == AName)
109 			return N;
110 	return NULL;
111 	unguard;
112 }
113 
114 //==========================================================================
115 //
116 //	VXmlNode::GetChild
117 //
118 //==========================================================================
119 
GetChild(const char * AName) const120 VXmlNode* VXmlNode::GetChild(const char* AName) const
121 {
122 	guard(VXmlNode::GetChild);
123 	VXmlNode* N = FindChild(AName);
124 	if (!N)
125 		Sys_Error("XML node %s not found", AName);
126 	return N;
127 	unguard;
128 }
129 
130 //==========================================================================
131 //
132 //	VXmlNode::GetChild
133 //
134 //==========================================================================
135 
GetChild(const VStr & AName) const136 VXmlNode* VXmlNode::GetChild(const VStr& AName) const
137 {
138 	guard(VXmlNode::GetChild);
139 	VXmlNode* N = FindChild(AName);
140 	if (!N)
141 		Sys_Error("XML node %s not found", *AName);
142 	return N;
143 	unguard;
144 }
145 
146 //==========================================================================
147 //
148 //	VXmlNode::FindNext
149 //
150 //==========================================================================
151 
FindNext(const char * AName) const152 VXmlNode* VXmlNode::FindNext(const char* AName) const
153 {
154 	guard(VXmlNode::FindNext);
155 	for (VXmlNode* N = NextSibling; N; N = N->NextSibling)
156 		if (N->Name == AName)
157 			return N;
158 	return NULL;
159 	unguard;
160 }
161 
162 //==========================================================================
163 //
164 //	VXmlNode::FindNext
165 //
166 //==========================================================================
167 
FindNext(const VStr & AName) const168 VXmlNode* VXmlNode::FindNext(const VStr& AName) const
169 {
170 	guard(VXmlNode::FindNext);
171 	for (VXmlNode* N = NextSibling; N; N = N->NextSibling)
172 		if (N->Name == AName)
173 			return N;
174 	return NULL;
175 	unguard;
176 }
177 
178 //==========================================================================
179 //
180 //	VXmlNode::FindNext
181 //
182 //==========================================================================
183 
FindNext() const184 VXmlNode* VXmlNode::FindNext() const
185 {
186 	guard(VXmlNode::FindNext);
187 	for (VXmlNode* N = NextSibling; N; N = N->NextSibling)
188 		if (N->Name == Name)
189 			return N;
190 	return NULL;
191 	unguard;
192 }
193 
194 //==========================================================================
195 //
196 //	VXmlNode::HasAttribute
197 //
198 //==========================================================================
199 
HasAttribute(const char * AttrName) const200 bool VXmlNode::HasAttribute(const char* AttrName) const
201 {
202 	guard(VXmlNode::HasAttribute);
203 	for (int i = 0; i < Attributes.Num(); i++)
204 		if (Attributes[i].Name == AttrName)
205 			return true;
206 	return false;
207 	unguard;
208 }
209 
210 //==========================================================================
211 //
212 //	VXmlNode::FindChild
213 //
214 //==========================================================================
215 
HasAttribute(const VStr & AttrName) const216 bool VXmlNode::HasAttribute(const VStr& AttrName) const
217 {
218 	guard(VXmlNode::HasAttribute);
219 	for (int i = 0; i < Attributes.Num(); i++)
220 		if (Attributes[i].Name == AttrName)
221 			return true;
222 	return false;
223 	unguard;
224 }
225 
226 //==========================================================================
227 //
228 //	VXmlNode::GetAttribute
229 //
230 //==========================================================================
231 
GetAttribute(const char * AttrName,bool Required) const232 VStr VXmlNode::GetAttribute(const char* AttrName, bool Required) const
233 {
234 	guard(VXmlNode::GetAttribute);
235 	for (int i = 0; i < Attributes.Num(); i++)
236 		if (Attributes[i].Name == AttrName)
237 			return Attributes[i].Value;
238 	if (Required)
239 		Sys_Error("XML attribute %s not found", AttrName);
240 	return VStr();
241 	unguard;
242 }
243 
244 //==========================================================================
245 //
246 //	VXmlNode::GetAttribute
247 //
248 //==========================================================================
249 
GetAttribute(const VStr & AttrName,bool Required) const250 VStr VXmlNode::GetAttribute(const VStr& AttrName, bool Required) const
251 {
252 	guard(VXmlNode::GetAttribute);
253 	for (int i = 0; i < Attributes.Num(); i++)
254 		if (Attributes[i].Name == AttrName)
255 			return Attributes[i].Value;
256 	if (Required)
257 		Sys_Error("XML attribute %s not found", *AttrName);
258 	return VStr();
259 	unguard;
260 }
261 
262 //==========================================================================
263 //
264 //	VXmlDocument::Parse
265 //
266 //==========================================================================
267 
Parse(VStream & Strm,VStr AName)268 void VXmlDocument::Parse(VStream& Strm, VStr AName)
269 {
270 	guard(VXmlDocument::Parse);
271 	Name = AName;
272 
273 	Buf = new char[Strm.TotalSize() + 1];
274 	Strm.Seek(0);
275 	Strm.Serialise(Buf, Strm.TotalSize());
276 	Buf[Strm.TotalSize()] = 0;
277 	CurPos = 0;
278 	EndPos = Strm.TotalSize();
279 
280 	//	Skip garbage some editors add in the begining of UTF-8 files.
281 	if ((vuint8)Buf[0] == 0xef && (vuint8)Buf[1] == 0xbb && (vuint8)Buf[2] == 0xbf)
282 	{
283 		CurPos += 3;
284 	}
285 
286 	do
287 	{
288 		SkipWhitespace();
289 	} while (SkipComment());
290 
291 	if (CurPos >= EndPos)
292 	{
293 		Error("Empty document");
294 	}
295 
296 	if (!(Buf[CurPos] == '<' && Buf[CurPos + 1] == '?' && Buf[CurPos + 2] == 'x' &&
297 		Buf[CurPos + 3] == 'm' && Buf[CurPos + 4] == 'l' && Buf[CurPos + 5] > 0 && Buf[CurPos + 5] <= ' '))
298 	{
299 		Error("XML declaration expected");
300 	}
301 	CurPos += 5;
302 	SkipWhitespace();
303 	VStr AttrName;
304 	VStr AttrValue;
305 	if (!ParseAttribute(AttrName, AttrValue))
306 	{
307 		Error("XML version expected");
308 	}
309 	if (AttrName != "version")
310 	{
311 		Error("XML version expected");
312 	}
313 	if (AttrValue != "1.0" && AttrValue != "1.1")
314 	{
315 		Error("Bad XML version");
316 	}
317 	SkipWhitespace();
318 	while (ParseAttribute(AttrName, AttrValue))
319 	{
320 		if (AttrName == "encoding")
321 		{
322 			if (AttrValue.ToUpper() != "UTF-8")
323 			{
324 				Error("Only UTF-8 is supported");
325 			}
326 		}
327 		else if (AttrName == "standalone")
328 		{
329 			if (AttrValue.ToLower() != "yes")
330 			{
331 				Error("Only standalone is supported");
332 			}
333 		}
334 		else
335 		{
336 			Error("Unknown attribute");
337 		}
338 		SkipWhitespace();
339 	}
340 	if (Buf[CurPos] != '?' || Buf[CurPos + 1] != '>')
341 	{
342 		Error("Bad syntax");
343 	}
344 	CurPos += 2;
345 
346 	do
347 	{
348 		SkipWhitespace();
349 	} while (SkipComment());
350 
351 	if (Buf[CurPos] != '<')
352 	{
353 		Error("Root node expected");
354 	}
355 	ParseNode(&Root);
356 
357 	do
358 	{
359 		SkipWhitespace();
360 	} while (SkipComment());
361 
362 	if (CurPos != EndPos)
363 	{
364 		Error("Text after root node");
365 	}
366 
367 	delete[] Buf;
368 	Buf = NULL;
369 	unguard;
370 }
371 
372 //==========================================================================
373 //
374 //	VXmlDocument::SkipWhitespace
375 //
376 //==========================================================================
377 
SkipWhitespace()378 void VXmlDocument::SkipWhitespace()
379 {
380 	guard(VXmlDocument::SkipWhitespace);
381 	while (Buf[CurPos] > 0 && Buf[CurPos] <= ' ')
382 	{
383 		CurPos++;
384 	}
385 	unguard;
386 }
387 
388 //==========================================================================
389 //
390 //	VXmlDocument::SkipComment
391 //
392 //==========================================================================
393 
SkipComment()394 bool VXmlDocument::SkipComment()
395 {
396 	guard(VXmlDocument::SkipComment);
397 	if (Buf[CurPos] == '<' && Buf[CurPos + 1] == '!' &&
398 		Buf[CurPos + 2] == '-' && Buf[CurPos + 3] == '-')
399 	{
400 		//	Skip comment.
401 		CurPos += 4;
402 		while (CurPos < EndPos - 2 && (Buf[CurPos] != '-' ||
403 			Buf[CurPos + 1] != '-' || Buf[CurPos + 2] != '>'))
404 		{
405 			CurPos++;
406 		}
407 		if (CurPos >= EndPos - 2)
408 		{
409 			Error("Unterminated comment");
410 		}
411 		CurPos += 3;
412 		return true;
413 	}
414 	return false;
415 	unguard;
416 }
417 
418 //==========================================================================
419 //
420 //	VXmlDocument::Error
421 //
422 //==========================================================================
423 
Error(const char * Msg)424 void VXmlDocument::Error(const char* Msg)
425 {
426 	guard(VXmlDocument::Error);
427 	Sys_Error("%s: %s", *Name, Msg);
428 	unguard;
429 }
430 
431 //==========================================================================
432 //
433 //	VXmlDocument::ParseName
434 //
435 //==========================================================================
436 
ParseName()437 VStr VXmlDocument::ParseName()
438 {
439 	guard(VXmlDocument::ParseName);
440 	VStr Ret;
441 	char c = Buf[CurPos];
442 	if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-' || c == ':'))
443 	{
444 		return VStr();
445 	}
446 
447 	do
448 	{
449 		Ret += c;
450 		CurPos++;
451 		c = Buf[CurPos];
452 	}
453 	while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == ':' || c == '.');
454 
455 	return Ret;
456 	unguard;
457 }
458 
459 //==========================================================================
460 //
461 //	VXmlDocument::ParseAttrValue
462 //
463 //==========================================================================
464 
ParseAttrValue(char EndChar)465 VStr VXmlDocument::ParseAttrValue(char EndChar)
466 {
467 	guard(VXmlDocument::ParseAttrValue);
468 	VStr Ret;
469 	while (CurPos < EndPos && Buf[CurPos] != EndChar)
470 	{
471 		if (Buf[CurPos] == '\r' && Buf[CurPos + 1] == '\n')
472 			CurPos++;
473 		Ret += Buf[CurPos];
474 		CurPos++;
475 	}
476 	if (CurPos >= EndPos)
477 	{
478 		Error("Unterminated attribute value");
479 	}
480 	CurPos++;
481 	return HandleReferences(Ret);
482 	unguard;
483 }
484 
485 //==========================================================================
486 //
487 //	VXmlDocument::ParseAttribute
488 //
489 //==========================================================================
490 
ParseAttribute(VStr & AttrName,VStr & AttrValue)491 bool VXmlDocument::ParseAttribute(VStr& AttrName, VStr& AttrValue)
492 {
493 	guard(VXmlDocument::ParseAttribute);
494 	AttrName = ParseName();
495 	if (AttrName.IsEmpty())
496 	{
497 		return false;
498 	}
499 	SkipWhitespace();
500 	if (Buf[CurPos] != '=')
501 	{
502 		Error("Attribute value expected");
503 	}
504 	CurPos++;
505 	SkipWhitespace();
506 	if (CurPos >= EndPos)
507 	{
508 		Error("unexpected end of document");
509 	}
510 	if (Buf[CurPos] == '\"' || Buf[CurPos] == '\'')
511 	{
512 		CurPos++;
513 		AttrValue = ParseAttrValue(Buf[CurPos - 1]);
514 	}
515 	else
516 	{
517 		Error("Unquoted attribute value");
518 	}
519 	return true;
520 	unguard;
521 }
522 
523 //==========================================================================
524 //
525 //	VXmlDocument::ParseNode
526 //
527 //==========================================================================
528 
ParseNode(VXmlNode * Node)529 void VXmlDocument::ParseNode(VXmlNode* Node)
530 {
531 	guard(VXmlDocument::ParseNode);
532 	if (Buf[CurPos] != '<')
533 	{
534 		Error("Bad tag start");
535 	}
536 	CurPos++;
537 	Node->Name = ParseName();
538 	if (Node->Name.IsEmpty())
539 	{
540 		Error("Bad or missing tag name");
541 	}
542 
543 	SkipWhitespace();
544 	VStr AttrName;
545 	VStr AttrValue;
546 	while (ParseAttribute(AttrName, AttrValue))
547 	{
548 		VXmlAttribute& A = Node->Attributes.Alloc();
549 		A.Name = AttrName;
550 		A.Value = AttrValue;
551 		SkipWhitespace();
552 	}
553 
554 	if (Buf[CurPos] == '/' && Buf[CurPos + 1] == '>')
555 	{
556 		CurPos += 2;
557 	}
558 	else if (Buf[CurPos] == '>')
559 	{
560 		CurPos++;
561 		while (CurPos < EndPos && (Buf[CurPos] != '<' || Buf[CurPos + 1] != '/'))
562 		{
563 			if (Buf[CurPos] == '<')
564 			{
565 				if (Buf[CurPos + 1] == '!' && Buf[CurPos + 2] == '-' && Buf[CurPos + 3] == '-')
566 				{
567 					SkipComment();
568 				}
569 				else if (Buf[CurPos + 1] == '!' && Buf[CurPos + 2] == '[' &&
570 					Buf[CurPos + 3] == 'C' && Buf[CurPos + 4] == 'D' &&
571 					Buf[CurPos + 5] == 'A' && Buf[CurPos + 6] == 'T' &&
572 					Buf[CurPos + 7] == 'A' && Buf[CurPos + 8] == '[')
573 				{
574 					CurPos += 9;
575 					VStr TextVal;
576 					while (CurPos < EndPos && (Buf[CurPos] != ']' ||
577 						Buf[CurPos + 1] != ']' || Buf[CurPos + 2] != '>'))
578 					{
579 						if (Buf[CurPos] == '\r' && Buf[CurPos + 1] == '\n')
580 							CurPos++;
581 						TextVal += Buf[CurPos];
582 						CurPos++;
583 					}
584 					if (CurPos >= EndPos)
585 					{
586 						Error("Unexpected end of file in CDATA");
587 					}
588 					Node->Value += TextVal;
589 					CurPos += 3;
590 				}
591 				else
592 				{
593 					VXmlNode* NewChild = new VXmlNode();
594 					NewChild->PrevSibling = Node->LastChild;
595 					if (Node->LastChild)
596 						Node->LastChild->NextSibling = NewChild;
597 					else
598 						Node->FirstChild = NewChild;
599 					Node->LastChild = NewChild;
600 					NewChild->Parent = Node;
601 					ParseNode(NewChild);
602 				}
603 			}
604 			else
605 			{
606 				VStr TextVal;
607 				bool HasNonWhitespace = false;
608 				while (CurPos < EndPos && Buf[CurPos] != '<')
609 				{
610 					if (Buf[CurPos] == '\r' && Buf[CurPos + 1] == '\n')
611 						CurPos++;
612 					TextVal += Buf[CurPos];
613 					if (Buf[CurPos] < 0 || Buf[CurPos] > ' ')
614 						HasNonWhitespace = true;
615 					CurPos++;
616 				}
617 				if (HasNonWhitespace)
618 				{
619 					Node->Value += HandleReferences(TextVal);
620 				}
621 			}
622 		}
623 		if (CurPos >= EndPos)
624 		{
625 			Error("Unexpected end of file");
626 		}
627 		CurPos += 2;
628 		VStr Test = ParseName();
629 		if (Node->Name != Test)
630 		{
631 			Error("Wrong end tag");
632 		}
633 		if (Buf[CurPos] != '>')
634 		{
635 			Error("Tag not closed");
636 		}
637 		CurPos++;
638 	}
639 	else
640 	{
641 		Error("Tag is not closed");
642 	}
643 	unguard;
644 }
645 
646 //==========================================================================
647 //
648 //	VXmlDocument::HandleReferences
649 //
650 //==========================================================================
651 
HandleReferences(const VStr & AStr)652 VStr VXmlDocument::HandleReferences(const VStr& AStr)
653 {
654 	guard(VXmlDocument::HandleReferences);
655 	VStr Ret = AStr;
656 	for (int i = 0; i < int(Ret.Length()); i++)
657 	{
658 		if (Ret[i] == '&')
659 		{
660 			int EndPos = i + 1;
661 			while (EndPos < Ret.Length() && Ret[EndPos] != ';')
662 			{
663 				EndPos++;
664 			}
665 			if (EndPos >= Ret.Length())
666 			{
667 				Error("Unterminated character or entity reference");
668 			}
669 			EndPos++;
670 			VStr Seq = VStr(Ret, i, int(EndPos) - i);
671 			VStr NewVal;
672 			if (Seq[1] == '#' && Seq[2] == 'x')
673 			{
674 				int Val = 0;
675 				for (int j = 3; j < int(Seq.Length()) - 1; j++)
676 				{
677 					if (Seq[j] >= '0' && Seq[j] < '9')
678 						Val = (Val << 4) + Seq[j] - '0';
679 					else if (Seq[j] >= 'a' && Seq[j] < 'f')
680 						Val = (Val << 4) + Seq[j] - 'a' + 10;
681 					else if (Seq[j] >= 'A' && Seq[j] < 'F')
682 						Val = (Val << 4) + Seq[j] - 'A' + 10;
683 					else
684 						Error("Bad character reference");
685 				}
686 				NewVal = VStr::FromChar(Val);
687 			}
688 			else if (Seq[1] == '#')
689 			{
690 				int Val = 0;
691 				for (int j = 2; j < int(Seq.Length()) - 1; j++)
692 				{
693 					if (Seq[j] >= '0' && Seq[j] < '9')
694 						Val = Val * 10 + Seq[j] - '0';
695 					else
696 						Error("Bad character reference");
697 				}
698 				NewVal = VStr::FromChar(Val);
699 			}
700 			else if (Seq == "&amp;")
701 				NewVal = "&";
702 			else if (Seq == "&quot;")
703 				NewVal = "\"";
704 			else if (Seq == "&apos;")
705 				NewVal = "\'";
706 			else if (Seq == "&lt;")
707 				NewVal = "<";
708 			else if (Seq == "&gt;")
709 				NewVal = ">";
710 			else
711 				Error("Unknown entity reference");
712 			Ret = VStr(Ret, 0, i) + NewVal + VStr(Ret, int(EndPos), int(Ret.Length() - EndPos));
713 			i += int(NewVal.Length() - 1);
714 		}
715 	}
716 	return Ret;
717 	unguard;
718 }
719