AERA
xml_parser.cpp
1 
73 #ifndef _CRT_SECURE_NO_DEPRECATE
74 #define _CRT_SECURE_NO_DEPRECATE
75 #endif
76 #include "types.h"
77 #include "xml_parser.h"
78 #ifdef _XMLWINDOWS
79 //#ifdef _DEBUG
80 //#define _CRTDBG_MAP_ALLOC
81 //#include <crtdbg.h>
82 //#endif
83 #define WIN32_LEAN_AND_MEAN
84 #include <Windows.h> // to have IsTextUnicode, MultiByteToWideChar, WideCharToMultiByte to handle unicode files
85  // to have "MessageBoxA" to display error messages for openFilHelper
86 #endif
87 
88 #include <memory.h>
89 #include <assert.h>
90 #include <stdio.h>
91 #include <string.h>
92 #include <stdlib.h>
93 #include "types.h"
94 
95 namespace core{
96 
97 XMLCSTR XMLNode::getVersion() { return _X("v2.30"); }
98 void freeXMLString(XMLSTR t){free(t);}
99 
100 static XMLNode::XMLCharEncoding characterEncoding=XMLNode::encoding_UTF8;
101 static char guessWideCharChars=1, dropWhiteSpace=1;
102 
103 inline int mmin( const int t1, const int t2 ) { return t1 < t2 ? t1 : t2; }
104 
105 // You can modify the initialization of the variable "XMLClearTags" below
106 // to change the clearTags that are currently recognized by the library.
107 // The number on the second columns is the length of the string inside the
108 // first column. The "<!DOCTYPE" declaration must be the second in the list.
109 typedef struct { XMLCSTR lpszOpen; int openTagLen; XMLCSTR lpszClose;} ALLXMLClearTag;
110 static ALLXMLClearTag XMLClearTags[] =
111 {
112  { _X("<![CDATA["),9, _X("]]>") },
113  { _X("<!DOCTYPE"),9, _X(">") },
114  { _X("<PRE>") ,5, _X("</PRE>") },
115  { _X("<Script>") ,8, _X("</Script>")},
116  { _X("<!--") ,4, _X("-->") },
117  { NULL ,0, NULL }
118 };
119 
120 // You can modify the initialization of the variable "XMLEntities" below
121 // to change the character entities that are currently recognized by the library.
122 // The number on the second columns is the length of the string inside the
123 // first column. Additionally, the syntaxes "&#xA0;" and "&#160;" are recognized.
124 typedef struct { XMLCSTR s; int l; XMLCHAR c;} XMLCharacterEntity;
125 static XMLCharacterEntity XMLEntities[] =
126 {
127  { _X("&amp;" ), 5, _X('&' )},
128  { _X("&lt;" ), 4, _X('<' )},
129  { _X("&gt;" ), 4, _X('>' )},
130  { _X("&quot;"), 6, _X('\"')},
131  { _X("&apos;"), 6, _X('\'')},
132  { NULL , 0, '\0' }
133 };
134 
135 // When rendering the XMLNode to a string (using the "createXMLString" function),
136 // you can ask for a beautiful formatting. This formatting is using the
137 // following indentation character:
138 #define INDENTCHAR _X('\t')
139 
140 // The following function parses the XML errors into a user friendly string.
141 // You can edit this to change the output language of the library to something else.
142 XMLCSTR XMLNode::getError(XMLError xerror)
143 {
144  switch (xerror)
145  {
146  case eXMLErrorNone: return _X("No error");
147  case eXMLErrorMissingEndTag: return _X("Warning: Unmatched end tag");
148  case eXMLErrorNoXMLTagFound: return _X("Warning: No XML tag found");
149  case eXMLErrorEmpty: return _X("> Error: No XML data");
150  case eXMLErrorMissingTagName: return _X("> Error: Missing start tag name");
151  case eXMLErrorMissingEndTagName: return _X("> Error: Missing end tag name");
152  case eXMLErrorUnmatchedEndTag: return _X("> Error: Unmatched end tag");
153  case eXMLErrorUnmatchedEndClearTag: return _X("> Error: Unmatched clear tag end");
154  case eXMLErrorUnexpectedToken: return _X("> Error: Unexpected token found");
155  case eXMLErrorNoElements: return _X("> Error: No elements found");
156  case eXMLErrorFileNotFound: return _X("> Error: File not found");
157  case eXMLErrorFirstTagNotFound: return _X("> Error: First Tag not found");
158  case eXMLErrorUnknownCharacterEntity:return _X("> Error: Unknown character entity");
159  case eXMLErrorCharConversionError: return _X("> Error: unable to convert between WideChar and MultiByte chars");
160  case eXMLErrorCannotOpenWriteFile: return _X("> Error: unable to open file for writing");
161  case eXMLErrorCannotWriteFile: return _X("> Error: cannot write into file");
162 
163  case eXMLErrorBase64DataSizeIsNotMultipleOf4: return _X("Warning: Base64-string length is not a multiple of 4");
164  case eXMLErrorBase64DecodeTruncatedData: return _X("Warning: Base64-string is truncated");
165  case eXMLErrorBase64DecodeIllegalCharacter: return _X("> Error: Base64-string contains an illegal character");
166  case eXMLErrorBase64DecodeBufferTooSmall: return _X("> Error: Base64 decode output buffer is too small");
167  };
168  return _X("Unknown");
169 }
170 
172 // Here start the abstraction layer to be OS-independent //
174 
175 // Here is an abstraction layer to access some common string manipulation functions.
176 // The abstraction layer is currently working for gcc, Microsoft Visual Studio 6.0,
177 // Microsoft Visual Studio .NET, CC (sun compiler) and Borland C++.
178 // If you plan to "port" the library to a new system/compiler, all you have to do is
179 // to edit the following lines.
180 #ifdef XML_NO_WIDE_CHAR
181 char myIsTextWideChar(const void *b, int len) { return FALSE; }
182 #else
183  #if defined (UNDER_CE) || !defined(_XMLWINDOWS)
184  char myIsTextWideChar(const void *b, int len) // inspired by the Wine API: RtlIsTextUnicode
185  {
186 #ifdef sun
187  // for SPARC processors: wchar_t* buffers must always be alligned, otherwise it's a char* buffer.
188  if ((((unsigned long)b)%sizeof(wchar_t))!=0) return FALSE;
189 #endif
190  const wchar_t *s=(const wchar_t*)b;
191 
192  // buffer too small:
193  if (len<(int)sizeof(wchar_t)) return FALSE;
194 
195  // odd length test
196  if (len&1) return FALSE;
197 
198  /* only checks the first 256 characters */
199  len=mmin(256,len/sizeof(wchar_t));
200 
201  // Check for the special byte order:
202  if (*((unsigned short*)s) == 0xFFFE) return TRUE; // IS_TEXT_UNICODE_REVERSE_SIGNATURE;
203  if (*((unsigned short*)s) == 0xFEFF) return TRUE; // IS_TEXT_UNICODE_SIGNATURE
204 
205  // checks for ASCII characters in the UNICODE stream
206  int i,stats=0;
207  for (i=0; i<len; i++) if (s[i]<=(unsigned short)255) stats++;
208  if (stats>len/2) return TRUE;
209 
210  // Check for UNICODE NULL chars
211  for (i=0; i<len; i++) if (!s[i]) return TRUE;
212 
213  return FALSE;
214  }
215  #else
216  char myIsTextWideChar(const void *b,int l) { return (char)IsTextUnicode((CONST LPVOID)b,l,NULL); };
217  #endif
218 #endif
219 
220 #ifdef _XMLWINDOWS
221 // for Microsoft Visual Studio 6.0 and Microsoft Visual Studio .NET,
222  #ifdef _XMLWIDECHAR
223  wchar_t *myMultiByteToWideChar(const char *s)
224  {
225  int i;
226  if (characterEncoding==XMLNode::encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,NULL,0);
227  else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,NULL,0);
228  if (i<0) return NULL;
229  wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(XMLCHAR));
230  if (characterEncoding==XMLNode::encoding_UTF8) i=(int)MultiByteToWideChar(CP_UTF8,0 ,s,-1,d,i);
231  else i=(int)MultiByteToWideChar(CP_ACP ,MB_PRECOMPOSED,s,-1,d,i);
232  d[i]=0;
233  return d;
234  }
235  static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return _wfopen(filename,mode); }
236  static inline int xstrlen(XMLCSTR c) { return (int)wcslen(c); }
237  static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _wcsnicmp(c1,c2,l);}
238  static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
239  static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _wcsicmp(c1,c2); }
240  static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
241  static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
242  #else
243  char *myWideCharToMultiByte(const wchar_t *s)
244  {
245  UINT codePage=CP_ACP; if (characterEncoding==XMLNode::encoding_UTF8) codePage=CP_UTF8;
246  int i=(int)WideCharToMultiByte(codePage, // code page
247  0, // performance and mapping flags
248  s, // wide-character string
249  -1, // number of chars in string
250  NULL, // buffer for new string
251  0, // size of buffer
252  NULL, // default for unmappable chars
253  NULL // set when default char used
254  );
255  if (i<0) return NULL;
256  char *d=(char*)malloc(i+1);
257  WideCharToMultiByte(codePage, // code page
258  0, // performance and mapping flags
259  s, // wide-character string
260  -1, // number of chars in string
261  d, // buffer for new string
262  i, // size of buffer
263  NULL, // default for unmappable chars
264  NULL // set when default char used
265  );
266  d[i]=0;
267  return d;
268  }
269  static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
270  static inline int xstrlen(XMLCSTR c) { return (int)strlen(c); }
271  static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return _strnicmp(c1,c2,l);}
272  static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
273  static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return _stricmp(c1,c2); }
274  static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
275  static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
276  #endif
277  #ifdef __BORLANDC__
278  static inline int _strnicmp(char *c1, char *c2, int l){ return strnicmp(c1,c2,l);}
279  #endif
280 #else
281 // for gcc and CC
282  #ifdef XML_NO_WIDE_CHAR
283  char *myWideCharToMultiByte(const wchar_t *s) { return NULL; }
284  #else
285  char *myWideCharToMultiByte(const wchar_t *s)
286  {
287  const wchar_t *ss=s;
288  int i=(int)wcsrtombs(NULL,&ss,0,NULL);
289  if (i<0) return NULL;
290  char *d=(char *)malloc(i+1);
291  wcsrtombs(d,&s,i,NULL);
292  d[i]=0;
293  return d;
294  }
295  #endif
296  #ifdef _XMLWIDECHAR
297  wchar_t *myMultiByteToWideChar(const char *s)
298  {
299  const char *ss=s;
300  int i=(int)mbsrtowcs(NULL,&ss,0,NULL);
301  if (i<0) return NULL;
302  wchar_t *d=(wchar_t *)malloc((i+1)*sizeof(wchar_t));
303  mbsrtowcs(d,&s,i,NULL);
304  d[i]=0;
305  return d;
306  }
307  int xstrlen(XMLCSTR c) { return wcslen(c); }
308  #ifdef sun
309  // for CC
310  #include <widec.h>
311  static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncasecmp(c1,c2,l);}
312  static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wsncmp(c1,c2,l);}
313  static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wscasecmp(c1,c2); }
314  #else
315  // for gcc
316  static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncasecmp(c1,c2,l);}
317  static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return wcsncmp(c1,c2,l);}
318  static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return wcscasecmp(c1,c2); }
319  #endif
320  static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)wcsstr(c1,c2); }
321  static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)wcscpy(c1,c2); }
322  static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode)
323  {
324  char *filenameAscii=myWideCharToMultiByte(filename);
325  FILE *f;
326  if (mode[0]==_X('r')) f=fopen(filenameAscii,"rb");
327  else f=fopen(filenameAscii,"wb");
328  free(filenameAscii);
329  return f;
330  }
331  #else
332  static inline FILE *xfopen(XMLCSTR filename,XMLCSTR mode) { return fopen(filename,mode); }
333  static inline int xstrlen(XMLCSTR c) { return strlen(c); }
334  static inline int xstrnicmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncasecmp(c1,c2,l);}
335  static inline int xstrncmp(XMLCSTR c1, XMLCSTR c2, int l) { return strncmp(c1,c2,l);}
336  static inline int xstricmp(XMLCSTR c1, XMLCSTR c2) { return strcasecmp(c1,c2); }
337  static inline XMLSTR xstrstr(XMLCSTR c1, XMLCSTR c2) { return (XMLSTR)strstr(c1,c2); }
338  static inline XMLSTR xstrcpy(XMLSTR c1, XMLCSTR c2) { return (XMLSTR)strcpy(c1,c2); }
339  #endif
340  static inline int _strnicmp(const char *c1,const char *c2, int l) { return strncasecmp(c1,c2,l);}
341 #endif
342 
344 // the "openFileHelper" function //
346 
347 // Since each application has its own way to report and deal with errors, you should modify & rewrite
348 // the following "openFileHelper" function to get an "error reporting mechanism" tailored to your needs.
349 XMLNode XMLNode::openFileHelper(XMLCSTR filename, XMLCSTR tag)
350 {
351  // guess the value of the global parameter "characterEncoding"
352  // (the guess is based on the first 200 bytes of the file).
353  FILE *f=xfopen(filename,_X("rb"));
354  if (f)
355  {
356  char bb[205];
357  int l=(int)fread(bb,1,200,f);
358  setGlobalOptions(guessCharEncoding(bb,l),guessWideCharChars,dropWhiteSpace);
359  fclose(f);
360  }
361 
362  // parse the file
363  XMLResults pResults;
364  XMLNode xnode=XMLNode::parseFile(filename,tag,&pResults);
365 
366  // display error message (if any)
367  if (pResults.error != eXMLErrorNone)
368  {
369  // create message
370  char message[2000],*s1=(char*)"",*s3=(char*)""; XMLCSTR s2=_X("");
371  if (pResults.error==eXMLErrorFirstTagNotFound) { s1=(char*)"First Tag should be '"; s2=tag; s3=(char*)"'.\n"; }
372  sprintf(message,
373 #ifdef _XMLWIDECHAR
374  "XML Parsing error inside file '%S'.\n%S\nAt line %i, column %i.\n%s%S%s"
375 #else
376  "XML Parsing error inside file '%s'.\n%s\nAt line %i, column %i.\n%s%s%s"
377 #endif
378  ,filename,XMLNode::getError(pResults.error),pResults.nLine,pResults.nColumn,s1,s2,s3);
379 
380  // display message
381 #if defined(_XMLWINDOWS) && !defined(UNDER_CE) && !defined(_XMLPARSER_NO_MESSAGEBOX_)
382  MessageBoxA(NULL,message,"XML Parsing error",MB_OK|MB_ICONERROR|MB_TOPMOST);
383 #else
384  printf("%s",message);
385 #endif
386  exit(255);
387  }
388  return xnode;
389 }
390 
392 // Here start the core implementation of the XMLParser library //
394 
395 // You should normally not change anything below this point.
396 
397 #ifndef _XMLWIDECHAR
398 // If "characterEncoding=ascii" then we assume that all characters have the same length of 1 byte.
399 // If "characterEncoding=UTF8" then the characters have different lengths (from 1 byte to 4 bytes).
400 // If "characterEncoding=ShiftJIS" then the characters have different lengths (from 1 byte to 2 bytes).
401 // This table is used as lookup-table to know the length of a character (in byte) based on the
402 // content of the first byte of the character.
403 // (note: if you modify this, you must always have XML_utf8ByteTable[0]=0 ).
404 static const char XML_utf8ByteTable[256] =
405 {
406  // 0 1 2 3 4 5 6 7 8 9 a b c d e f
407  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
408  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
409  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
410  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
411  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
412  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
413  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
414  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70End of ASCII range
415  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x80 0x80 to 0xc1 invalid
416  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x90
417  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
418  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
419  1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xc0 0xc2 to 0xdf 2 byte
420  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xd0
421  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,// 0xe0 0xe0 to 0xef 3 byte
422  4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
423 };
424 static const char XML_asciiByteTable[256] =
425 {
426  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
427  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
428  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
429  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
430  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
431  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
432 };
433 static const char XML_sjisByteTable[256] =
434 {
435  // 0 1 2 3 4 5 6 7 8 9 a b c d e f
436  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x00
437  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x10
438  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x20
439  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x30
440  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x40
441  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x50
442  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x60
443  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0x70 End of ASCII range
444  1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x80 0x81 to 0x9F 2 bytes
445  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0x90
446  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xa0
447  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xb0
448  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xc0
449  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,// 0xd0
450  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,// 0xe0 0xe0 to 0xef 2 bytes
451  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 // 0xf0
452 };
453 static const char *XML_ByteTable=(const char *)XML_utf8ByteTable; // the default is "characterEncoding=XMLNode::encoding_UTF8"
454 #endif
455 
456 
457 XMLNode XMLNode::emptyXMLNode;
458 XMLClear XMLNode::emptyXMLClear={ NULL, NULL, NULL};
459 XMLAttribute XMLNode::emptyXMLAttribute={ NULL, NULL};
460 
461 // Enumeration used to decipher what type a token is
462 typedef enum XMLTokenTypeTag
463 {
464  eTokenText = 0,
465  eTokenQuotedText,
466  eTokenTagStart, /* "<" */
467  eTokenTagEnd, /* "</" */
468  eTokenCloseTag, /* ">" */
469  eTokenEquals, /* "=" */
470  eTokenDeclaration, /* "<?" */
471  eTokenShortHandClose, /* "/>" */
472  eTokenClear,
473  eTokenError
474 } XMLTokenType;
475 
476 // Main structure used for parsing XML
477 typedef struct XML
478 {
479  XMLCSTR lpXML;
480  XMLCSTR lpszText;
481  int nIndex,nIndexMissigEndTag;
482  enum XMLError error;
483  XMLCSTR lpEndTag;
484  int cbEndTag;
485  XMLCSTR lpNewElement;
486  int cbNewElement;
487  int nFirst;
488 } XML;
489 
490 typedef struct
491 {
492  ALLXMLClearTag *pClr;
493  XMLCSTR pStr;
494 } NextToken;
495 
496 // Enumeration used when parsing attributes
497 typedef enum Attrib
498 {
499  eAttribName = 0,
500  eAttribEquals,
501  eAttribValue
502 } Attrib;
503 
504 // Enumeration used when parsing elements to dictate whether we are currently
505 // inside a tag
506 typedef enum Status
507 {
508  eInsideTag = 0,
509  eOutsideTag
510 } Status;
511 
512 XMLError XMLNode::writeToFile(XMLCSTR filename, const char *encoding, char nFormat) const
513 {
514  if (!d) return eXMLErrorNone;
515  FILE *f=xfopen(filename,_X("wb"));
516  if (!f) return eXMLErrorCannotOpenWriteFile;
517 #ifdef _XMLWIDECHAR
518  unsigned char h[2]={ 0xFF, 0xFE };
519  if (!fwrite(h,2,1,f)) return eXMLErrorCannotWriteFile;
520  if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
521  {
522  if (!fwrite(_X("<?xml version=\"1.0\" encoding=\"utf-16\"?>\n"),sizeof(wchar_t)*40,1,f))
523  return eXMLErrorCannotWriteFile;
524  }
525 #else
526  if ((!isDeclaration())&&((d->lpszName)||(!getChildNode().isDeclaration())))
527  {
528  if (characterEncoding==encoding_UTF8)
529  {
530  // header so that windows recognize the file as UTF-8:
531  unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
532  encoding="utf-8";
533  } else if (characterEncoding==encoding_ShiftJIS) encoding="SHIFT-JIS";
534 
535  if (!encoding) encoding="ISO-8859-1";
536  if (fprintf(f,"<?xml version=\"1.0\" encoding=\"%s\"?>\n",encoding)<0) return eXMLErrorCannotWriteFile;
537  } else
538  {
539  if (characterEncoding==encoding_UTF8)
540  {
541  unsigned char h[3]={0xEF,0xBB,0xBF}; if (!fwrite(h,3,1,f)) return eXMLErrorCannotWriteFile;
542  }
543  }
544 #endif
545  int i;
546  XMLSTR t=createXMLString(nFormat,&i);
547  if (!fwrite(t,sizeof(XMLCHAR)*i,1,f)) return eXMLErrorCannotWriteFile;
548  if (fclose(f)!=0) return eXMLErrorCannotWriteFile;
549  free(t);
550  return eXMLErrorNone;
551 }
552 
553 // Duplicate a given string.
554 XMLSTR stringDup(XMLCSTR lpszData, int cbData)
555 {
556  if (lpszData==NULL) return NULL;
557 
558  XMLSTR lpszNew;
559  if (cbData==0) cbData=(int)xstrlen(lpszData);
560  lpszNew = (XMLSTR)malloc((cbData+1) * sizeof(XMLCHAR));
561  if (lpszNew)
562  {
563  memcpy(lpszNew, lpszData, (cbData) * sizeof(XMLCHAR));
564  lpszNew[cbData] = (XMLCHAR)NULL;
565  }
566  return lpszNew;
567 }
568 
569 XMLSTR toXMLStringUnSafe(XMLSTR dest,XMLCSTR source)
570 {
571  XMLSTR dd=dest;
572  XMLCHAR ch;
573  XMLCharacterEntity *entity;
574  while ((ch=*source))
575  {
576  entity=XMLEntities;
577  do
578  {
579  if (ch==entity->c) {xstrcpy(dest,entity->s); dest+=entity->l; source++; goto out_of_loop1; }
580  entity++;
581  } while(entity->s);
582 #ifdef _XMLWIDECHAR
583  *(dest++)=*(source++);
584 #else
585  switch(XML_ByteTable[(unsigned char)ch])
586  {
587  case 4: *(dest++)=*(source++);
588  case 3: *(dest++)=*(source++);
589  case 2: *(dest++)=*(source++);
590  case 1: *(dest++)=*(source++);
591  }
592 #endif
593 out_of_loop1:
594  ;
595  }
596  *dest=0;
597  return dd;
598 }
599 
600 // private (used while rendering):
601 int lengthXMLString(XMLCSTR source)
602 {
603  int r=0;
604  XMLCharacterEntity *entity;
605  XMLCHAR ch;
606  while ((ch=*source))
607  {
608  entity=XMLEntities;
609  do
610  {
611  if (ch==entity->c) { r+=entity->l; source++; goto out_of_loop1; }
612  entity++;
613  } while(entity->s);
614 #ifdef _XMLWIDECHAR
615  r++; source++;
616 #else
617  ch=XML_ByteTable[(unsigned char)ch]; r+=ch; source+=ch;
618 #endif
619 out_of_loop1:
620  ;
621  }
622  return r;
623 }
624 
625 ToXMLStringTool::~ToXMLStringTool(){ freeBuffer(); }
626 void ToXMLStringTool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
627 XMLSTR ToXMLStringTool::toXML(XMLCSTR source)
628 {
629  int l=lengthXMLString(source)+1;
630  if (l>buflen) { buflen=l; buf=(XMLSTR)realloc(buf,l*sizeof(XMLCHAR)); }
631  return toXMLStringUnSafe(buf,source);
632 }
633 
634 // private:
635 XMLSTR fromXMLString(XMLCSTR s, int lo, XML *pXML)
636 {
637  // This function is the opposite of the function "toXMLString". It decodes the escape
638  // sequences &amp;, &quot;, &apos;, &lt;, &gt; and replace them by the characters
639  // &,",',<,>. This function is used internally by the XML Parser. All the calls to
640  // the XML library will always gives you back "decoded" strings.
641  //
642  // in: string (s) and length (lo) of string
643  // out: new allocated string converted from xml
644  if (!s) return NULL;
645 
646  int ll=0,j;
647  XMLSTR d;
648  XMLCSTR ss=s;
649  XMLCharacterEntity *entity;
650  while ((lo>0)&&(*s))
651  {
652  if (*s==_X('&'))
653  {
654  if ((lo>2)&&(s[1]==_X('#')))
655  {
656  s+=2; lo-=2;
657  if ((*s==_X('X'))||(*s==_X('x'))) { s++; lo--; }
658  while ((*s)&&(*s!=_X(';'))&&((lo--)>0)) s++;
659  if (*s!=_X(';'))
660  {
661  pXML->error=eXMLErrorUnknownCharacterEntity;
662  return NULL;
663  }
664  s++; lo--;
665  } else
666  {
667  entity=XMLEntities;
668  do
669  {
670  if ((lo>=entity->l)&&(xstrnicmp(s,entity->s,entity->l)==0)) { s+=entity->l; lo-=entity->l; break; }
671  entity++;
672  } while(entity->s);
673  if (!entity->s)
674  {
675  pXML->error=eXMLErrorUnknownCharacterEntity;
676  return NULL;
677  }
678  }
679  } else
680  {
681 #ifdef _XMLWIDECHAR
682  s++; lo--;
683 #else
684  j=XML_ByteTable[(unsigned char)*s]; s+=j; lo-=j; ll+=j-1;
685 #endif
686  }
687  ll++;
688  }
689 
690  d=(XMLSTR)malloc((ll+1)*sizeof(XMLCHAR));
691  s=d;
692  while (ll-->0)
693  {
694  if (*ss==_X('&'))
695  {
696  if (ss[1]==_X('#'))
697  {
698  ss+=2; j=0;
699  if ((*ss==_X('X'))||(*ss==_X('x')))
700  {
701  ss++;
702  while (*ss!=_X(';'))
703  {
704  if ((*ss>=_X('0'))&&(*ss<=_X('9'))) j=(j<<4)+*ss-_X('0');
705  else if ((*ss>=_X('A'))&&(*ss<=_X('F'))) j=(j<<4)+*ss-_X('A')+10;
706  else if ((*ss>=_X('a'))&&(*ss<=_X('f'))) j=(j<<4)+*ss-_X('a')+10;
707  else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
708  ss++;
709  }
710  } else
711  {
712  while (*ss!=_X(';'))
713  {
714  if ((*ss>=_X('0'))&&(*ss<=_X('9'))) j=(j*10)+*ss-_X('0');
715  else { free((void*)s); pXML->error=eXMLErrorUnknownCharacterEntity;return NULL;}
716  ss++;
717  }
718  }
719  (*d++)=(XMLCHAR)j; ss++;
720  } else
721  {
722  entity=XMLEntities;
723  do
724  {
725  if (xstrnicmp(ss,entity->s,entity->l)==0) { *(d++)=entity->c; ss+=entity->l; break; }
726  entity++;
727  } while(entity->s);
728  }
729  } else
730  {
731 #ifdef _XMLWIDECHAR
732  *(d++)=*(ss++);
733 #else
734  switch(XML_ByteTable[(unsigned char)*ss])
735  {
736  case 4: *(d++)=*(ss++); ll--;
737  case 3: *(d++)=*(ss++); ll--;
738  case 2: *(d++)=*(ss++); ll--;
739  case 1: *(d++)=*(ss++);
740  }
741 #endif
742  }
743  }
744  *d=0;
745  return (XMLSTR)s;
746 }
747 
748 #define XML_isSPACECHAR(ch) ((ch==_X('\n'))||(ch==_X(' '))||(ch== _X('\t'))||(ch==_X('\r')))
749 
750 // private:
751 char myTagCompare(XMLCSTR cclose, XMLCSTR copen)
752 // !!!! WARNING strange convention&:
753 // return 0 if equals
754 // return 1 if different
755 {
756  if (!cclose) return 1;
757  int l=(int)xstrlen(cclose);
758  if (xstrnicmp(cclose, copen, l)!=0) return 1;
759  const XMLCHAR c=copen[l];
760  if (XML_isSPACECHAR(c)||
761  (c==_X('/' ))||
762  (c==_X('<' ))||
763  (c==_X('>' ))||
764  (c==_X('=' ))) return 0;
765  return 1;
766 }
767 
768 // Obtain the next character from the string.
769 static inline XMLCHAR getNextChar(XML *pXML)
770 {
771  XMLCHAR ch = pXML->lpXML[pXML->nIndex];
772 #ifdef _XMLWIDECHAR
773  if (ch!=0) pXML->nIndex++;
774 #else
775  pXML->nIndex+=XML_ByteTable[(unsigned char)ch];
776 #endif
777  return ch;
778 }
779 
780 // Find the next token in a string.
781 // pcbToken contains the number of characters that have been read.
782 static NextToken GetNextToken(XML *pXML, int *pcbToken, enum XMLTokenTypeTag *pType)
783 {
784  NextToken result;
785  XMLCHAR ch;
786  XMLCHAR chTemp;
787  int indexStart,nFoundMatch,nIsText=FALSE;
788  result.pClr=NULL; // prevent warning
789 
790  // Find next non-white space character
791  do { indexStart=pXML->nIndex; ch=getNextChar(pXML); } while XML_isSPACECHAR(ch);
792 
793  if (ch)
794  {
795  // Cache the current string pointer
796  result.pStr = &pXML->lpXML[indexStart];
797 
798  // First check whether the token is in the clear tag list (meaning it
799  // does not need formatting).
800  ALLXMLClearTag *ctag=XMLClearTags;
801  do
802  {
803  if (xstrncmp(ctag->lpszOpen, result.pStr, ctag->openTagLen)==0)
804  {
805  result.pClr=ctag;
806  pXML->nIndex+=ctag->openTagLen-1;
807  *pType=eTokenClear;
808  return result;
809  }
810  ctag++;
811  } while(ctag->lpszOpen);
812 
813  // If we didn't find a clear tag then check for standard tokens
814  switch(ch)
815  {
816  // Check for quotes
817  case _X('\''):
818  case _X('\"'):
819  // Type of token
820  *pType = eTokenQuotedText;
821  chTemp = ch;
822 
823  // Set the size
824  nFoundMatch = FALSE;
825 
826  // Search through the string to find a matching quote
827  while((ch = getNextChar(pXML)))
828  {
829  if (ch==chTemp) { nFoundMatch = TRUE; break; }
830  if (ch==_X('<')) break;
831  }
832 
833  // If we failed to find a matching quote
834  if (nFoundMatch == FALSE)
835  {
836  pXML->nIndex=indexStart+1;
837  nIsText=TRUE;
838  break;
839  }
840 
841 // 4.02.2002
842 // if (FindNonWhiteSpace(pXML)) pXML->nIndex--;
843 
844  break;
845 
846  // Equals (used with attribute values)
847  case _X('='):
848  *pType = eTokenEquals;
849  break;
850 
851  // Close tag
852  case _X('>'):
853  *pType = eTokenCloseTag;
854  break;
855 
856  // Check for tag start and tag end
857  case _X('<'):
858 
859  // Peek at the next character to see if we have an end tag '</',
860  // or an xml declaration '<?'
861  chTemp = pXML->lpXML[pXML->nIndex];
862 
863  // If we have a tag end...
864  if (chTemp == _X('/'))
865  {
866  // Set the type and ensure we point at the next character
867  getNextChar(pXML);
868  *pType = eTokenTagEnd;
869  }
870 
871  // If we have an XML declaration tag
872  else if (chTemp == _X('?'))
873  {
874 
875  // Set the type and ensure we point at the next character
876  getNextChar(pXML);
877  *pType = eTokenDeclaration;
878  }
879 
880  // Otherwise we must have a start tag
881  else
882  {
883  *pType = eTokenTagStart;
884  }
885  break;
886 
887  // Check to see if we have a short hand type end tag ('/>').
888  case _X('/'):
889 
890  // Peek at the next character to see if we have a short end tag '/>'
891  chTemp = pXML->lpXML[pXML->nIndex];
892 
893  // If we have a short hand end tag...
894  if (chTemp == _X('>'))
895  {
896  // Set the type and ensure we point at the next character
897  getNextChar(pXML);
898  *pType = eTokenShortHandClose;
899  break;
900  }
901 
902  // If we haven't found a short hand closing tag then drop into the
903  // text process
904 
905  // Other characters
906  default:
907  nIsText = TRUE;
908  }
909 
910  // If this is a TEXT node
911  if (nIsText)
912  {
913  // Indicate we are dealing with text
914  *pType = eTokenText;
915  while((ch = getNextChar(pXML)))
916  {
917  if XML_isSPACECHAR(ch)
918  {
919  indexStart++; break;
920 
921  } else if (ch==_X('/'))
922  {
923  // If we find a slash then this maybe text or a short hand end tag
924  // Peek at the next character to see it we have short hand end tag
925  ch=pXML->lpXML[pXML->nIndex];
926  // If we found a short hand end tag then we need to exit the loop
927  if (ch==_X('>')) { pXML->nIndex--; break; }
928 
929  } else if ((ch==_X('<'))||(ch==_X('>'))||(ch==_X('=')))
930  {
931  pXML->nIndex--; break;
932  }
933  }
934  }
935  *pcbToken = pXML->nIndex-indexStart;
936  } else
937  {
938  // If we failed to obtain a valid character
939  *pcbToken = 0;
940  *pType = eTokenError;
941  result.pStr=NULL;
942  }
943 
944  return result;
945 }
946 
947 XMLCSTR XMLNode::updateName_WOSD(XMLSTR lpszName)
948 {
949  if (!d) { free(lpszName); return NULL; }
950  if (d->lpszName&&(lpszName!=d->lpszName)) free((void*)d->lpszName);
951  d->lpszName=lpszName;
952  return lpszName;
953 }
954 
955 // private:
956 XMLNode::XMLNode(struct XMLNodeDataTag *p){ d=p; (p->ref_count)++; }
957 XMLNode::XMLNode(XMLNodeData *pParent, XMLSTR lpszName, char isDeclaration)
958 {
959  d=(XMLNodeData*)malloc(sizeof(XMLNodeData));
960  d->ref_count=1;
961 
962  d->lpszName=NULL;
963  d->nChild= 0;
964  d->nText = 0;
965  d->nClear = 0;
966  d->nAttribute = 0;
967 
968  d->isDeclaration = isDeclaration;
969 
970  d->pParent = pParent;
971  d->pChild= NULL;
972  d->pText= NULL;
973  d->pClear= NULL;
974  d->pAttribute= NULL;
975  d->pOrder= NULL;
976 
977  updateName_WOSD(lpszName);
978 }
979 
980 XMLNode XMLNode::createXMLTopNode_WOSD(XMLSTR lpszName, char isDeclaration) { return XMLNode(NULL,lpszName,isDeclaration); }
981 XMLNode XMLNode::createXMLTopNode(XMLCSTR lpszName, char isDeclaration) { return XMLNode(NULL,stringDup(lpszName),isDeclaration); }
982 
983 #define MEMORYINCREASE 50
984 
985 static inline void myFree(void *p) { if (p) free(p); };
986 static inline void *myRealloc(void *p, int newsize, int memInc, int sizeofElem)
987 {
988  if (p==NULL) { if (memInc) return malloc(memInc*sizeofElem); return malloc(sizeofElem); }
989  if ((memInc==0)||((newsize%memInc)==0)) p=realloc(p,(newsize+memInc)*sizeofElem);
990 // if (!p)
991 // {
992 // printf("XMLParser > Error: Not enough memory! Aborting...\n"); exit(220);
993 // }
994  return p;
995 }
996 
997 // private:
998 int XMLNode::findPosition(XMLNodeData *d, int index, XMLElementType xtype)
999 {
1000  if (index<0) return -1;
1001  int i=0,j=(int)((index<<2)+xtype),*o=d->pOrder; while (o[i]!=j) i++; return i;
1002 }
1003 
1004 // private:
1005 // update "order" information when deleting a content of a XMLNode
1006 int XMLNode::removeOrderElement(XMLNodeData *d, XMLElementType t, int index)
1007 {
1008  int n=d->nChild+d->nText+d->nClear, *o=d->pOrder,i=findPosition(d,index,t);
1009  memmove(o+i, o+i+1, (n-i)*sizeof(int));
1010  for (;i<n;i++)
1011  if ((o[i]&3)==(int)t) o[i]-=4;
1012  // We should normally do:
1013  // d->pOrder=(int)realloc(d->pOrder,n*sizeof(int));
1014  // but we skip reallocation because it's too time consuming.
1015  // Anyway, at the end, it will be free'd completely at once.
1016  return i;
1017 }
1018 
1019 void *XMLNode::addToOrder(int memoryIncrease,int *_pos, int nc, void *p, int size, XMLElementType xtype)
1020 {
1021  // in: *_pos is the position inside d->pOrder ("-1" means "EndOf")
1022  // out: *_pos is the index inside p
1023  p=myRealloc(p,(nc+1),memoryIncrease,size);
1024  int n=d->nChild+d->nText+d->nClear;
1025  d->pOrder=(int*)myRealloc(d->pOrder,n+1,memoryIncrease*3,sizeof(int));
1026  int pos=*_pos,*o=d->pOrder;
1027 
1028  if ((pos<0)||(pos>=n)) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
1029 
1030  int i=pos;
1031  memmove(o+i+1, o+i, (n-i)*sizeof(int));
1032 
1033  while ((pos<n)&&((o[pos]&3)!=(int)xtype)) pos++;
1034  if (pos==n) { *_pos=nc; o[n]=(int)((nc<<2)+xtype); return p; }
1035 
1036  o[i]=o[pos];
1037  for (i=pos+1;i<=n;i++) if ((o[i]&3)==(int)xtype) o[i]+=4;
1038 
1039  *_pos=pos=o[pos]>>2;
1040  memmove(((char*)p)+(pos+1)*size,((char*)p)+pos*size,(nc-pos)*size);
1041 
1042  return p;
1043 }
1044 
1045 // Add a child node to the given element.
1046 XMLNode XMLNode::addChild_priv(int memoryIncrease, XMLSTR lpszName, char isDeclaration, int pos)
1047 {
1048  if (!lpszName) return emptyXMLNode;
1049  d->pChild=(XMLNode*)addToOrder(memoryIncrease,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
1050  d->pChild[pos].d=NULL;
1051  d->pChild[pos]=XMLNode(d,lpszName,isDeclaration);
1052  d->nChild++;
1053  return d->pChild[pos];
1054 }
1055 
1056 // Add an attribute to an element.
1057 XMLAttribute *XMLNode::addAttribute_priv(int memoryIncrease,XMLSTR lpszName, XMLSTR lpszValuev)
1058 {
1059  if (!lpszName) return &emptyXMLAttribute;
1060  if (!d) { myFree(lpszName); myFree(lpszValuev); return &emptyXMLAttribute; }
1061  int nc=d->nAttribute;
1062  d->pAttribute=(XMLAttribute*)myRealloc(d->pAttribute,(nc+1),memoryIncrease,sizeof(XMLAttribute));
1063  XMLAttribute *pAttr=d->pAttribute+nc;
1064  pAttr->lpszName = lpszName;
1065  pAttr->lpszValue = lpszValuev;
1066  d->nAttribute++;
1067  return pAttr;
1068 }
1069 
1070 // Add text to the element.
1071 XMLCSTR XMLNode::addText_priv(int memoryIncrease, XMLSTR lpszValue, int pos)
1072 {
1073  if (!lpszValue) return NULL;
1074  if (!d) { myFree(lpszValue); return NULL; }
1075  d->pText=(XMLCSTR*)addToOrder(memoryIncrease,&pos,d->nText,d->pText,sizeof(XMLSTR),eNodeText);
1076  d->pText[pos]=lpszValue;
1077  d->nText++;
1078  return lpszValue;
1079 }
1080 
1081 // Add clear (unformatted) text to the element.
1082 XMLClear *XMLNode::addClear_priv(int memoryIncrease, XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
1083 {
1084  if (!lpszValue) return &emptyXMLClear;
1085  if (!d) { myFree(lpszValue); return &emptyXMLClear; }
1086  d->pClear=(XMLClear *)addToOrder(memoryIncrease,&pos,d->nClear,d->pClear,sizeof(XMLClear),eNodeClear);
1087  XMLClear *pNewClear=d->pClear+pos;
1088  pNewClear->lpszValue = lpszValue;
1089  if (!lpszOpen) lpszOpen=XMLClearTags->lpszOpen;
1090  if (!lpszClose) lpszClose=XMLClearTags->lpszClose;
1091  pNewClear->lpszOpenTag = lpszOpen;
1092  pNewClear->lpszCloseTag = lpszClose;
1093  d->nClear++;
1094  return pNewClear;
1095 }
1096 
1097 // private:
1098 // Parse a clear (unformatted) type node.
1099 char XMLNode::parseClearTag(void *px, void *_pClear)
1100 {
1101  XML *pXML=(XML *)px;
1102  ALLXMLClearTag pClear=*((ALLXMLClearTag*)_pClear);
1103  int cbTemp=0;
1104  XMLCSTR lpszTemp=NULL;
1105  XMLCSTR lpXML=&pXML->lpXML[pXML->nIndex];
1106  static XMLCSTR docTypeEnd=_X("]>");
1107 
1108  // Find the closing tag
1109  // Seems the <!DOCTYPE need a better treatment so lets handle it
1110  if (pClear.lpszOpen==XMLClearTags[1].lpszOpen)
1111  {
1112  XMLCSTR pCh=lpXML;
1113  while (*pCh)
1114  {
1115  if (*pCh==_X('<')) { pClear.lpszClose=docTypeEnd; lpszTemp=xstrstr(lpXML,docTypeEnd); break; }
1116  else if (*pCh==_X('>')) { lpszTemp=pCh; break; }
1117 #ifdef _XMLWIDECHAR
1118  pCh++;
1119 #else
1120  pCh+=XML_ByteTable[(unsigned char)(*pCh)];
1121 #endif
1122  }
1123  } else lpszTemp=xstrstr(lpXML, pClear.lpszClose);
1124 
1125  if (lpszTemp)
1126  {
1127  // Cache the size and increment the index
1128  cbTemp = (int)(lpszTemp - lpXML);
1129 
1130  pXML->nIndex += cbTemp+(int)xstrlen(pClear.lpszClose);
1131 
1132  // Add the clear node to the current element
1133  addClear_priv(MEMORYINCREASE,stringDup(lpXML,cbTemp), pClear.lpszOpen, pClear.lpszClose,-1);
1134  return 0;
1135  }
1136 
1137  // If we failed to find the end tag
1138  pXML->error = eXMLErrorUnmatchedEndClearTag;
1139  return 1;
1140 }
1141 
1142 void XMLNode::exactMemory(XMLNodeData *d)
1143 {
1144  if (d->pOrder) d->pOrder=(int*)realloc(d->pOrder,(d->nChild+d->nText+d->nClear)*sizeof(int));
1145  if (d->pChild) d->pChild=(XMLNode*)realloc(d->pChild,d->nChild*sizeof(XMLNode));
1146  if (d->pAttribute) d->pAttribute=(XMLAttribute*)realloc(d->pAttribute,d->nAttribute*sizeof(XMLAttribute));
1147  if (d->pText) d->pText=(XMLCSTR*)realloc(d->pText,d->nText*sizeof(XMLSTR));
1148  if (d->pClear) d->pClear=(XMLClear *)realloc(d->pClear,d->nClear*sizeof(XMLClear));
1149 }
1150 
1151 char XMLNode::maybeAddTxT(void *pa, XMLCSTR tokenPStr)
1152 {
1153  XML *pXML=(XML *)pa;
1154  XMLCSTR lpszText=pXML->lpszText;
1155  if (!lpszText) return 0;
1156  if (dropWhiteSpace) while (XML_isSPACECHAR(*lpszText)&&(lpszText!=tokenPStr)) lpszText++;
1157  int cbText = (int)(tokenPStr - lpszText);
1158  if (!cbText) { pXML->lpszText=NULL; return 0; }
1159  if (dropWhiteSpace) { cbText--; while ((cbText)&&XML_isSPACECHAR(lpszText[cbText])) cbText--; cbText++; }
1160  if (!cbText) { pXML->lpszText=NULL; return 0; }
1161  XMLSTR lpt=fromXMLString(lpszText,cbText,pXML);
1162  if (!lpt) return 1;
1163  addText_priv(MEMORYINCREASE,lpt,-1);
1164  pXML->lpszText=NULL;
1165  return 0;
1166 }
1167 // private:
1168 // Recursively parse an XML element.
1169 int XMLNode::ParseXMLElement(void *pa)
1170 {
1171  XML *pXML=(XML *)pa;
1172  int cbToken;
1173  enum XMLTokenTypeTag xtype;
1174  NextToken token;
1175  XMLCSTR lpszTemp=NULL;
1176  int cbTemp=0;
1177  char nDeclaration;
1178  XMLNode pNew;
1179  enum Status status; // inside or outside a tag
1180  enum Attrib attrib = eAttribName;
1181 
1182  assert(pXML);
1183 
1184  // If this is the first call to the function
1185  if (pXML->nFirst)
1186  {
1187  // Assume we are outside of a tag definition
1188  pXML->nFirst = FALSE;
1189  status = eOutsideTag;
1190  } else
1191  {
1192  // If this is not the first call then we should only be called when inside a tag.
1193  status = eInsideTag;
1194  }
1195 
1196  // Iterate through the tokens in the document
1197  for(;;)
1198  {
1199  // Obtain the next token
1200  token = GetNextToken(pXML, &cbToken, &xtype);
1201 
1202  if (xtype != eTokenError)
1203  {
1204  // Check the current status
1205  switch(status)
1206  {
1207 
1208  // If we are outside of a tag definition
1209  case eOutsideTag:
1210 
1211  // Check what type of token we obtained
1212  switch(xtype)
1213  {
1214  // If we have found text or quoted text
1215  case eTokenText:
1216  case eTokenCloseTag: /* '>' */
1217  case eTokenShortHandClose: /* '/>' */
1218  case eTokenQuotedText:
1219  case eTokenEquals:
1220  break;
1221 
1222  // If we found a start tag '<' and declarations '<?'
1223  case eTokenTagStart:
1224  case eTokenDeclaration:
1225 
1226  // Cache whether this new element is a declaration or not
1227  nDeclaration = (xtype == eTokenDeclaration);
1228 
1229  // If we have node text then add this to the element
1230  if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1231 
1232  // Find the name of the tag
1233  token = GetNextToken(pXML, &cbToken, &xtype);
1234 
1235  // Return an error if we couldn't obtain the next token or
1236  // it wasnt text
1237  if (xtype != eTokenText)
1238  {
1239  pXML->error = eXMLErrorMissingTagName;
1240  return FALSE;
1241  }
1242 
1243  // If we found a new element which is the same as this
1244  // element then we need to pass this back to the caller..
1245 
1246 #ifdef APPROXIMATE_PARSING
1247  if (d->lpszName &&
1248  myTagCompare(d->lpszName, token.pStr) == 0)
1249  {
1250  // Indicate to the caller that it needs to create a
1251  // new element.
1252  pXML->lpNewElement = token.pStr;
1253  pXML->cbNewElement = cbToken;
1254  return TRUE;
1255  } else
1256 #endif
1257  {
1258  // If the name of the new element differs from the name of
1259  // the current element we need to add the new element to
1260  // the current one and recurse
1261  pNew = addChild_priv(MEMORYINCREASE,stringDup(token.pStr,cbToken), nDeclaration,-1);
1262 
1263  while (!pNew.isEmpty())
1264  {
1265  // Callself to process the new node. If we return
1266  // FALSE this means we dont have any more
1267  // processing to do...
1268 
1269  if (!pNew.ParseXMLElement(pXML)) return FALSE;
1270  else
1271  {
1272  // If the call to recurse this function
1273  // evented in a end tag specified in XML then
1274  // we need to unwind the calls to this
1275  // function until we find the appropriate node
1276  // (the element name and end tag name must
1277  // match)
1278  if (pXML->cbEndTag)
1279  {
1280  // If we are back at the root node then we
1281  // have an unmatched end tag
1282  if (!d->lpszName)
1283  {
1284  pXML->error=eXMLErrorUnmatchedEndTag;
1285  return FALSE;
1286  }
1287 
1288  // If the end tag matches the name of this
1289  // element then we only need to unwind
1290  // once more...
1291 
1292  if (myTagCompare(d->lpszName, pXML->lpEndTag)==0)
1293  {
1294  pXML->cbEndTag = 0;
1295  }
1296 
1297  return TRUE;
1298  } else
1299  if (pXML->cbNewElement)
1300  {
1301  // If the call indicated a new element is to
1302  // be created on THIS element.
1303 
1304  // If the name of this element matches the
1305  // name of the element we need to create
1306  // then we need to return to the caller
1307  // and let it process the element.
1308 
1309  if (myTagCompare(d->lpszName, pXML->lpNewElement)==0)
1310  {
1311  return TRUE;
1312  }
1313 
1314  // Add the new element and recurse
1315  pNew = addChild_priv(MEMORYINCREASE,stringDup(pXML->lpNewElement,pXML->cbNewElement),0,-1);
1316  pXML->cbNewElement = 0;
1317  }
1318  else
1319  {
1320  // If we didn't have a new element to create
1321  pNew = emptyXMLNode;
1322 
1323  }
1324  }
1325  }
1326  }
1327  break;
1328 
1329  // If we found an end tag
1330  case eTokenTagEnd:
1331 
1332  // If we have node text then add this to the element
1333  if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1334 
1335  // Find the name of the end tag
1336  token = GetNextToken(pXML, &cbTemp, &xtype);
1337 
1338  // The end tag should be text
1339  if (xtype != eTokenText)
1340  {
1341  pXML->error = eXMLErrorMissingEndTagName;
1342  return FALSE;
1343  }
1344  lpszTemp = token.pStr;
1345 
1346  // After the end tag we should find a closing tag
1347  token = GetNextToken(pXML, &cbToken, &xtype);
1348  if (xtype != eTokenCloseTag)
1349  {
1350  pXML->error = eXMLErrorMissingEndTagName;
1351  return FALSE;
1352  }
1353  pXML->lpszText=pXML->lpXML+pXML->nIndex;
1354 
1355  // We need to return to the previous caller. If the name
1356  // of the tag cannot be found we need to keep returning to
1357  // caller until we find a match
1358  if (myTagCompare(d->lpszName, lpszTemp) != 0)
1359 #ifdef STRICT_PARSING
1360  {
1361  pXML->error=eXMLErrorUnmatchedEndTag;
1362  pXML->nIndexMissigEndTag=pXML->nIndex;
1363  return FALSE;
1364  }
1365 #else
1366  {
1367  pXML->error=eXMLErrorMissingEndTag;
1368  pXML->nIndexMissigEndTag=pXML->nIndex;
1369  pXML->lpEndTag = lpszTemp;
1370  pXML->cbEndTag = cbTemp;
1371  }
1372 #endif
1373 
1374  // Return to the caller
1375  exactMemory(d);
1376  return TRUE;
1377 
1378  // If we found a clear (unformatted) token
1379  case eTokenClear:
1380  // If we have node text then add this to the element
1381  if (maybeAddTxT(pXML,token.pStr)) return FALSE;
1382  if (parseClearTag(pXML, token.pClr)) return FALSE;
1383  pXML->lpszText=pXML->lpXML+pXML->nIndex;
1384  break;
1385 
1386  default:
1387  break;
1388  }
1389  break;
1390 
1391  // If we are inside a tag definition we need to search for attributes
1392  case eInsideTag:
1393 
1394  // Check what part of the attribute (name, equals, value) we
1395  // are looking for.
1396  switch(attrib)
1397  {
1398  // If we are looking for a new attribute
1399  case eAttribName:
1400 
1401  // Check what the current token type is
1402  switch(xtype)
1403  {
1404  // If the current type is text...
1405  // Eg. 'attribute'
1406  case eTokenText:
1407  // Cache the token then indicate that we are next to
1408  // look for the equals
1409  lpszTemp = token.pStr;
1410  cbTemp = cbToken;
1411  attrib = eAttribEquals;
1412  break;
1413 
1414  // If we found a closing tag...
1415  // Eg. '>'
1416  case eTokenCloseTag:
1417  // We are now outside the tag
1418  status = eOutsideTag;
1419  pXML->lpszText=pXML->lpXML+pXML->nIndex;
1420  break;
1421 
1422  // If we found a short hand '/>' closing tag then we can
1423  // return to the caller
1424  case eTokenShortHandClose:
1425  exactMemory(d);
1426  pXML->lpszText=pXML->lpXML+pXML->nIndex;
1427  return TRUE;
1428 
1429  // Errors...
1430  case eTokenQuotedText: /* '"SomeText"' */
1431  case eTokenTagStart: /* '<' */
1432  case eTokenTagEnd: /* '</' */
1433  case eTokenEquals: /* '=' */
1434  case eTokenDeclaration: /* '<?' */
1435  case eTokenClear:
1436  pXML->error = eXMLErrorUnexpectedToken;
1437  return FALSE;
1438  default: break;
1439  }
1440  break;
1441 
1442  // If we are looking for an equals
1443  case eAttribEquals:
1444  // Check what the current token type is
1445  switch(xtype)
1446  {
1447  // If the current type is text...
1448  // Eg. 'Attribute AnotherAttribute'
1449  case eTokenText:
1450  // Add the unvalued attribute to the list
1451  addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
1452  // Cache the token then indicate. We are next to
1453  // look for the equals attribute
1454  lpszTemp = token.pStr;
1455  cbTemp = cbToken;
1456  break;
1457 
1458  // If we found a closing tag 'Attribute >' or a short hand
1459  // closing tag 'Attribute />'
1460  case eTokenShortHandClose:
1461  case eTokenCloseTag:
1462  // If we are a declaration element '<?' then we need
1463  // to remove extra closing '?' if it exists
1464  pXML->lpszText=pXML->lpXML+pXML->nIndex;
1465 
1466  if (d->isDeclaration &&
1467  (lpszTemp[cbTemp-1]) == _X('?'))
1468  {
1469  cbTemp--;
1470  }
1471 
1472  if (cbTemp)
1473  {
1474  // Add the unvalued attribute to the list
1475  addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp), NULL);
1476  }
1477 
1478  // If this is the end of the tag then return to the caller
1479  if (xtype == eTokenShortHandClose)
1480  {
1481  exactMemory(d);
1482  return TRUE;
1483  }
1484 
1485  // We are now outside the tag
1486  status = eOutsideTag;
1487  break;
1488 
1489  // If we found the equals token...
1490  // Eg. 'Attribute ='
1491  case eTokenEquals:
1492  // Indicate that we next need to search for the value
1493  // for the attribute
1494  attrib = eAttribValue;
1495  break;
1496 
1497  // Errors...
1498  case eTokenQuotedText: /* 'Attribute "InvalidAttr"'*/
1499  case eTokenTagStart: /* 'Attribute <' */
1500  case eTokenTagEnd: /* 'Attribute </' */
1501  case eTokenDeclaration: /* 'Attribute <?' */
1502  case eTokenClear:
1503  pXML->error = eXMLErrorUnexpectedToken;
1504  return FALSE;
1505  default: break;
1506  }
1507  break;
1508 
1509  // If we are looking for an attribute value
1510  case eAttribValue:
1511  // Check what the current token type is
1512  switch(xtype)
1513  {
1514  // If the current type is text or quoted text...
1515  // Eg. 'Attribute = "Value"' or 'Attribute = Value' or
1516  // 'Attribute = 'Value''.
1517  case eTokenText:
1518  case eTokenQuotedText:
1519  // If we are a declaration element '<?' then we need
1520  // to remove extra closing '?' if it exists
1521  if (d->isDeclaration &&
1522  (token.pStr[cbToken-1]) == _X('?'))
1523  {
1524  cbToken--;
1525  }
1526 
1527  if (cbTemp)
1528  {
1529  // Add the valued attribute to the list
1530  if (xtype==eTokenQuotedText) { token.pStr++; cbToken-=2; }
1531  XMLSTR attrVal=(XMLSTR)token.pStr;
1532  if (attrVal)
1533  {
1534  attrVal=fromXMLString(attrVal,cbToken,pXML);
1535  if (!attrVal) return FALSE;
1536  }
1537  addAttribute_priv(MEMORYINCREASE,stringDup(lpszTemp,cbTemp),attrVal);
1538  }
1539 
1540  // Indicate we are searching for a new attribute
1541  attrib = eAttribName;
1542  break;
1543 
1544  // Errors...
1545  case eTokenTagStart: /* 'Attr = <' */
1546  case eTokenTagEnd: /* 'Attr = </' */
1547  case eTokenCloseTag: /* 'Attr = >' */
1548  case eTokenShortHandClose: /* "Attr = />" */
1549  case eTokenEquals: /* 'Attr = =' */
1550  case eTokenDeclaration: /* 'Attr = <?' */
1551  case eTokenClear:
1552  pXML->error = eXMLErrorUnexpectedToken;
1553  return FALSE;
1554  break;
1555  default: break;
1556  }
1557  }
1558  }
1559  }
1560  // If we failed to obtain the next token
1561  else
1562  {
1563  if ((!d->isDeclaration)&&(d->pParent))
1564  {
1565 #ifdef STRICT_PARSING
1566  pXML->error=eXMLErrorUnmatchedEndTag;
1567 #else
1568  pXML->error=eXMLErrorMissingEndTag;
1569 #endif
1570  pXML->nIndexMissigEndTag=pXML->nIndex;
1571  }
1572  maybeAddTxT(pXML,pXML->lpXML+pXML->nIndex);
1573  return FALSE;
1574  }
1575  }
1576 }
1577 
1578 // Count the number of lines and columns in an XML string.
1579 static void CountLinesAndColumns(XMLCSTR lpXML, int nUpto, XMLResults *pResults)
1580 {
1581  XMLCHAR ch;
1582  assert(lpXML);
1583  assert(pResults);
1584 
1585  struct XML xml={ lpXML,lpXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
1586 
1587  pResults->nLine = 1;
1588  pResults->nColumn = 1;
1589  while (xml.nIndex<nUpto)
1590  {
1591  ch = getNextChar(&xml);
1592  if (ch != _X('\n')) pResults->nColumn++;
1593  else
1594  {
1595  pResults->nLine++;
1596  pResults->nColumn=1;
1597  }
1598  }
1599 }
1600 
1601 // Parse XML and return the root element.
1602 XMLNode XMLNode::parseString(XMLCSTR lpszXML, XMLCSTR tag, XMLResults *pResults)
1603 {
1604  if (!lpszXML)
1605  {
1606  if (pResults)
1607  {
1608  pResults->error=eXMLErrorNoElements;
1609  pResults->nLine=0;
1610  pResults->nColumn=0;
1611  }
1612  return emptyXMLNode;
1613  }
1614 
1615  XMLNode xnode(NULL,NULL,FALSE);
1616  struct XML xml={ lpszXML, lpszXML, 0, 0, eXMLErrorNone, NULL, 0, NULL, 0, TRUE };
1617 
1618  // Create header element
1619  xnode.ParseXMLElement(&xml);
1620  enum XMLError error = xml.error;
1621  if (!xnode.nChildNode()) error=eXMLErrorNoXMLTagFound;
1622  if ((xnode.nChildNode()==1)&&(xnode.nElement()==1)) xnode=xnode.getChildNode(); // skip the empty node
1623 
1624  // If no error occurred
1625  if ((error==eXMLErrorNone)||(error==eXMLErrorMissingEndTag)||(error==eXMLErrorNoXMLTagFound))
1626  {
1627  XMLCSTR name=xnode.getName();
1628  if (tag&&xstrlen(tag)&&((!name)||(xstricmp(xnode.getName(),tag))))
1629  {
1630  XMLNode nodeTmp;
1631  int i=0;
1632  while (i<xnode.nChildNode())
1633  {
1634  nodeTmp=xnode.getChildNode(i);
1635  if (xstricmp(nodeTmp.getName(),tag)==0) break;
1636  if (nodeTmp.isDeclaration()) { xnode=nodeTmp; i=0; } else i++;
1637  }
1638  if (i>=xnode.nChildNode())
1639  {
1640  if (pResults)
1641  {
1642  pResults->error=eXMLErrorFirstTagNotFound;
1643  pResults->nLine=0;
1644  pResults->nColumn=0;
1645  }
1646  return emptyXMLNode;
1647  }
1648  xnode=nodeTmp;
1649  }
1650  } else
1651  {
1652  // Cleanup: this will destroy all the nodes
1653  xnode = emptyXMLNode;
1654  }
1655 
1656 
1657  // If we have been given somewhere to place results
1658  if (pResults)
1659  {
1660  pResults->error = error;
1661 
1662  // If we have an error
1663  if (error!=eXMLErrorNone)
1664  {
1665  if (error==eXMLErrorMissingEndTag) xml.nIndex=xml.nIndexMissigEndTag;
1666  // Find which line and column it starts on.
1667  CountLinesAndColumns(xml.lpXML, xml.nIndex, pResults);
1668  }
1669  }
1670  return xnode;
1671 }
1672 
1673 XMLNode XMLNode::parseFile(XMLCSTR filename, XMLCSTR tag, XMLResults *pResults)
1674 {
1675  if (pResults) { pResults->nLine=0; pResults->nColumn=0; }
1676  FILE *f=xfopen(filename,_X("rb"));
1677  if (f==NULL) { if (pResults) pResults->error=eXMLErrorFileNotFound; return emptyXMLNode; }
1678  fseek(f,0,SEEK_END);
1679  int l=ftell(f),headerSz=0;
1680  if (!l) { if (pResults) pResults->error=eXMLErrorEmpty; fclose(f); return emptyXMLNode; }
1681  fseek(f,0,SEEK_SET);
1682  unsigned char *buf=(unsigned char*)malloc(l+4);
1683  fread(buf,l,1,f);
1684  fclose(f);
1685  buf[l]=0;buf[l+1]=0;buf[l+2]=0;buf[l+3]=0;
1686 #ifdef _XMLWIDECHAR
1687  if (guessWideCharChars)
1688  {
1689  if (!myIsTextWideChar(buf,l))
1690  {
1691  if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
1692  XMLSTR b2=myMultiByteToWideChar((const char*)(buf+headerSz));
1693  free(buf); buf=(unsigned char*)b2; headerSz=0;
1694  } else
1695  {
1696  if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
1697  if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
1698  }
1699  }
1700 #else
1701  if (guessWideCharChars)
1702  {
1703  if (myIsTextWideChar(buf,l))
1704  {
1705  l/=sizeof(wchar_t);
1706  if ((buf[0]==0xef)&&(buf[1]==0xff)) headerSz=2;
1707  if ((buf[0]==0xff)&&(buf[1]==0xfe)) headerSz=2;
1708  char *b2=myWideCharToMultiByte((const wchar_t*)(buf+headerSz));
1709  free(buf); buf=(unsigned char*)b2; headerSz=0;
1710  } else
1711  {
1712  if ((buf[0]==0xef)&&(buf[1]==0xbb)&&(buf[2]==0xbf)) headerSz=3;
1713  }
1714  }
1715 #endif
1716 
1717  if (!buf) { if (pResults) pResults->error=eXMLErrorCharConversionError; return emptyXMLNode; }
1718  XMLNode x=parseString((XMLSTR)(buf+headerSz),tag,pResults);
1719  free(buf);
1720  return x;
1721 }
1722 
1723 static inline void charmemset(XMLSTR dest,XMLCHAR c,int l) { while (l--) *(dest++)=c; }
1724 // private:
1725 // Creates an user friendly XML string from a given element with
1726 // appropriate white space and carriage returns.
1727 //
1728 // This recurses through all subnodes then adds contents of the nodes to the
1729 // string.
1730 int XMLNode::CreateXMLStringR(XMLNodeData *pEntry, XMLSTR lpszMarker, int nFormat)
1731 {
1732  int nResult = 0;
1733  int cb;
1734  int cbElement;
1735  int nChildFormat=-1;
1736  int nElementI=pEntry->nChild+pEntry->nText+pEntry->nClear;
1737  int i,j;
1738 
1739  assert(pEntry);
1740 
1741 #define LENSTR(lpsz) (lpsz ? xstrlen(lpsz) : 0)
1742 
1743  // If the element has no name then assume this is the head node.
1744  cbElement = (int)LENSTR(pEntry->lpszName);
1745 
1746  if (cbElement)
1747  {
1748  // "<elementname "
1749  cb = nFormat == -1 ? 0 : nFormat;
1750 
1751  if (lpszMarker)
1752  {
1753  if (cb) charmemset(lpszMarker, INDENTCHAR, sizeof(XMLCHAR)*cb);
1754  nResult = cb;
1755  lpszMarker[nResult++]=_X('<');
1756  if (pEntry->isDeclaration) lpszMarker[nResult++]=_X('?');
1757  xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
1758  nResult+=cbElement;
1759  lpszMarker[nResult++]=_X(' ');
1760 
1761  } else
1762  {
1763  nResult+=cbElement+2+cb;
1764  if (pEntry->isDeclaration) nResult++;
1765  }
1766 
1767  // Enumerate attributes and add them to the string
1768  XMLAttribute *pAttr=pEntry->pAttribute;
1769  for (i=0; i<pEntry->nAttribute; i++)
1770  {
1771  // "Attrib
1772  cb = (int)LENSTR(pAttr->lpszName);
1773  if (cb)
1774  {
1775  if (lpszMarker) xstrcpy(&lpszMarker[nResult], pAttr->lpszName);
1776  nResult += cb;
1777  // "Attrib=Value "
1778  if (pAttr->lpszValue)
1779  {
1780  cb=(int)lengthXMLString(pAttr->lpszValue);
1781  if (lpszMarker)
1782  {
1783  lpszMarker[nResult]=_X('=');
1784  lpszMarker[nResult+1]=_X('"');
1785  if (cb) toXMLStringUnSafe(&lpszMarker[nResult+2],pAttr->lpszValue);
1786  lpszMarker[nResult+cb+2]=_X('"');
1787  }
1788  nResult+=cb+3;
1789  }
1790  if (lpszMarker) lpszMarker[nResult] = _X(' ');
1791  nResult++;
1792  }
1793  pAttr++;
1794  }
1795 
1796  if (pEntry->isDeclaration)
1797  {
1798  if (lpszMarker)
1799  {
1800  lpszMarker[nResult-1]=_X('?');
1801  lpszMarker[nResult]=_X('>');
1802  }
1803  nResult++;
1804  if (nFormat!=-1)
1805  {
1806  if (lpszMarker) lpszMarker[nResult]=_X('\n');
1807  nResult++;
1808  }
1809  } else
1810  // If there are child nodes we need to terminate the start tag
1811  if (nElementI)
1812  {
1813  if (lpszMarker) lpszMarker[nResult-1]=_X('>');
1814  if (nFormat!=-1)
1815  {
1816  if (lpszMarker) lpszMarker[nResult]=_X('\n');
1817  nResult++;
1818  }
1819  } else nResult--;
1820  }
1821 
1822  // Calculate the child format for when we recurse. This is used to
1823  // determine the number of spaces used for prefixes.
1824  if (nFormat!=-1)
1825  {
1826  if (cbElement&&(!pEntry->isDeclaration)) nChildFormat=nFormat+1;
1827  else nChildFormat=nFormat;
1828  }
1829 
1830  // Enumerate through remaining children
1831  for (i=0; i<nElementI; i++)
1832  {
1833  j=pEntry->pOrder[i];
1834  switch((XMLElementType)(j&3))
1835  {
1836  // Text nodes
1837  case eNodeText:
1838  {
1839  // "Text"
1840  XMLCSTR pChild=pEntry->pText[j>>2];
1841  cb = (int)lengthXMLString(pChild);
1842  if (cb)
1843  {
1844  if (nFormat!=-1)
1845  {
1846  if (lpszMarker)
1847  {
1848  charmemset(&lpszMarker[nResult],INDENTCHAR,sizeof(XMLCHAR)*(nFormat + 1));
1849  toXMLStringUnSafe(&lpszMarker[nResult+nFormat+1],pChild);
1850  lpszMarker[nResult+nFormat+1+cb]=_X('\n');
1851  }
1852  nResult+=cb+nFormat+2;
1853  } else
1854  {
1855  if (lpszMarker) toXMLStringUnSafe(&lpszMarker[nResult], pChild);
1856  nResult += cb;
1857  }
1858  }
1859  break;
1860  }
1861 
1862  // Clear type nodes
1863  case eNodeClear:
1864  {
1865  XMLClear *pChild=pEntry->pClear+(j>>2);
1866  // "OpenTag"
1867  cb = (int)LENSTR(pChild->lpszOpenTag);
1868  if (cb)
1869  {
1870  if (nFormat!=-1)
1871  {
1872  if (lpszMarker)
1873  {
1874  charmemset(&lpszMarker[nResult], INDENTCHAR, sizeof(XMLCHAR)*(nFormat + 1));
1875  xstrcpy(&lpszMarker[nResult+nFormat+1], pChild->lpszOpenTag);
1876  }
1877  nResult+=cb+nFormat+1;
1878  }
1879  else
1880  {
1881  if (lpszMarker)xstrcpy(&lpszMarker[nResult], pChild->lpszOpenTag);
1882  nResult += cb;
1883  }
1884  }
1885 
1886  // "OpenTag Value"
1887  cb = (int)LENSTR(pChild->lpszValue);
1888  if (cb)
1889  {
1890  if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszValue);
1891  nResult += cb;
1892  }
1893 
1894  // "OpenTag Value CloseTag"
1895  cb = (int)LENSTR(pChild->lpszCloseTag);
1896  if (cb)
1897  {
1898  if (lpszMarker) xstrcpy(&lpszMarker[nResult], pChild->lpszCloseTag);
1899  nResult += cb;
1900  }
1901 
1902  if (nFormat!=-1)
1903  {
1904  if (lpszMarker) lpszMarker[nResult] = _X('\n');
1905  nResult++;
1906  }
1907  break;
1908  }
1909 
1910  // Element nodes
1911  case eNodeChild:
1912  {
1913  // Recursively add child nodes
1914  nResult += CreateXMLStringR(pEntry->pChild[j>>2].d, lpszMarker ? lpszMarker + nResult : 0, nChildFormat);
1915  break;
1916  }
1917  default: break;
1918  }
1919  }
1920 
1921  if ((cbElement)&&(!pEntry->isDeclaration))
1922  {
1923  // If we have child entries we need to use long XML notation for
1924  // closing the element - "<elementname>blah blah blah</elementname>"
1925  if (nElementI)
1926  {
1927  // "</elementname>\0"
1928  if (lpszMarker)
1929  {
1930  if (nFormat != -1)
1931  {
1932  if (nFormat)
1933  {
1934  charmemset(&lpszMarker[nResult], INDENTCHAR,sizeof(XMLCHAR)*nFormat);
1935  nResult+=nFormat;
1936  }
1937  }
1938 
1939  xstrcpy(&lpszMarker[nResult], _X("</"));
1940  nResult += 2;
1941  xstrcpy(&lpszMarker[nResult], pEntry->lpszName);
1942  nResult += cbElement;
1943 
1944  if (nFormat == -1)
1945  {
1946  xstrcpy(&lpszMarker[nResult], _X(">"));
1947  nResult++;
1948  } else
1949  {
1950  xstrcpy(&lpszMarker[nResult], _X(">\n"));
1951  nResult+=2;
1952  }
1953  } else
1954  {
1955  if (nFormat != -1) nResult+=cbElement+4+nFormat;
1956  else nResult+=cbElement+3;
1957  }
1958  } else
1959  {
1960  // If there are no children we can use shorthand XML notation -
1961  // "<elementname/>"
1962  // "/>\0"
1963  if (lpszMarker)
1964  {
1965  if (nFormat == -1)
1966  {
1967  xstrcpy(&lpszMarker[nResult], _X("/>"));
1968  nResult += 2;
1969  }
1970  else
1971  {
1972  xstrcpy(&lpszMarker[nResult], _X("/>\n"));
1973  nResult += 3;
1974  }
1975  }
1976  else
1977  {
1978  nResult += nFormat == -1 ? 2 : 3;
1979  }
1980  }
1981  }
1982 
1983  return nResult;
1984 }
1985 
1986 #undef LENSTR
1987 
1988 // Create an XML string
1989 // @param int nFormat - 0 if no formatting is required
1990 // otherwise nonzero for formatted text
1991 // with carriage returns and indentation.
1992 // @param int *pnSize - [out] pointer to the size of the
1993 // returned string not including the
1994 // NULL terminator.
1995 // @return XMLSTR - Allocated XML string, you must free
1996 // this with free().
1997 XMLSTR XMLNode::createXMLString(int nFormat, int *pnSize) const
1998 {
1999  if (!d) { if (pnSize) *pnSize=0; return NULL; }
2000 
2001  XMLSTR lpszResult = NULL;
2002  int cbStr;
2003 
2004  // Recursively Calculate the size of the XML string
2005  if (!dropWhiteSpace) nFormat=0;
2006  nFormat = nFormat ? 0 : -1;
2007  cbStr = CreateXMLStringR(d, 0, nFormat);
2008  assert(cbStr);
2009  // Alllocate memory for the XML string + the NULL terminator and
2010  // create the recursively XML string.
2011  lpszResult=(XMLSTR)malloc((cbStr+1)*sizeof(XMLCHAR));
2012  CreateXMLStringR(d, lpszResult, nFormat);
2013  if (pnSize) *pnSize = cbStr;
2014  return lpszResult;
2015 }
2016 
2017 int XMLNode::detachFromParent(XMLNodeData *d)
2018 {
2019  XMLNode *pa=d->pParent->pChild;
2020  int i=0;
2021  while (((void*)(pa[i].d))!=((void*)d)) i++;
2022  d->pParent->nChild--;
2023  if (d->pParent->nChild) memmove(pa+i,pa+i+1,(d->pParent->nChild-i)*sizeof(XMLNode));
2024  else { free(pa); d->pParent->pChild=NULL; }
2025  return removeOrderElement(d->pParent,eNodeChild,i);
2026 }
2027 
2028 XMLNode::~XMLNode() { deleteNodeContent_priv(1,0); }
2029 void XMLNode::deleteNodeContent(){ deleteNodeContent_priv(0,1); }
2030 void XMLNode::deleteNodeContent_priv(char isInDestuctor, char force)
2031 {
2032  if (!d) return;
2033  if (isInDestuctor) (d->ref_count)--;
2034  if ((d->ref_count==0)||force)
2035  {
2036  int i;
2037  if (d->pParent) detachFromParent(d);
2038  for(i=0; i<d->nChild; i++) { d->pChild[i].d->pParent=NULL; d->pChild[i].deleteNodeContent_priv(1,force); }
2039  myFree(d->pChild);
2040  for(i=0; i<d->nText; i++) free((void*)d->pText[i]);
2041  myFree(d->pText);
2042  for(i=0; i<d->nClear; i++) free((void*)d->pClear[i].lpszValue);
2043  myFree(d->pClear);
2044  for(i=0; i<d->nAttribute; i++)
2045  {
2046  free((void*)d->pAttribute[i].lpszName);
2047  if (d->pAttribute[i].lpszValue) free((void*)d->pAttribute[i].lpszValue);
2048  }
2049  myFree(d->pAttribute);
2050  myFree(d->pOrder);
2051  myFree((void*)d->lpszName);
2052  d->nChild=0; d->nText=0; d->nClear=0; d->nAttribute=0;
2053  d->pChild=NULL; d->pText=NULL; d->pClear=NULL; d->pAttribute=NULL;
2054  d->pOrder=NULL; d->lpszName=NULL; d->pParent=NULL;
2055  }
2056  if (d->ref_count==0)
2057  {
2058  free(d);
2059  d=NULL;
2060  }
2061 }
2062 
2063 XMLNode XMLNode::addChild(XMLNode childNode, int pos)
2064 {
2065  XMLNodeData *dc=childNode.d;
2066  if ((!dc)||(!d)) return childNode;
2067  if (dc->pParent) { if ((detachFromParent(dc)<=pos)&&(dc->pParent==d)) pos--; } else dc->ref_count++;
2068  dc->pParent=d;
2069 // int nc=d->nChild;
2070 // d->pChild=(XMLNode*)myRealloc(d->pChild,(nc+1),memoryIncrease,sizeof(XMLNode));
2071  d->pChild=(XMLNode*)addToOrder(0,&pos,d->nChild,d->pChild,sizeof(XMLNode),eNodeChild);
2072  d->pChild[pos].d=dc;
2073  d->nChild++;
2074  return childNode;
2075 }
2076 
2077 void XMLNode::deleteAttribute(int i)
2078 {
2079  if ((!d)||(i<0)||(i>=d->nAttribute)) return;
2080  d->nAttribute--;
2081  XMLAttribute *p=d->pAttribute+i;
2082  free((void*)p->lpszName);
2083  if (p->lpszValue) free((void*)p->lpszValue);
2084  if (d->nAttribute) memmove(p,p+1,(d->nAttribute-i)*sizeof(XMLAttribute)); else { free(p); d->pAttribute=NULL; }
2085 }
2086 
2087 void XMLNode::deleteAttribute(XMLAttribute *a){ if (a) deleteAttribute(a->lpszName); }
2088 void XMLNode::deleteAttribute(XMLCSTR lpszName)
2089 {
2090  int j=0;
2091  getAttribute(lpszName,&j);
2092  if (j) deleteAttribute(j-1);
2093 }
2094 
2095 XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,int i)
2096 {
2097  if (!d) { if (lpszNewValue) free(lpszNewValue); if (lpszNewName) free(lpszNewName); return NULL; }
2098  if (i>=d->nAttribute)
2099  {
2100  if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
2101  return NULL;
2102  }
2103  XMLAttribute *p=d->pAttribute+i;
2104  if (p->lpszValue&&p->lpszValue!=lpszNewValue) free((void*)p->lpszValue);
2105  p->lpszValue=lpszNewValue;
2106  if (lpszNewName&&p->lpszName!=lpszNewName) { free((void*)p->lpszName); p->lpszName=lpszNewName; };
2107  return p;
2108 }
2109 
2110 XMLAttribute *XMLNode::updateAttribute_WOSD(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
2111 {
2112  if (oldAttribute) return updateAttribute_WOSD((XMLSTR)newAttribute->lpszValue,(XMLSTR)newAttribute->lpszName,oldAttribute->lpszName);
2113  return addAttribute_WOSD((XMLSTR)newAttribute->lpszName,(XMLSTR)newAttribute->lpszValue);
2114 }
2115 
2116 XMLAttribute *XMLNode::updateAttribute_WOSD(XMLSTR lpszNewValue, XMLSTR lpszNewName,XMLCSTR lpszOldName)
2117 {
2118  int j=0;
2119  getAttribute(lpszOldName,&j);
2120  if (j) return updateAttribute_WOSD(lpszNewValue,lpszNewName,j-1);
2121  else
2122  {
2123  if (lpszNewName) return addAttribute_WOSD(lpszNewName,lpszNewValue);
2124  else return addAttribute_WOSD(stringDup(lpszOldName),lpszNewValue);
2125  }
2126 }
2127 
2128 int XMLNode::indexText(XMLCSTR lpszValue) const
2129 {
2130  if (!d) return -1;
2131  int i,l=d->nText;
2132  if (!lpszValue) { if (l) return 0; return -1; }
2133  XMLCSTR *p=d->pText;
2134  for (i=0; i<l; i++) if (lpszValue==p[i]) return i;
2135  return -1;
2136 }
2137 
2138 void XMLNode::deleteText(int i)
2139 {
2140  if ((!d)||(i<0)||(i>=d->nText)) return;
2141  d->nText--;
2142  XMLCSTR *p=d->pText+i;
2143  free((void*)*p);
2144  if (d->nText) memmove(p,p+1,(d->nText-i)*sizeof(XMLCSTR)); else { free(p); d->pText=NULL; }
2145  removeOrderElement(d,eNodeText,i);
2146 }
2147 
2148 void XMLNode::deleteText(XMLCSTR lpszValue) { deleteText(indexText(lpszValue)); }
2149 
2150 XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, int i)
2151 {
2152  if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; }
2153  if (i>=d->nText) return addText_WOSD(lpszNewValue);
2154  XMLCSTR *p=d->pText+i;
2155  if (*p!=lpszNewValue) { free((void*)*p); *p=lpszNewValue; }
2156  return lpszNewValue;
2157 }
2158 
2159 XMLCSTR XMLNode::updateText_WOSD(XMLSTR lpszNewValue, XMLCSTR lpszOldValue)
2160 {
2161  if (!d) { if (lpszNewValue) free(lpszNewValue); return NULL; }
2162  int i=indexText(lpszOldValue);
2163  if (i>=0) return updateText_WOSD(lpszNewValue,i);
2164  return addText_WOSD(lpszNewValue);
2165 }
2166 
2167 void XMLNode::deleteClear(int i)
2168 {
2169  if ((!d)||(i<0)||(i>=d->nClear)) return;
2170  d->nClear--;
2171  XMLClear *p=d->pClear+i;
2172  free((void*)p->lpszValue);
2173  if (d->nClear) memmove(p,p+1,(d->nClear-i)*sizeof(XMLClear)); else { free(p); d->pClear=NULL; }
2174  removeOrderElement(d,eNodeClear,i);
2175 }
2176 
2177 int XMLNode::indexClear(XMLCSTR lpszValue) const
2178 {
2179  if (!d) return -1;
2180  int i,l=d->nClear;
2181  if (!lpszValue) { if (l) return 0; return -1; }
2182  XMLClear *p=d->pClear;
2183  for (i=0; i<l; i++) if (lpszValue==p[i].lpszValue) return i;
2184  return -1;
2185 }
2186 
2187 void XMLNode::deleteClear(XMLCSTR lpszValue) { deleteClear(indexClear(lpszValue)); }
2188 void XMLNode::deleteClear(XMLClear *a) { if (a) deleteClear(a->lpszValue); }
2189 
2190 XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, int i)
2191 {
2192  if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; }
2193  if (i>=d->nClear) return addClear_WOSD(lpszNewContent);
2194  XMLClear *p=d->pClear+i;
2195  if (lpszNewContent!=p->lpszValue) { free((void*)p->lpszValue); p->lpszValue=lpszNewContent; }
2196  return p;
2197 }
2198 
2199 XMLClear *XMLNode::updateClear_WOSD(XMLSTR lpszNewContent, XMLCSTR lpszOldValue)
2200 {
2201  if (!d) { if (lpszNewContent) free(lpszNewContent); return NULL; }
2202  int i=indexClear(lpszOldValue);
2203  if (i>=0) return updateClear_WOSD(lpszNewContent,i);
2204  return addClear_WOSD(lpszNewContent);
2205 }
2206 
2207 XMLClear *XMLNode::updateClear_WOSD(XMLClear *newP,XMLClear *oldP)
2208 {
2209  if (oldP) return updateClear_WOSD((XMLSTR)newP->lpszValue,(XMLSTR)oldP->lpszValue);
2210  return NULL;
2211 }
2212 
2213 XMLNode& XMLNode::operator=( const XMLNode& A )
2214 {
2215  // shallow copy
2216  if (this != &A)
2217  {
2218  deleteNodeContent_priv(1,0);
2219  d=A.d;
2220  if (d) (d->ref_count) ++ ;
2221  }
2222  return *this;
2223 }
2224 
2225 XMLNode::XMLNode(const XMLNode &A)
2226 {
2227  // shallow copy
2228  d=A.d;
2229  if (d) (d->ref_count)++ ;
2230 }
2231 
2232 int XMLNode::nChildNode(XMLCSTR name) const
2233 {
2234  if (!d) return 0;
2235  int i,j=0,n=d->nChild;
2236  XMLNode *pc=d->pChild;
2237  for (i=0; i<n; i++)
2238  {
2239  if (xstricmp(pc->d->lpszName, name)==0) j++;
2240  pc++;
2241  }
2242  return j;
2243 }
2244 
2245 XMLNode XMLNode::getChildNode(XMLCSTR name, int *j) const
2246 {
2247  if (!d) return emptyXMLNode;
2248  int i=0,n=d->nChild;
2249  if (j) i=*j;
2250  XMLNode *pc=d->pChild+i;
2251  for (; i<n; i++)
2252  {
2253  if (xstricmp(pc->d->lpszName, name)==0)
2254  {
2255  if (j) *j=i+1;
2256  return *pc;
2257  }
2258  pc++;
2259  }
2260  return emptyXMLNode;
2261 }
2262 
2263 XMLNode XMLNode::getChildNode(XMLCSTR name, int j) const
2264 {
2265  if (!d) return emptyXMLNode;
2266  int i=0;
2267  while (j-->0) getChildNode(name,&i);
2268  return getChildNode(name,&i);
2269 }
2270 
2271 int XMLNode::positionOfText (int i) const { if (i>=d->nText ) i=d->nText-1; return findPosition(d,i,eNodeText ); }
2272 int XMLNode::positionOfClear (int i) const { if (i>=d->nClear) i=d->nClear-1; return findPosition(d,i,eNodeClear); }
2273 int XMLNode::positionOfChildNode(int i) const { if (i>=d->nChild) i=d->nChild-1; return findPosition(d,i,eNodeChild); }
2274 int XMLNode::positionOfText (XMLCSTR lpszValue) const { return positionOfText (indexText (lpszValue)); }
2275 int XMLNode::positionOfClear(XMLCSTR lpszValue) const { return positionOfClear(indexClear(lpszValue)); }
2276 int XMLNode::positionOfClear(XMLClear *a) const { if (a) return positionOfClear(a->lpszValue); return positionOfClear(); }
2277 int XMLNode::positionOfChildNode(XMLNode x) const
2278 {
2279  if ((!d)||(!x.d)) return -1;
2280  XMLNodeData *dd=x.d;
2281  XMLNode *pc=d->pChild;
2282  int i=d->nChild;
2283  while (i--) if (pc[i].d==dd) return findPosition(d,i,eNodeChild);
2284  return -1;
2285 }
2286 int XMLNode::positionOfChildNode(XMLCSTR name, int count) const
2287 {
2288  if (!name) return positionOfChildNode(count);
2289  int j=0;
2290  do { getChildNode(name,&j); if (j<0) return -1; } while (count--);
2291  return findPosition(d,j-1,eNodeChild);
2292 }
2293 
2294 XMLNode XMLNode::getChildNodeWithAttribute(XMLCSTR name,XMLCSTR attributeName,XMLCSTR attributeValue, int *k) const
2295 {
2296  int i=0,j;
2297  if (k) i=*k;
2298  XMLNode x;
2299  XMLCSTR t;
2300  do
2301  {
2302  x=getChildNode(name,&i);
2303  if (!x.isEmpty())
2304  {
2305  if (attributeValue)
2306  {
2307  j=0;
2308  do
2309  {
2310  t=x.getAttribute(attributeName,&j);
2311  if (t&&(xstricmp(attributeValue,t)==0)) { if (k) *k=i+1; return x; }
2312  } while (t);
2313  } else
2314  {
2315  if (x.isAttributeSet(attributeName)) { if (k) *k=i+1; return x; }
2316  }
2317  }
2318  } while (!x.isEmpty());
2319  return emptyXMLNode;
2320 }
2321 
2322 // Find an attribute on an node.
2323 XMLCSTR XMLNode::getAttribute(XMLCSTR lpszAttrib, int *j) const
2324 {
2325  if (!d) return NULL;
2326  int i=0,n=d->nAttribute;
2327  if (j) i=*j;
2328  XMLAttribute *pAttr=d->pAttribute+i;
2329  for (; i<n; i++)
2330  {
2331  if (xstricmp(pAttr->lpszName, lpszAttrib)==0)
2332  {
2333  if (j) *j=i+1;
2334  return pAttr->lpszValue;
2335  }
2336  pAttr++;
2337  }
2338  return NULL;
2339 }
2340 
2341 char XMLNode::isAttributeSet(XMLCSTR lpszAttrib) const
2342 {
2343  if (!d) return FALSE;
2344  int i,n=d->nAttribute;
2345  XMLAttribute *pAttr=d->pAttribute;
2346  for (i=0; i<n; i++)
2347  {
2348  if (xstricmp(pAttr->lpszName, lpszAttrib)==0)
2349  {
2350  return TRUE;
2351  }
2352  pAttr++;
2353  }
2354  return FALSE;
2355 }
2356 
2357 XMLCSTR XMLNode::getAttribute(XMLCSTR name, int j) const
2358 {
2359  if (!d) return NULL;
2360  int i=0;
2361  while (j-->0) getAttribute(name,&i);
2362  return getAttribute(name,&i);
2363 }
2364 
2365 XMLNodeContents XMLNode::enumContents(int i) const
2366 {
2367  XMLNodeContents c;
2368  if (!d) { c.type=eNodeNULL; return c; }
2369  if (i<d->nAttribute)
2370  {
2371  c.type=eNodeAttribute;
2372  c.attrib=d->pAttribute[i];
2373  return c;
2374  }
2375  i-=d->nAttribute;
2376  c.type=(XMLElementType)(d->pOrder[i]&3);
2377  i=(d->pOrder[i])>>2;
2378  switch (c.type)
2379  {
2380  case eNodeChild: c.child = d->pChild[i]; break;
2381  case eNodeText: c.text = d->pText[i]; break;
2382  case eNodeClear: c.clear = d->pClear[i]; break;
2383  default: break;
2384  }
2385  return c;
2386 }
2387 
2388 XMLCSTR XMLNode::getName() const { if (!d) return NULL; return d->lpszName; }
2389 int XMLNode::nText() const { if (!d) return 0; return d->nText; }
2390 int XMLNode::nChildNode() const { if (!d) return 0; return d->nChild; }
2391 int XMLNode::nAttribute() const { if (!d) return 0; return d->nAttribute; }
2392 int XMLNode::nClear() const { if (!d) return 0; return d->nClear; }
2393 int XMLNode::nElement() const { if (!d) return 0; return d->nAttribute+d->nChild+d->nText+d->nClear; }
2394 XMLClear XMLNode::getClear (int i) const { if ((!d)||(i>=d->nClear )) return emptyXMLClear; return d->pClear[i]; }
2395 XMLAttribute XMLNode::getAttribute (int i) const { if ((!d)||(i>=d->nAttribute)) return emptyXMLAttribute; return d->pAttribute[i]; }
2396 XMLCSTR XMLNode::getAttributeName (int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszName; }
2397 XMLCSTR XMLNode::getAttributeValue(int i) const { if ((!d)||(i>=d->nAttribute)) return NULL; return d->pAttribute[i].lpszValue; }
2398 XMLCSTR XMLNode::getText (int i) const { if ((!d)||(i>=d->nText )) return NULL; return d->pText[i]; }
2399 XMLNode XMLNode::getChildNode (int i) const { if ((!d)||(i>=d->nChild )) return emptyXMLNode; return d->pChild[i]; }
2400 XMLNode XMLNode::getParentNode ( ) const { if ((!d)||(!d->pParent )) return emptyXMLNode; return XMLNode(d->pParent); }
2401 char XMLNode::isDeclaration ( ) const { if (!d) return 0; return d->isDeclaration; }
2402 char XMLNode::isEmpty ( ) const { return (d==NULL); }
2403 XMLNode XMLNode::emptyNode ( ) { return XMLNode::emptyXMLNode; }
2404 
2405 XMLNode XMLNode::addChild(XMLCSTR lpszName, char isDeclaration, int pos)
2406  { return addChild_priv(0,stringDup(lpszName),isDeclaration,pos); }
2407 XMLNode XMLNode::addChild_WOSD(XMLSTR lpszName, char isDeclaration, int pos)
2408  { return addChild_priv(0,lpszName,isDeclaration,pos); }
2409 XMLAttribute *XMLNode::addAttribute(XMLCSTR lpszName, XMLCSTR lpszValue)
2410  { return addAttribute_priv(0,stringDup(lpszName),stringDup(lpszValue)); }
2411 XMLAttribute *XMLNode::addAttribute_WOSD(XMLSTR lpszName, XMLSTR lpszValuev)
2412  { return addAttribute_priv(0,lpszName,lpszValuev); }
2413 XMLCSTR XMLNode::addText(XMLCSTR lpszValue, int pos)
2414  { return addText_priv(0,stringDup(lpszValue),pos); }
2415 XMLCSTR XMLNode::addText_WOSD(XMLSTR lpszValue, int pos)
2416  { return addText_priv(0,lpszValue,pos); }
2417 XMLClear *XMLNode::addClear(XMLCSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
2418  { return addClear_priv(0,stringDup(lpszValue),lpszOpen,lpszClose,pos); }
2419 XMLClear *XMLNode::addClear_WOSD(XMLSTR lpszValue, XMLCSTR lpszOpen, XMLCSTR lpszClose, int pos)
2420  { return addClear_priv(0,lpszValue,lpszOpen,lpszClose,pos); }
2421 XMLCSTR XMLNode::updateName(XMLCSTR lpszName)
2422  { return updateName_WOSD(stringDup(lpszName)); }
2423 XMLAttribute *XMLNode::updateAttribute(XMLAttribute *newAttribute, XMLAttribute *oldAttribute)
2424  { return updateAttribute_WOSD(stringDup(newAttribute->lpszValue),stringDup(newAttribute->lpszName),oldAttribute->lpszName); }
2425 XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,int i)
2426  { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),i); }
2427 XMLAttribute *XMLNode::updateAttribute(XMLCSTR lpszNewValue, XMLCSTR lpszNewName,XMLCSTR lpszOldName)
2428  { return updateAttribute_WOSD(stringDup(lpszNewValue),stringDup(lpszNewName),lpszOldName); }
2429 XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, int i)
2430  { return updateText_WOSD(stringDup(lpszNewValue),i); }
2431 XMLCSTR XMLNode::updateText(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2432  { return updateText_WOSD(stringDup(lpszNewValue),lpszOldValue); }
2433 XMLClear *XMLNode::updateClear(XMLCSTR lpszNewContent, int i)
2434  { return updateClear_WOSD(stringDup(lpszNewContent),i); }
2435 XMLClear *XMLNode::updateClear(XMLCSTR lpszNewValue, XMLCSTR lpszOldValue)
2436  { return updateClear_WOSD(stringDup(lpszNewValue),lpszOldValue); }
2437 XMLClear *XMLNode::updateClear(XMLClear *newP,XMLClear *oldP)
2438  { return updateClear_WOSD(stringDup(newP->lpszValue),oldP->lpszValue); }
2439 
2440 char XMLNode::setGlobalOptions(XMLCharEncoding _characterEncoding, char _guessWideCharChars, char _dropWhiteSpace)
2441 {
2442  guessWideCharChars=_guessWideCharChars; dropWhiteSpace=_dropWhiteSpace;
2443 #ifdef _XMLWIDECHAR
2444  if (_characterEncoding) characterEncoding=_characterEncoding;
2445 #else
2446  switch(_characterEncoding)
2447  {
2448  case encoding_UTF8: characterEncoding=_characterEncoding; XML_ByteTable=XML_utf8ByteTable; break;
2449  case encoding_ascii: characterEncoding=_characterEncoding; XML_ByteTable=XML_asciiByteTable; break;
2450  case encoding_ShiftJIS: characterEncoding=_characterEncoding; XML_ByteTable=XML_sjisByteTable; break;
2451  default: return 1;
2452  }
2453 #endif
2454  return 0;
2455 }
2456 
2457 XMLNode::XMLCharEncoding XMLNode::guessCharEncoding(void *buf,int l, char useXMLEncodingAttribute)
2458 {
2459 #ifdef _XMLWIDECHAR
2460  return (XMLCharEncoding)0;
2461 #else
2462  if (l<25) return (XMLCharEncoding)0;
2463  if (guessWideCharChars&&(myIsTextWideChar(buf,l))) return (XMLCharEncoding)0;
2464  unsigned char *b=(unsigned char*)buf;
2465  if ((b[0]==0xef)&&(b[1]==0xbb)&&(b[2]==0xbf)) return encoding_UTF8;
2466 
2467  // Match utf-8 model ?
2468  XMLCharEncoding bestGuess=encoding_UTF8;
2469  int i=0;
2470  while (i<l)
2471  switch (XML_utf8ByteTable[b[i]])
2472  {
2473  case 4: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=encoding_ascii; i=l; } // 10bbbbbb ?
2474  case 3: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=encoding_ascii; i=l; } // 10bbbbbb ?
2475  case 2: i++; if ((i<l)&&(b[i]& 0xC0)!=0x80) { bestGuess=encoding_ascii; i=l; } // 10bbbbbb ?
2476  case 1: i++; break;
2477  case 0: i=l;
2478  }
2479  if (!useXMLEncodingAttribute) return bestGuess;
2480  // if encoding is specified and different from utf-8 than it's non-utf8
2481  // otherwise it's utf-8
2482  char bb[201];
2483  l=mmin(l,200);
2484  memcpy(bb,buf,l); // copy buf into bb to be able to do "bb[l]=0"
2485  bb[l]=0;
2486  b=(unsigned char*)strstr(bb,"encoding");
2487  if (!b) return bestGuess;
2488  b+=8; while XML_isSPACECHAR(*b) b++; if (*b!='=') return bestGuess;
2489  b++; while XML_isSPACECHAR(*b) b++; if ((*b!='\'')&&(*b!='"')) return bestGuess;
2490  b++; while XML_isSPACECHAR(*b) b++;
2491 
2492  if ((_strnicmp((char*)b,"utf-8",5)==0)||
2493  (_strnicmp((char*)b,"utf8",4)==0))
2494  {
2495  if (bestGuess==encoding_ascii) return (XMLCharEncoding)0;
2496  return encoding_UTF8;
2497  }
2498 
2499  if ((_strnicmp((char*)b,"shiftjis",8)==0)||
2500  (_strnicmp((char*)b,"shift-jis",9)==0)||
2501  (_strnicmp((char*)b,"sjis",4)==0)) return encoding_ShiftJIS;
2502 
2503  return encoding_ascii;
2504 #endif
2505 }
2506 #undef XML_isSPACECHAR
2507 
2509 // Here starts the base64 conversion functions. //
2511 
2512 static const char base64Fillchar = _X('='); // used to mark partial words at the end
2513 
2514 // this lookup table defines the base64 encoding
2515 XMLCSTR base64EncodeTable=_X("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
2516 
2517 // Decode Table gives the index of any valid base64 character in the Base64 table]
2518 // 96: '=' - 97: space char - 98: illegal char - 99: end of string
2519 const unsigned char base64DecodeTable[] = {
2520  99,98,98,98,98,98,98,98,98,97, 97,98,98,97,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //00 -29
2521  98,98,97,98,98,98,98,98,98,98, 98,98,98,62,98,98,98,63,52,53, 54,55,56,57,58,59,60,61,98,98, //30 -59
2522  98,96,98,98,98, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24, //60 -89
2523  25,98,98,98,98,98,98,26,27,28, 29,30,31,32,33,34,35,36,37,38, 39,40,41,42,43,44,45,46,47,48, //90 -119
2524  49,50,51,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //120 -149
2525  98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //150 -179
2526  98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //180 -209
2527  98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98,98,98,98,98, //210 -239
2528  98,98,98,98,98,98,98,98,98,98, 98,98,98,98,98,98 //240 -255
2529 };
2530 
2531 XMLParserBase64Tool::~XMLParserBase64Tool(){ freeBuffer(); }
2532 
2533 void XMLParserBase64Tool::freeBuffer(){ if (buf) free(buf); buf=NULL; buflen=0; }
2534 
2535 int XMLParserBase64Tool::encodeLength(int inlen, char formatted)
2536 {
2537  unsigned int i=((inlen-1)/3*4+4+1);
2538  if (formatted) i+=inlen/54;
2539  return i;
2540 }
2541 
2542 XMLSTR XMLParserBase64Tool::encode(unsigned char *inbuf, unsigned int inlen, char formatted)
2543 {
2544  int i=encodeLength(inlen,formatted),k=17,eLen=inlen/3,j;
2545  alloc(i*sizeof(XMLCHAR));
2546  XMLSTR curr=(XMLSTR)buf;
2547  for(i=0;i<eLen;i++)
2548  {
2549  // Copy next three bytes into lower 24 bits of int, paying attention to sign.
2550  j=(inbuf[0]<<16)|(inbuf[1]<<8)|inbuf[2]; inbuf+=3;
2551  // Encode the int into four chars
2552  *(curr++)=base64EncodeTable[ j>>18 ];
2553  *(curr++)=base64EncodeTable[(j>>12)&0x3f];
2554  *(curr++)=base64EncodeTable[(j>> 6)&0x3f];
2555  *(curr++)=base64EncodeTable[(j )&0x3f];
2556  if (formatted) { if (!k) { *(curr++)=_X('\n'); k=18; } k--; }
2557  }
2558  eLen=inlen-eLen*3; // 0 - 2.
2559  if (eLen==1)
2560  {
2561  *(curr++)=base64EncodeTable[ inbuf[0]>>2 ];
2562  *(curr++)=base64EncodeTable[(inbuf[0]<<4)&0x3F];
2563  *(curr++)=base64Fillchar;
2564  *(curr++)=base64Fillchar;
2565  } else if (eLen==2)
2566  {
2567  j=(inbuf[0]<<8)|inbuf[1];
2568  *(curr++)=base64EncodeTable[ j>>10 ];
2569  *(curr++)=base64EncodeTable[(j>> 4)&0x3f];
2570  *(curr++)=base64EncodeTable[(j<< 2)&0x3f];
2571  *(curr++)=base64Fillchar;
2572  }
2573  *(curr++)=0;
2574  return (XMLSTR)buf;
2575 }
2576 
2577 unsigned int XMLParserBase64Tool::decodeSize(XMLCSTR data,XMLError *xe)
2578 {
2579  if (xe) *xe=eXMLErrorNone;
2580  int size=0;
2581  unsigned char c;
2582  //skip any extra characters (e.g. newlines or spaces)
2583  while (*data)
2584  {
2585 #ifdef _XMLWIDECHAR
2586  if (*data>255) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2587 #endif
2588  c=base64DecodeTable[(unsigned char)(*data)];
2589  if (c<97) size++;
2590  else if (c==98) { if (xe) *xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2591  data++;
2592  }
2593  if (xe&&(size%4!=0)) *xe=eXMLErrorBase64DataSizeIsNotMultipleOf4;
2594  if (size==0) return 0;
2595  do { data--; size--; } while(*data==base64Fillchar); size++;
2596  return (unsigned int)((size*3)/4);
2597 }
2598 
2599 unsigned char XMLParserBase64Tool::decode(XMLCSTR data, unsigned char *buf, int len, XMLError *xe)
2600 {
2601  if (xe) *xe=eXMLErrorNone;
2602  int i=0,p=0;
2603  unsigned char d,c;
2604  for(;;)
2605  {
2606 
2607 #ifdef _XMLWIDECHAR
2608 #define BASE64DECODE_READ_NEXT_CHAR(c) \
2609  do { \
2610  if (data[i]>255){ c=98; break; } \
2611  c=base64DecodeTable[(unsigned char)data[i++]]; \
2612  }while (c==97); \
2613  if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2614 #else
2615 #define BASE64DECODE_READ_NEXT_CHAR(c) \
2616  do { c=base64DecodeTable[(unsigned char)data[i++]]; }while (c==97); \
2617  if(c==98){ if(xe)*xe=eXMLErrorBase64DecodeIllegalCharacter; return 0; }
2618 #endif
2619 
2620  BASE64DECODE_READ_NEXT_CHAR(c)
2621  if (c==99) { return 2; }
2622  if (c==96)
2623  {
2624  if (p==(int)len) return 2;
2625  if (xe) *xe=eXMLErrorBase64DecodeTruncatedData;
2626  return 1;
2627  }
2628 
2629  BASE64DECODE_READ_NEXT_CHAR(d)
2630  if ((d==99)||(d==96)) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
2631  if (p==(int)len) { if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall; return 0; }
2632  buf[p++]=(unsigned char)((c<<2)|((d>>4)&0x3));
2633 
2634  BASE64DECODE_READ_NEXT_CHAR(c)
2635  if (c==99) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
2636  if (p==(int)len)
2637  {
2638  if (c==96) return 2;
2639  if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
2640  return 0;
2641  }
2642  if (c==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
2643  buf[p++]=(unsigned char)(((d<<4)&0xf0)|((c>>2)&0xf));
2644 
2645  BASE64DECODE_READ_NEXT_CHAR(d)
2646  if (d==99 ) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
2647  if (p==(int)len)
2648  {
2649  if (d==96) return 2;
2650  if (xe) *xe=eXMLErrorBase64DecodeBufferTooSmall;
2651  return 0;
2652  }
2653  if (d==96) { if (xe) *xe=eXMLErrorBase64DecodeTruncatedData; return 1; }
2654  buf[p++]=(unsigned char)(((c<<6)&0xc0)|d);
2655  }
2656 }
2657 #undef BASE64DECODE_READ_NEXT_CHAR
2658 
2659 void XMLParserBase64Tool::alloc(int newsize)
2660 {
2661  if ((!buf)&&(newsize)) { buf=malloc(newsize); buflen=newsize; return; }
2662  if (newsize>buflen) { buf=realloc(buf,newsize); buflen=newsize; }
2663 }
2664 
2665 unsigned char *XMLParserBase64Tool::decode(XMLCSTR data, int *outlen, XMLError *xe)
2666 {
2667  if (xe) *xe=eXMLErrorNone;
2668  unsigned int len=decodeSize(data,xe);
2669  if (outlen) *outlen=len;
2670  if (!len) return NULL;
2671  alloc(len+1);
2672  if(!decode(data,(unsigned char*)buf,len,xe)){ return NULL; }
2673  return (unsigned char*)buf;
2674 }
2675 
2676 }
core::XMLCharacterEntity
Definition: xml_parser.cpp:124
core::XML
Definition: xml_parser.cpp:478
core::ALLXMLClearTag
Definition: xml_parser.cpp:109
core::NextToken
Definition: xml_parser.cpp:491