xml.c

Go to the documentation of this file.
00001 
00007 #include <stdio.h>
00008 #include "ctl/ctldef.h"
00009 #include "ctl/debug.h"
00010 #include "ctl/xml.h"
00011 
00012 #ifndef CTL_UNIT
00013 
00014 
00015 static void DefaultXMLException( const ctl_xmlread* xml, const char* reason, const char* szP0, const char* szP1 )
00016 {
00017     assertobjconst(xml);
00018     assertconst(reason,1);
00019     LOG( LOG_ERROR, szconst(char,"%s: %.32s %.32s\n"), reason, szP0?szP0:"NULL", szP1?szP1:"" );
00020     assert( !isdebugging() );
00021 }
00022 
00023 
00024 /****************************************************************************
00025 
00026 XML writing functions
00027 
00028 *****************************************************************************/
00029 
00030 static void xml_puts( ctl_xmlwrite* self, const char* str )
00031 {
00032     assertobjptr(self);
00033     assertconst(str,1);
00034     if( NULL != self->buff )
00035     {
00036         ctl_gstring_strcat(char, self->buff, str );
00037     }
00038     else if( NULL != self->fp )
00039     {
00040         cfputs(char)( str, self->fp );
00041     }
00042     else
00043     {
00044         throwassert("No file or buffer");
00045     }
00046 }
00047 static void xml_printf( ctl_xmlwrite* self, const char* fmt, ... )
00048 {
00049     va_list args;
00050     assertobjptr(self);
00051     assertconst(fmt,1);
00052     va_start( args, fmt );
00053     if( NULL != self->buff )
00054     {
00055         ctl_gstring_vsprintf( char, self->buff, fmt, args );
00056     }
00057     else if( NULL != self->fp )
00058     {
00059         cvfprintf(char)( self->fp, fmt, args );
00060     }
00061     else
00062     {
00063         throwassert("No file or buffer");
00064     }
00065 }
00066 
00073 bool ctl_xmlwrite_begin( ctl_xmlwrite* self, ctl_gstring(char)* stringbuff )
00074 {
00075     assertobjptr(self);
00076     assertobjptr( stringbuff );
00077     self->indent = 0;
00078     self->pretty = true;
00079     self->buff = stringbuff;
00080     self->fp = NULL;
00081     return true;
00082 }
00083 
00090 bool ctl_xmlwrite_create( ctl_xmlwrite* self, const char* szPath )
00091 {
00092     assertobjptr(self);
00093     assertconst(szPath,1);
00094     self->indent = 0;
00095     self->pretty = true;
00096     self->buff = NULL;
00097     self->fp = fopen( szPath, "wb" );
00098     if( NULL != self->fp )
00099     {
00100         ctl_xmlwrite_addheader( self );
00101         return true;
00102     }
00103     return false;
00104 }
00105 
00111 void ctl_xmlwrite_close( ctl_xmlwrite* self )
00112 {
00113     assertobjptr(self);
00114     if( NULL != self->fp )
00115     {
00116         fclose( self->fp );
00117         self->fp = NULL;
00118     }
00119 }
00120 
00126 void ctl_xmlwrite_addheader( ctl_xmlwrite* self )
00127 {
00128     xml_puts( self, szconst(char,"<?xml version=\"1.0\" ?>") );
00129 }
00130 
00136 void ctl_xmlwrite_newline( ctl_xmlwrite* self )
00137 {
00138     assertobjptr(self);
00139     if( self->pretty )
00140     {
00141         xml_printf( self, szconst(char,"\n%*s"), self->indent,szconst(char,"") );
00142     }
00143 }
00144 
00150 void ctl_xmlwrite_indent( ctl_xmlwrite* self )
00151 {
00152     assertobjptr(self);
00153     self->indent += XML_TAB_DEPTH;
00154 }
00155 
00161 void ctl_xmlwrite_outdent( ctl_xmlwrite* self )
00162 {
00163     assertobjptr(self);
00164     self->indent -= XML_TAB_DEPTH;
00165     ctl_xmlwrite_newline( self );
00166 }
00167 
00175 void ctl_xmlwrite_generaltag( ctl_xmlwrite* self, const char* type, const char* label )
00176 {
00177     assertobjptr(self);
00178     assertconst(type,1);
00179     assertconst(label,1);
00180     ctl_xmlwrite_newline( self );
00181     xml_printf( self, szconst(char,"<%s ") CTL_LABEL_TYPE szconst(char,"=\"%s\">"), label, type );
00182 }
00183 
00192 void ctl_xmlwrite_containertag( ctl_xmlwrite* self, const char* type, size_t count, const char* label )
00193 {
00194     assertobjptr(self);
00195     assertconst(type,1);
00196     assertconst(label,1);
00197     ctl_xmlwrite_newline( self );
00198     xml_printf( self, szconst(char,"<%s ") CTL_LABEL_TYPE szconst(char,"=\"container\" ") CTL_LABEL_MBRTYPE szconst(char,"=\"%s\" ") CTL_LABEL_COUNT szconst(char,"=\"%d\">"), label, type, count );
00199 }
00200 
00208 void ctl_xmlwrite_endtag( ctl_xmlwrite* self, const char* type, const char* label )
00209 {
00210     assertobjptr(self);
00211     assertconst(type,1);
00212     assertconst(label,1);
00213     xml_printf( self, szconst(char,"</%s>"), label );
00214 
00215 }
00216 
00221 void ctl_xmlwrite_string_char( ctl_xmlwrite* self, const char* begin, const char* end )
00222 {
00223     assertobjptr(self);
00224     assertconst(begin,1);
00225     if( NULL == end )
00226         end = begin + cstrlen(char)(begin);
00227     assert( begin && end >= begin );
00228     {
00229         ctl_gstring(char)   tmp;
00230         const char* psz = begin;
00231         bool bigger = false;
00232         ctl_gstring_init(char, &tmp );
00233         while ( psz < end )
00234         {
00235             switch ( *psz )
00236             {
00237             case chconst(char,'<'): 
00238                 ctl_gstring_strcat(char, &tmp, szconst(char,"&lt;") );
00239                 break;
00240             case chconst(char,'>'): 
00241                 ctl_gstring_strcat(char, &tmp, szconst(char,"&gt;") );
00242                 break;
00243             case chconst(char,'\"'): 
00244                 ctl_gstring_strcat(char, &tmp, szconst(char,"&quot;") );
00245                 break;
00246             case chconst(char,'\''): 
00247                 ctl_gstring_strcat(char, &tmp, szconst(char,"&apos;") );
00248                 break;
00249             case chconst(char,'&'): 
00250                 ctl_gstring_strcat(char, &tmp, szconst(char,"&amp;") );
00251                 break;
00252             default:
00253                 ctl_gstring_add(char, &tmp, *psz );
00254                 break;
00255             }
00256             psz++;
00257         }
00258         /* If we didn't find any characters that needed to be escaped to fit, and string is bigger than making it a <![CDATA[...]]>, and string doesn't contain elements of CDATA, make it into a CDATA */
00259         {
00260             char* poisonCDATA = cstrstr(char)( begin, szconst(char,"]]>") );
00261             if ( !bigger && ctl_gstring_size(char, &tmp ) > (size_t)((end-begin)+12) && !poisonCDATA && poisonCDATA < end )
00262             {
00263                 /* Append string to file as CDATA */
00264                 xml_printf( self, szconst(char,"<![CDATA[%.*s]]>"), (end-begin), begin );
00265             }
00266             else
00267             {
00268                 /* Append escaped string to file */
00269                 xml_puts( self, tmp.begin );
00270             }
00271         }
00272         ctl_gstring_destroy(char, &tmp );
00273     }
00274 }
00275 
00280 void ctl_xmlwrite_string_wchar_t( ctl_xmlwrite* self, const wchar_t* begin, const wchar_t* end )
00281 {
00282     assertobjptr(self);
00283     assertconst(begin,1);
00284     if( NULL == end )
00285         end = begin + cstrlen(wchar_t)(begin);
00286     assert( begin && end >= begin );
00287     {
00288         ctl_gstring(char)   tmp;
00289         const wchar_t* psz = begin;
00290         bool bigger = false;
00291         ctl_gstring_init(char, &tmp );
00292         while ( psz < end )
00293         {
00294             switch ( *psz )
00295             {
00296             case chconst(wchar_t,'<'): 
00297                 ctl_gstring_strcat(char, &tmp, szconst(char,"&lt;") );
00298                 break;
00299             case chconst(wchar_t,'>'): 
00300                 ctl_gstring_strcat(char, &tmp, szconst(char,"&gt;") );
00301                 break;
00302             case chconst(wchar_t,'\"'): 
00303                 ctl_gstring_strcat(char, &tmp, szconst(char,"&quot;") );
00304                 break;
00305             case chconst(wchar_t,'\''): 
00306                 ctl_gstring_strcat(char, &tmp, szconst(char,"&apos;") );
00307                 break;
00308             case chconst(wchar_t,'&'): 
00309                 ctl_gstring_strcat(char, &tmp, szconst(char,"&amp;") );
00310                 break;
00311             default:
00312                 if ( *((uint8*)psz) > 127 )
00313                 {
00314                     /* Translate wide characters out of range of my ASCII XML handling */
00315                     ctl_gstring_strcat(char, &tmp, szconst(char,"&#x") );
00316                     ctl_gstring_putxint32(char, &tmp, (unsigned)*psz );
00317                     ctl_gstring_strcat(char, &tmp, szconst(char,";") );
00318                     bigger = true;
00319                 }
00320                 else
00321                 {
00322                     /* No translation needed */
00323                     ctl_gstring_add(char, &tmp, (char)*psz );
00324                 }
00325                 break;
00326             }
00327             psz++;
00328         }
00329         /* Append escaped string to file */
00330         xml_puts( self, tmp.begin );
00331         ctl_gstring_destroy(char, &tmp );
00332     }
00333 }
00334 
00335 /****************************************************************************
00336 
00337 XML reading functions
00338 
00339 *****************************************************************************/
00340 
00341 /* Forward declare some little text munching helpers*/
00342 static const char* SkipSpace( const char* text, const char* szEnd );
00343 static const char* FindSpace( const char* text, const char* szEnd );
00344 static const char* FindChar( const char* text, const char* szEnd, char ch );
00345 static const char* FindGT( const char* text, const char* szEnd );
00346 static const char* FindLT( const char* text, const char* szEnd );
00347 
00348 
00349 /* Find the ending quote of the same type */
00350 static const char* MatchQuote( const ctl_xmlread* self, const char* sz, const char* szEnd )
00351 {
00352     assertobjconst(self);
00353     assertconst(sz,1);
00354     assert( szEnd >= sz );
00355     {
00356         char matchMe = *sz;
00357         if ( matchMe != chconst(char,'\'') && matchMe != chconst(char,'\"') )
00358         {
00359             self->except(self,szconst(char,"Bad XML: Expected a quote here"), sz, NULL );
00360             return NULL;
00361         }
00362         {
00363             const char* found = FindChar( sz+1, szEnd, matchMe );
00364             if ( NULL == found )
00365             {
00366                 self->except(self,szconst(char,"Bad XML: Missing closing quote"), sz, NULL );
00367                 return NULL;
00368             }
00369             return found;
00370         }
00371     }
00372 }
00373 
00374 /* Pop pointer up to end of white space */
00375 static const char* SkipSpace( const char* sz, const char* szEnd )
00376 {
00377     assertconst(sz,1);
00378     assert( szEnd >= sz );
00379     {
00380         const char* p = sz;
00381         while ( cisspace(char)(*p) && p < szEnd )
00382             p++;
00383         return p;
00384     }
00385 }
00386 
00387 /* Pop pointer up to white space */
00388 static const char* FindSpace( const char* sz, const char* szEnd )
00389 {
00390     assertconst(sz,1);
00391     assert( szEnd >= sz );
00392     {
00393         const char* p = sz;
00394         while ( !cisspace(char)(*p) && p < szEnd )
00395             p++;
00396         return p;
00397     }
00398 }
00399 /* Pop pointer up to end of white space */
00400 static const char* FindChar( const char* sz, const char* szEnd, char ch )
00401 {
00402     assertconst(sz,1);
00403     assert( szEnd >= sz );
00404     while ( sz < szEnd )
00405     {
00406         if ( *sz == ch )
00407             return sz;
00408         sz++;
00409     }
00410     return NULL;
00411 }
00412 
00413 /* Find a '>', typically on the end of a tag */
00414 static const char* FindGT( const char* sz, const char* szEnd )
00415 {
00416     /* If there are embedded quotes, keep track of them, ignoring '>' or '<' */
00417     bool bQuote = false;
00418     assertconst(sz,1);
00419     assert( szEnd >= sz );
00420     while ( sz < szEnd )
00421     {
00422         if ( *sz == chconst(char,'\"') )
00423             bQuote = !bQuote;
00424         else if ( *sz == chconst(char,'>') && !bQuote )
00425             return sz;
00426         sz++;
00427     }
00428     return NULL;
00429 }
00430 
00431 /* Find a '<', typically on the end of a tag */
00432 static const char* FindLT( const char* sz, const char* szEnd )
00433 {
00434     assertconst(sz,1);
00435     assert( szEnd >= sz );
00436     while ( sz < szEnd )
00437     {
00438         if ( *sz == '<' )
00439             return sz;
00440         sz++;
00441     }
00442     return NULL;
00443 }
00444 
00445 
00454 static const char* MatchTag( ctl_xmlread* self, const char* text )
00455 {
00456     /* Recursively scan to find tags*/
00457     assertobjptr(self);
00458     assert( text );
00459     {
00460         const char* next = FindLT( text+1, self->szEnd );
00461         while ( next )
00462         {
00463             /* Eat the whole set of warts that begin with an '!'*/
00464             if ( next[1] == '!' )
00465             {
00466                 /* If we encounter <![CDATA[ ... ]]>, skip it*/
00467                 if ( !cstrncmp(char)( text, szconst(char,"<![CDATA["), 9 ) )
00468                 {
00469                     const char* szGT = cstrstr(char)(text, szconst(char,"]]>"));
00470                     if ( szGT && szGT < self->szEnd )
00471                     {
00472                         /* Look for next thing*/
00473                         next = FindLT( szGT+3, self->szEnd );
00474                     }
00475                     else
00476                     {
00477                         self->except( self,szconst(char,"Bad XML: <![CDATA[ doesn't have a ']]>'"), self->szpCurr, NULL );
00478                         return NULL;
00479                     }
00480                 }
00481                 else if ( !cstrncmp(char)( next, szconst(char,"<!--"), 4 ) )
00482                 {
00483                     const char* pGT = cstrstr(char)(next+4, szconst(char,"-->"));
00484                     if ( pGT && pGT < self->szEnd )
00485                     {
00486                         next = FindLT( pGT+3, self->szEnd );
00487                         continue;
00488                     }
00489                     else
00490                     {
00491                         self->except( self,szconst(char,"Bad XML: '<!--' comment doesn't have a '-->'"), self->szpCurr, pGT );
00492                         return NULL;
00493                     }
00494                 }
00495                 else
00496                 {
00497                     /* Found something else - eat it.*/
00498                     const char* pGT = FindGT( next+1, self->szEnd );
00499                     if ( pGT )
00500                     {
00501                         next = FindLT( pGT+1, self->szEnd );
00502                         continue;
00503                     }
00504                     else
00505                     {
00506                         self->except( self,szconst(char,"Bad XML: '<!' data doesn't have a '>'"), text, pGT );
00507                         return NULL;
00508                     }
00509                 }
00510             }
00511             else if ( next[1] == chconst(char,'/') )
00512             {
00513                 /* Due to recursive nature of search, this MUST be the match, else malformed.*/
00514                 const char* p1 = text + 1;
00515                 const char* p2 = next + 2;
00516                 while ( *p2 != chconst(char,'>') )
00517                     if ( *p1++ != *p2++ )
00518                     {
00519                         self->except( self,szconst(char,"Bad XML: Mismatched tags"), text, next );
00520                         return NULL;
00521                     }
00522                 /* Return start of matching </tag>*/
00523                 return next;
00524             }
00525             else
00526             {
00527                 const char* pGT = FindGT( next+1, self->szEnd );
00528                 if ( pGT && *(pGT-1) == chconst(char,'/') )
00529                 {
00530                     /* Tag is single-entry shorthand*/
00531                     next = FindLT( pGT+1, self->szEnd );
00532                     self->bRecursible = true;
00533                 }
00534                 else
00535                 {
00536                     /* Recurse to find end of embedded <tag>*/
00537                     const char* pNext;
00538                     self->bRecursible = true;
00539                     pNext = MatchTag( self, next );
00540                     if ( NULL == pNext )
00541                     {
00542                         self->except( self,szconst(char,"Bad XML: Mismatched tags"), text, pNext );
00543                         return NULL;
00544                     }
00545                     next = FindLT( pNext+1, self->szEnd );
00546                 }
00547             }
00548         }
00549     }
00550     return NULL;
00551 }
00552 
00561 const char* ctl_xmlread_translate_char( const ctl_xmlread* self, char* begin, const char* end, bool bEatWhite )
00562 {
00563     assertobjconst(self);
00564     assertptr( begin,end-begin );
00565     assert( end >= begin );
00566     {
00567         const char* text = begin;
00568         char* outtext = begin;
00569         /* Gnaw off leading spaces (note: not good for mixed/embedded text)*/
00570         if ( bEatWhite )
00571         {
00572             while( cisspace(char)(*text) )
00573                 text++;
00574         }
00575         while ( text < end )
00576         {
00577             if ( text[0] == chconst(char,'&') )
00578             {
00579                 if ( text[1] == chconst(char,'#') )
00580                 {
00581                     char *p;
00582                     char ret;
00583                     if ( text[2] == chconst(char,'x') )
00584                         ret = (char)cstrtol(char)(text+3,&p,16);
00585                     else
00586                         ret = (char)cstrtol(char)(text+2,&p,10);
00587                     if ( *p++ != chconst(char,';') )
00588                     {
00589                         self->except( self,szconst(char,"Bad XML: Bad character Reference"), begin, text );
00590                         return NULL;
00591                     }
00592                     text = p;
00593                     *outtext++ = ret;
00594                 }
00595                 else
00596                 {
00597                     /* Get label*/
00598                     text++;
00599                     /* Next parse point after ';'*/
00600                     if( !cstrncmp(char)(text,szconst(char,"amp;"),4) )
00601                     {
00602                         *outtext++ = chconst(char,'&');
00603                         text += 4;
00604                     }
00605                     else if( !cstrncmp(char)(text,szconst(char,"apos;"),5) )
00606                     {
00607                         *outtext++ = chconst(char,'\'');
00608                         text += 5;
00609                     }
00610                     else if( !cstrncmp(char)(text,szconst(char,"lt;"),3) )
00611                     {
00612                         *outtext++ = chconst(char,'<');
00613                         text += 3;
00614                     }
00615                     else if( !cstrncmp(char)(text,szconst(char,"gt;"),3) )
00616                     {
00617                         *outtext++ = chconst(char,'>');
00618                         text += 3;
00619                     }
00620                     else if( !cstrncmp(char)(text,szconst(char,"quot;"),5) )
00621                     {
00622                         *outtext++ = chconst(char,'\"');
00623                         text += 5;
00624                     }
00625                     else
00626                     {
00627                         self->except( self,szconst(char,"Bad XML: Unknown entity reference"), begin, text );
00628                         return NULL;
00629                     }
00630                 }
00631             }
00632             else if ( text[0] == chconst(char,'<') )
00633             {
00634                 /* Handle CDATA*/
00635                 if ( !cstrncmp(char)( text, szconst(char,"<![CDATA["), 9 ) )
00636                 {
00637                     const char* szGT = cstrstr(char)(text+9, szconst(char,"]]>"));
00638                     if ( szGT && szGT < end )
00639                     {
00640                         /* + "<![CDATA["*/
00641                         text += 9;
00642                         if ( bEatWhite )
00643                         {
00644                             text=SkipSpace(text,szGT);
00645                         }
00646                         while ( text < szGT )
00647                         {
00648                             *outtext++ = *text++;
00649                         }
00650                         /* + "]]>"*/
00651                         text += 3;
00652                     }
00653                     else
00654                     {
00655                         self->except( self,szconst(char,"Bad XML: <![CDATA[ doesn't have a ']]>'"), text, NULL );
00656                         return NULL;
00657                     }
00658                 }
00659                 else
00660                 {
00661                     /* If we encounter embedded tags (and comments) in the body text we're reading, skip over it recursively.*/
00662                     int count = 0;
00663                     text++;
00664                     while ( text < end )
00665                     {
00666                         if ( *text == chconst(char,'<') )
00667                             count++;
00668                         else if ( *text == chconst(char,'>') && !count-- )
00669                         {
00670                             text++;
00671                             break;
00672                         }
00673                         text++;
00674                     }
00675                 }
00676             }
00677             else
00678             {
00679                 *outtext++ = *text++;
00680             }
00681         }
00682         /* Chew off trailing spaces */
00683         if ( bEatWhite )
00684         {
00685             while( outtext > begin && cisspace(char)(*(outtext-1)) )
00686                 outtext--;
00687         }
00688         /* NUL terminate, where possible */
00689         if( outtext < end )
00690             *outtext = 0;
00691         return outtext;
00692     }
00693 }
00694 
00703 const wchar_t* ctl_xmlread_translate_wchar_t( const ctl_xmlread* self, wchar_t* begin, const wchar_t* end, bool bEatWhite )
00704 {
00705     assertobjconst(self);
00706     assertptr( begin, sizeof(wchar_t)*(end-begin) );
00707     assert( end >= begin );
00708     {
00709         const wchar_t* text = begin;
00710         wchar_t* outtext = begin;
00711         /* Gnaw off leading spaces (note: not good for mixed/embedded text)*/
00712         if ( bEatWhite )
00713         {
00714             while( cisspace(wchar_t)(*text) )
00715                 text++;
00716         }
00717         while ( text < end )
00718         {
00719             if ( text[0] == chconst(wchar_t,'&') )
00720             {
00721                 if ( text[1] == chconst(wchar_t,'#') )
00722                 {
00723                     wchar_t *p;
00724                     wchar_t ret;
00725                     if ( text[2] == chconst(wchar_t,'x') )
00726                         ret = (wchar_t)cstrtol(wchar_t)(text+3,&p,16);
00727                     else
00728                         ret = (wchar_t)cstrtol(wchar_t)(text+2,&p,10);
00729                     if ( *p++ != chconst(wchar_t,';') )
00730                     {
00731                         self->except( self,szconst(char,"Bad XML: Bad character Reference"), NULL, NULL );
00732                         return NULL;
00733                     }
00734                     text = p;
00735                     *outtext++ = ret;
00736                 }
00737                 else
00738                 {
00739                     /* Get label*/
00740                     text++;
00741                     /* Next parse point after ';'*/
00742                     if( !cstrncmp(wchar_t)(text,szconst(wchar_t,"amp;"),4) )
00743                     {
00744                         *outtext++ = chconst(wchar_t,'&');
00745                         text += 4;
00746                     }
00747                     else if( !cstrncmp(wchar_t)(text,szconst(wchar_t,"apos;"),5) )
00748                     {
00749                         *outtext++ = chconst(wchar_t,'\'');
00750                         text += 5;
00751                     }
00752                     else if( !cstrncmp(wchar_t)(text,szconst(wchar_t,"lt;"),3) )
00753                     {
00754                         *outtext++ = chconst(wchar_t,'<');
00755                         text += 3;
00756                     }
00757                     else if( !cstrncmp(wchar_t)(text,szconst(wchar_t,"gt;"),3) )
00758                     {
00759                         *outtext++ = chconst(wchar_t,'>');
00760                         text += 3;
00761                     }
00762                     else if( !cstrncmp(wchar_t)(text,szconst(wchar_t,"quot;"),5) )
00763                     {
00764                         *outtext++ = chconst(wchar_t,'\"');
00765                         text += 5;
00766                     }
00767                     else
00768                     {
00769                         self->except( self,szconst(char,"Bad XML: Unknown entity reference"), NULL, NULL );
00770                         return NULL;
00771                     }
00772                 }
00773             }
00774             else if ( text[0] == chconst(wchar_t,'<') )
00775             {
00776                 /* Handle CDATA*/
00777                 if ( !cstrncmp(wchar_t)( text, szconst(wchar_t,"<![CDATA["), 9 ) )
00778                 {
00779                     const wchar_t* szGT = cstrstr(wchar_t)(text+9, szconst(wchar_t,"]]>"));
00780                     if ( szGT && szGT < end )
00781                     {
00782                         /* + "<![CDATA["*/
00783                         text += 9;
00784                         if ( bEatWhite )
00785                         {
00786                             while( cisspace(wchar_t)(*text) )
00787                                 text++;
00788                         }
00789                         while ( text < szGT )
00790                         {
00791                             *outtext++ = *text++;
00792                         }
00793                         /* + "]]>"*/
00794                         text += 3;
00795                     }
00796                     else
00797                     {
00798                         self->except( self,szconst(char,"Bad XML: <![CDATA[ doesn't have a ']]>'"), NULL, NULL );
00799                         return NULL;
00800                     }
00801                 }
00802                 else
00803                 {
00804                     /* If we encounter embedded tags (and comments) in the body text we're reading, skip over it recursively.*/
00805                     int count = 0;
00806                     text++;
00807                     while ( text < end )
00808                     {
00809                         if ( *text == chconst(wchar_t,'<') )
00810                             count++;
00811                         else if ( *text == chconst(wchar_t,'>') && !count-- )
00812                         {
00813                             text++;
00814                             break;
00815                         }
00816                         text++;
00817                     }
00818                 }
00819             }
00820             else
00821             {
00822                 *outtext++ = *text++;
00823             }
00824         }
00825         /* Chew off trailing spaces */
00826         if ( bEatWhite )
00827         {
00828             while( outtext > begin && cisspace(wchar_t)(*(outtext-1)) )
00829                 outtext--;
00830         }
00831         /* NUL terminate, where possible */
00832         if( outtext < end )
00833             *outtext = 0;
00834         return outtext;
00835     }
00836 }
00837 
00846 size_t ctl_xmlread_string( const ctl_xmlread* self, ctl_gstring(char)* str, const char* begin, const char* end, bool bEatWhite )
00847 {
00848     assertobjconst(self);
00849     assertobjptr( str );
00850     assertconst( begin,1 );
00851     assertconst( end,1 );
00852     assert( end >= begin );
00853     ctl_gstring_strncpy( char, str, begin, end-begin );
00854     str->end = (char*)ctl_xmlread_translate_char( self, str->begin, str->end, bEatWhite );
00855     return ctl_gstring_size(char, str );
00856 }
00857 
00868 bool ctl_xmlread_init( ctl_xmlread* self, const char* begin, const char* end, ctl_xmlread_exception except, void* exceptParam )
00869 {
00870     assertobjptr(self);
00871     assertconst( begin,1 );
00872     assertconst( end,1 );
00873     assert( end >= begin );
00874     self->szBegin = begin;
00875     self->szEnd = end;
00876     self->szpCurr = self->szBegin;
00877     self->szpEnd = self->szBegin;
00878     self->szcCurr = self->szBegin;
00879     self->szcEnd = self->szEnd;
00880     self->bRecursible = false;
00881     self->except = except ? except : DefaultXMLException;
00882     assertcodeptr(self->except);
00883     self->exceptParam = exceptParam;
00884 
00885     {
00886         const char* found = FindLT( self->szBegin, self->szEnd );
00887         if ( found && !cstrncmp(char)( found+1, szconst(char,"?xml"), 4 ) )
00888         {
00889             self->szBegin = self->szpCurr = found;
00890             self->szpEnd = FindGT( self->szpCurr+5, self->szEnd );
00891             if ( self->szpEnd && *(self->szpEnd-1) != chconst(char,'?') )
00892             {
00893                 self->except( self, szconst(char,"Bad XML: '<?xml' does not end with '?>'"), self->szpCurr, self->szpEnd );
00894                 return false;
00895             }
00896             self->szcCurr = self->szpEnd = self->szpCurr = self->szBegin = SkipSpace(self->szpEnd+1,self->szEnd);
00897         }
00898     }
00899     return ctl_xmlread_next( self );
00900 }
00901 
00902 /* Navigate data */
00903 
00910 void ctl_xmlread_rewind( ctl_xmlread* self )
00911 {
00912     assertobjptr(self);
00913     ctl_xmlread_init( self, self->szBegin, self->szEnd, self->except, self->exceptParam );
00914 }
00915 
00922 bool ctl_xmlread_next( ctl_xmlread* self )
00923 {
00924     assertobjptr(self);
00925     self->szpCurr = FindLT( self->szpEnd, self->szEnd );
00926     /* Eat the whole set of warts that begin with an '!'*/
00927     while ( self->szpCurr && self->szpCurr[1] == chconst(char,'!') )
00928     {
00929         /* If we encounter <![CDATA[ ... ]]>, skip it*/
00930         if ( !cstrncmp(char)( self->szpCurr, szconst(char,"<![CDATA["), 9 ) )
00931         {
00932             const char* szGT = cstrstr(char)(self->szpCurr+9, szconst(char,"]]>"));
00933             if ( szGT && szGT < self->szEnd )
00934             {
00935                 /* Look for next thing*/
00936                 self->szpCurr = FindLT( szGT+3, self->szEnd );
00937             }
00938             else
00939             {
00940                 self->except( self, szconst(char,"Bad XML: <![CDATA[ doesn't have a ']]>'"), self->szpCurr, NULL );
00941                 return false;
00942             }
00943         }
00944         else if ( !cstrncmp(char)( self->szpCurr, szconst(char,"<!--"), 4 ) )
00945         {
00946             const char* pGT = cstrstr(char)(self->szpCurr+4, szconst(char,"-->"));
00947             if ( pGT && pGT < self->szEnd )
00948             {
00949                 self->szpCurr = FindLT( pGT+3, self->szEnd );
00950                 continue;
00951             }
00952             else
00953             {
00954                 self->except( self, szconst(char,"Bad XML: '<!--' comment doesn't have a '-->'"), self->szpCurr, pGT );
00955                 return false;
00956             }
00957         }
00958         else
00959         {
00960             const char* szGT = FindGT( self->szpCurr+1, self->szEnd );
00961             if ( szGT )
00962             {
00963                 /* Look for next thing*/
00964                 self->szpCurr = FindLT( szGT+1, self->szEnd );
00965             }
00966             else
00967             {
00968                 self->except( self, szconst(char,"Bad XML: <! data doesn't have a '>'"), self->szpCurr, szGT );
00969                 return false;
00970             }
00971         }
00972     }
00973     if ( self->szpCurr )
00974     {
00975         /* Found a tag.*/
00976         self->szcCurr = FindGT(self->szpCurr+1, self->szEnd);
00977         if ( self->szcCurr )
00978         {
00979             /* See if it's an empty element tag <data stuff="stuff" />*/
00980             if ( *(self->szcCurr - 1) == chconst(char,'/') )
00981             {
00982                 self->szcCurr++;
00983                 self->szcEnd = self->szpEnd = self->szcCurr;
00984                 self->bRecursible = false;
00985                 return true;
00986             }
00987             else
00988             {
00989                 self->szcCurr++;
00990                 self->bRecursible = false;
00991                 self->szcEnd = MatchTag( self, self->szpCurr );
00992                 if ( self->szcEnd )
00993                 {
00994                     self->szpEnd = FindGT(self->szcEnd+1, self->szEnd);
00995                     if ( self->szpEnd )
00996                     {
00997                         self->szpEnd++;
00998                         return true;
00999                     }
01000                     else
01001                     {
01002                         self->except( self, szconst(char,"Bad XML: </tag> doesn't have a '>'"), self->szcCurr, self->szcEnd );
01003                         return false;
01004                     }
01005                 }
01006                 else
01007                 {
01008                     self->except( self, szconst(char,"Bad XML: Tag doesn't end with '</tag>', or same </tag>"), self->szcCurr, NULL );
01009                     return false;
01010                 }
01011             }
01012         }
01013         else
01014         {
01015             self->except( self, szconst(char,"Bad XML: Tag doesn't end with '>'"), self->szpCurr, NULL );
01016             return false;
01017         }
01018     }
01019     return false;
01020 }
01021 
01029 bool ctl_xmlread_find( ctl_xmlread* self, const char* type )
01030 {
01031     assertobjptr(self);
01032     assertconst(type,1);
01033     ctl_xmlread_rewind(self);
01034     if( ctl_xmlread_is( self, type ) )
01035         return true;
01036     return ctl_xmlread_findnext( self,type );
01037 }
01038 
01046 bool ctl_xmlread_findnext( ctl_xmlread* self, const char* type )
01047 {
01048     /* Backup current state */
01049     const char* bszpCurr     = self->szpCurr;
01050     const char* bszpEnd      = self->szpEnd;
01051     const char* bszcCurr     = self->szcCurr;
01052     const char* bszcEnd      = self->szcEnd;
01053     bool        bbRecursible = self->bRecursible;
01054     assertobjptr(self);
01055     assertconst(type,1);
01056     while ( ctl_xmlread_next(self) )
01057     {
01058         if( ctl_xmlread_is( self, type ) )
01059             return true;
01060     }
01061     /* Restore to backup state */
01062     self->szpCurr = bszpCurr;
01063     self->szpEnd = bszpEnd;
01064     self->szcCurr = bszcCurr;
01065     self->szcEnd = bszcEnd;
01066     self->bRecursible = bbRecursible;
01067     return false;
01068 }
01069 
01076 bool ctl_xmlread_canrecurse( ctl_xmlread* self )
01077 {
01078     assertobjptr(self);
01079     return self->bRecursible;
01080 }
01081 
01089 bool ctl_xmlread_recurse( ctl_xmlread* newself, const ctl_xmlread* self )
01090 {
01091     assertobjconst(self);
01092     assertobjptr( newself );
01093     if( self->bRecursible )
01094         return ctl_xmlread_init( newself, self->szcCurr, self->szcEnd, self->except, self->exceptParam );
01095     return false;
01096 }
01097 
01098 /* Get or compare tag's name */
01099 
01107 bool ctl_xmlread_getname( const ctl_xmlread* self, ctl_gstring(char)* str )
01108 {
01109     assertobjconst(self);
01110     assertobjptr( str );
01111     {
01112         const char* p = self->szpCurr+1;
01113         ctl_gstring_reset(char, str );
01114         while ( *p != chconst(char,'>') && (*p != chconst(char,'/') || p[1] != chconst(char,'>')) && !cisspace(char)(*p) )
01115             ctl_gstring_add(char, str, *p++ );
01116         return true;
01117     }
01118 }
01119 
01127 bool ctl_xmlread_is( const ctl_xmlread* self, const char* str )
01128 {
01129     assertobjconst(self);
01130     assertconst( str, 1 );
01131     {
01132         const char* pStr = str;
01133         const char* p = self->szpCurr+1;
01134         while ( *p != chconst(char,'>') && (*p != chconst(char,'/') || p[1] != chconst(char,'>')) && !cisspace(char)(*p) )
01135             if ( *p++ != *pStr++ )
01136                 return false;
01137         return !*pStr;
01138     }
01139 }
01140 
01141 /* Get translated contents of tag data */
01142 
01151 bool ctl_xmlread_getattribute( ctl_xmlread* self, ctl_gstring(char)* str, const char* label )
01152 {
01153     assertobjptr(self);
01154     assertobjptr( str );
01155     assertconst( label,1 );
01156     {
01157         ctl_sconst( char ) raw;
01158         ctl_gstring_reset(char, str );
01159         if( ctl_xmlread_rawattribute( self, &raw, label ) )
01160             return 0 != ctl_xmlread_string( self, str, raw.begin, raw.end, true );;
01161         return false;
01162     }
01163 }
01164 
01173 bool ctl_xmlread_getcontent( const ctl_xmlread* self, ctl_gstring(char)* str, bool eatWhite )
01174 {
01175     assertobjconst(self);
01176     assertobjptr( str );
01177     {
01178         const char* p = self->szcCurr;
01179         ctl_gstring_reset(char, str );
01180         return 0 != ctl_xmlread_string( self, str, p, self->szcEnd, eatWhite );
01181     }
01182 }
01183 
01192 bool ctl_xmlread_gettextbefore( const ctl_xmlread* self, ctl_gstring(char)* str, bool eatWhite )
01193 {
01194     assertobjconst(self);
01195     assertobjptr( str );
01196     {
01197         const char* pBegin = self->szpCurr-1;
01198         const char* pEnd = pBegin;
01199         ctl_gstring_reset(char, str );
01200         while ( pBegin > self->szBegin && *pBegin != chconst(char,'>') )
01201             pBegin--;
01202         if ( pBegin++ > self->szBegin )
01203             return 0 != ctl_xmlread_string( self, str, pBegin, pEnd, eatWhite );
01204     }
01205     return false;
01206 }
01207 
01216 bool ctl_xmlread_gettextafter( const ctl_xmlread* self, ctl_gstring(char)* str, bool eatWhite )
01217 {
01218     assertobjconst(self);
01219     assertobjptr( str );
01220     {
01221         const char* p = self->szpEnd;
01222         const char* p2 = FindLT(p, self->szEnd);
01223         ctl_gstring_reset(char, str );
01224         if ( p2 )
01225             return 0 != ctl_xmlread_string( self, str, p, p2, eatWhite );
01226     }
01227     return false;
01228 }
01229 
01230 /* Get untranslated contents of tag data - much faster than above, for fields we don't expect to find filled with \verbatim <>;"'\endverbatim  stuff */
01231 
01239 bool ctl_xmlread_rawname( const ctl_xmlread* self, ctl_sconst( char )* str )
01240 {
01241     assertobjconst(self);
01242     assertobjptr( str );
01243     {
01244         const char* p = str->begin = str->parse = self->szpCurr+1;
01245         while ( *p != chconst(char,'>') && (*p != chconst(char,'/') || p[1] != chconst(char,'>')) && !cisspace(char)(*p) )
01246             p++;
01247         str->end = p;
01248         return true;
01249     }
01250 }
01251 
01260 bool ctl_xmlread_rawattribute( ctl_xmlread* self, ctl_sconst( char )* str, const char* label )
01261 {
01262     assertobjptr(self);
01263     assertobjptr( str );
01264     assertconst( label,1 );
01265     {
01266         /* Skip over tag*/
01267         const char* p = self->szpCurr+1;
01268         const char* tagEnd = self->szcCurr-1;
01269         p = SkipSpace(p,tagEnd);
01270         if ( *p == chconst(char,'>') )
01271             return false;
01272         while ( p < tagEnd )
01273         {
01274             /* At first attribute*/
01275             p = FindSpace(p,tagEnd);
01276             p = SkipSpace(p,tagEnd);
01277             if ( *p == chconst(char,'>') )
01278                 return false;
01279             if ( !cstrncmp(char)(p,label,cstrlen(char)(label)) )
01280             {
01281                 const char* end;
01282                 /* Found the label, so now find '='*/
01283                 p = FindChar( p, tagEnd, chconst(char,'=') );
01284                 if ( NULL == p )
01285                 {
01286                     self->except( self, szconst(char,"Bad XML: Expected an '=' in an attribute."), self->szpCurr, p );
01287                     return false;
01288                 }
01289 
01290                 
01291                 /* Found the '=', so now find quote*/
01292                 p++;
01293                 p = SkipSpace(p,tagEnd);
01294                 end = MatchQuote( self, p, tagEnd );
01295                 if( NULL == end )
01296                 {
01297                     return false;
01298                 }
01299                 p++;
01300                 /* And we got the string*/
01301                 str->begin = str->parse = p;
01302                 str->end = end;
01303                 return true;
01304             }
01305             else
01306             {
01307                 /* Found a label, so now find '='*/
01308                 p = FindChar( p, tagEnd, chconst(char,'=') );
01309                 if ( NULL == p )
01310                 {
01311                     self->except( self, szconst(char,"Bad XML: Expected an '=' in an attribute."), self->szpCurr, p );
01312                     return false;
01313                 }
01314                 /* Found the '=', so now find quote*/
01315                 p++;
01316                 p = SkipSpace(p,tagEnd);
01317                 p = MatchQuote( self, p, tagEnd ) + 1;
01318                 if( NULL == p )
01319                 {
01320                     return false;
01321                 }
01322             }
01323         }
01324     }
01325     return false;
01326 }
01327 
01336 bool ctl_xmlread_getattribute_int( ctl_xmlread* self, const char* label, int32* value )
01337 {
01338     ctl_sconst( char ) tmp;
01339     if( ctl_xmlread_rawattribute( self, &tmp, label ) )
01340         return ctl_sconst_getint32(char, &tmp, value );
01341     return false;
01342 }
01343 
01344 
01353 bool ctl_xmlread_rawcontent( const ctl_xmlread* self, ctl_sconst(char)* str, bool eatWhite )
01354 {
01355     assertobjconst(self);
01356     assertobjptr( str );
01357     str->begin = str->parse = self->szcCurr;
01358     str->end = self->szcEnd;
01359     return str->end > str->begin;
01360 }
01361 
01370 bool ctl_xmlread_rawtextbefore( const ctl_xmlread* self, ctl_sconst(char)* str, bool eatWhite )
01371 {
01372     assertobjconst(self);
01373     assertobjptr( str );
01374     {
01375         const char* pBegin = self->szpCurr-1;
01376         const char* pEnd = pBegin;
01377         while ( pBegin > self->szBegin && *pBegin != chconst(char,'>') )
01378             pBegin--;
01379         if ( pBegin++ > self->szBegin )
01380         {
01381             str->begin = str->parse = pBegin;
01382             str->end = pEnd;
01383             return str->end > str->begin;
01384         }
01385     }
01386     return false;
01387 }
01388 
01397 bool ctl_xmlread_rawtextafter( const ctl_xmlread* self, ctl_sconst(char)* str, bool eatWhite )
01398 {
01399     assertobjconst(self);
01400     assertobjptr( str );
01401     str->begin = str->parse = self->szpEnd;
01402     str->end = FindLT(str->begin, self->szEnd);
01403     return str->end > str->begin;
01404 }
01405 
01406 
01411 #define DECL_DEF(type,unused) \
01412 void ppConcat(type,_xml_write)( const type* val, ctl_xmlwrite* self, const char* label )\
01413 {\
01414     if( NULL == label )\
01415         label = #type;\
01416     assertconst(label,1);   /* Inputs should be valid-looking */\
01417     assertobjconst(val);\
01418     assertobjptr(self);\
01419     ctl_xmlwrite_generaltag( self, szconst(char,#type), label );\
01420     xml_printf( self, szconst(char,"%") type_printf(char,type), typecast_printf(type)*val );\
01421     ctl_xmlwrite_endtag( self, szconst(char,#type), label );\
01422 }
01423 DEF_ENUMTYPES(DECL_DEF,unused)
01424 #undef DECL_DEF
01425 
01427 #define DECL_DEF(type,unused) \
01428 void ppConcat(type,_xml_write_array)( const type* ptr, size_t count, ctl_xmlwrite* self, const char* label )\
01429 {\
01430     if( NULL == label )\
01431         label = #type;\
01432     assertconst(label,1);   /* Inputs should be valid-looking */\
01433     assertconst(ptr,sizeof(*ptr));/* Inputs should be valid-looking */\
01434     assertobjptr(self);\
01435     ctl_xmlwrite_containertag( self, szconst(char,#type), count, label );\
01436     while( count-- ) \
01437         xml_printf( self, szconst(char," %") type_printf(char,type), typecast_printf(type)*ptr++ );\
01438     ctl_xmlwrite_endtag( self, szconst(char,#type), label );\
01439 }
01440 DEF_ENUMTYPES(DECL_DEF,unused)
01441 #undef DECL_DEF
01442 
01447 #define DECL_DEF(type,unused) \
01448 bool ppConcat(type,_xml_read)( type* val, ctl_xmlread* self, const char* label )\
01449 {\
01450     assertptr(val,sizeof(*val));\
01451     assertobjptr(self);\
01452     if( label ? ctl_xmlread_find( self, label ) : ctl_xmlread_is( self, #type ) )\
01453     {\
01454         ctl_sconst(char) body;\
01455         ctl_xmlread_rawcontent( self, &body, true );\
01456         ppConcat(ctl_sconst_get,type)(char, &body, val );\
01457         return true;\
01458     }\
01459     return false;\
01460 }
01461 DEF_ENUMTYPES(DECL_DEF,unused)
01462 #undef DECL_DEF
01463 
01468 #define DECL_DEF(type,unused) \
01469 bool ppConcat(type,_xml_read_array)( type* ptr, size_t count, ctl_xmlread* self, const char* label )\
01470 {\
01471     int remain = (int)count;\
01472     bool ret = false;\
01473     assertptr(ptr,sizeof(*ptr));/* Inputs should be valid-looking */\
01474     assertobjptr(self);\
01475     if( ctl_xmlread_find( self, label ) )\
01476     {\
01477         ctl_sconst(char) body;\
01478         ctl_xmlread_rawcontent( self, &body, true );\
01479         while( remain-- && ppConcat(ctl_sconst_get,type)(char, &body, ptr ) )\
01480             ptr++;\
01481         ret = true;\
01482     }\
01483     return ret;\
01484 }
01485 DEF_ENUMTYPES(DECL_DEF,unused)
01486 #undef DECL_DEF
01487 
01488 
01497 void char_xml_write_string( const char* buff, size_t size, ctl_xmlwrite* xml, const char* label )
01498 {
01499     assertconst(buff,size);/* Inputs should be valid-looking */
01500     assertobjptr( xml );
01501     assertconst( label,1 );
01502     ctl_xmlwrite_generaltag( xml, "string_char", label );
01503     ctl_xmlwrite_string_char( xml, buff, buff+size );
01504     ctl_xmlwrite_endtag( xml, "string_char", label );
01505 }
01506 
01515 void wchar_t_xml_write_string( const wchar_t* buff, size_t size, ctl_xmlwrite* xml, const char* label )
01516 {
01517     assertconst(buff,size*sizeof(wchar_t));/* Inputs should be valid-looking */
01518     assertobjptr( xml );
01519     assertconst( label,1 );
01520     ctl_xmlwrite_generaltag( xml, "string_wchar_t", label );
01521     ctl_xmlwrite_string_wchar_t( xml, buff, buff+size );
01522     ctl_xmlwrite_endtag( xml, "string_wchar_t", label );
01523 }
01524 
01534 bool char_xml_read_string( char* buff, size_t size, ctl_xmlread* self, const char* label )
01535 {
01536     assertptr(buff,size*sizeof(char));/* Inputs should be valid-looking */
01537     assertobjptr( self );
01538     assertconst( label,1 );
01539     if( ctl_xmlread_find( self, label ) )
01540     {
01541         ctl_sconst(char) body;
01542         size_t readsize;
01543         ctl_xmlread_rawcontent( self, &body, true );
01544         readsize = min( ctl_sconst_size(char,&body), size );
01545         cmemcpy( char, buff, body.begin, readsize );
01546         if( readsize < size )
01547             buff[readsize] = 0;
01548         ctl_xmlread_translate_char( self, buff, buff+readsize, true );
01549         return true;
01550     }
01551     return false;
01552 }
01553 
01563 bool wchar_t_xml_read_string( wchar_t* buff, size_t size, ctl_xmlread* self, const char* label )
01564 {
01565     assertptr(buff,size*sizeof(wchar_t));/* Inputs should be valid-looking */
01566     assertobjptr( self );
01567     assertconst( label,1 );
01568     if( ctl_xmlread_find( self, label ) )
01569     {
01570         ctl_sconst(char) body;
01571         ctl_xmlread_rawcontent( self, &body, true );
01572         /*size = min( ctl_sconst_size(char,&body), size ); */
01573         {
01574             ctl_gstring(wchar_t) tmp;
01575             ctl_gstring_init(wchar_t, &tmp);
01576             {
01577                 ctl_sconst_foreach(char,&body,curr)
01578                 {
01579                     ctl_gstring_add(wchar_t, &tmp, (wchar_t)*curr );
01580                 }
01581             }
01582             ctl_xmlread_translate_wchar_t( self, tmp.begin, tmp.end, true );
01583             cstrncpy(wchar_t)(buff,tmp.begin,size);
01584             ctl_gstring_destroy(wchar_t, &tmp);
01585         }
01586         return true;
01587     }
01588     return false;
01589 }
01590 
01599 bool char_xml_read_gstring( ctl_gstring(char)* str, ctl_xmlread* self, const char* label )
01600 {
01601     assertobjptr(str);/* Inputs should be valid-looking */
01602     assertobjptr(self);
01603     assertconst( label,1 );
01604     if( ctl_xmlread_find( self, label ) )
01605     {
01606         ctl_sconst(char) body;
01607         size_t readsize;
01608         ctl_xmlread_rawcontent( self, &body, true );
01609         readsize = ctl_sconst_size(char,&body);
01610         ctl_gstring_resize(char, str, readsize );
01611         memcpy( str->begin, body.begin, ctl_chsize(char,readsize) );
01612         ctl_xmlread_translate_char( self, str->begin, str->end, true );
01613         str->end = str->begin + cstrlen(char)(str->begin);
01614         return true;
01615     }
01616     return false;
01617 }
01618 
01627 bool wchar_t_xml_read_gstring( ctl_gstring(wchar_t)* str, ctl_xmlread* self, const char* label )
01628 {
01629     assertobjptr(str);/* Inputs should be valid-looking */
01630     assertobjptr(self);
01631     assertconst( label,1 );
01632     if( ctl_xmlread_find( self, label ) )
01633     {
01634         ctl_sconst(char) body;
01635         ctl_xmlread_rawcontent( self, &body, true );
01636         {
01637             ctl_gstring_reset(wchar_t, str );
01638             {
01639                 ctl_sconst_foreach(char,&body,curr)
01640                 {
01641                     ctl_gstring_add(wchar_t, str, (wchar_t)*curr );
01642                 }
01643             }
01644             ctl_xmlread_translate_wchar_t( self, str->begin, str->end, true );
01645             str->end = str->begin + cstrlen(wchar_t)(str->begin);
01646         }
01647         return true;
01648     }
01649     return false;
01650 }
01651 
01658 void raw_xml_writebytes( const ctl_serial* serial, ctl_xmlwrite* self, const char* label )
01659 {
01660     size_t count;
01661     assertobjconst(serial);/* Inputs should be valid-looking */
01662     assertobjptr(self);
01663     assertconst( label,1 );
01664     count = ctl_serial_total( serial );
01665     ctl_xmlwrite_containertag( self, "raw", count, label );
01666     ctl_xmlwrite_indent( self );
01667     {
01668         const uint8* p = serial->begin;
01669         while( p < serial->end )
01670         {
01671             if( 0 == ((p-serial->begin)&0xf) )
01672                 ctl_xmlwrite_newline( self );
01673             xml_printf( self, " %02x", (unsigned)*p );
01674             p++;
01675         }
01676     }
01677     ctl_xmlwrite_outdent( self );
01678     ctl_xmlwrite_endtag( self, "raw", label );
01679 
01680 }
01688 bool raw_xml_readbytes( ctl_serial* serial, ctl_xmlread* self, const char* label )
01689 {
01690     int32 size;
01691     assertobjptr(serial);/* Inputs should be valid-looking */
01692     assertobjptr(self);
01693     assertconst( label,1 );
01694     if( ctl_xmlread_find( self, label ) )
01695     {
01696         if( ctl_xmlread_getattribute_int( self, CTL_LABEL_COUNT, &size ) )
01697         {
01698             ctl_sconst(char) body;
01699             ctl_xmlread_rawcontent( self, &body, true );
01700             ctl_serial_alloc( serial, size );
01701             {
01702                 uint8 v;
01703                 while( ctl_sconst_getxuint8(char, &body, &v ) )
01704                     uint8_serial_write( &v, serial );
01705             }
01706             return true;
01707         }
01708     }
01709     return false;
01710 }
01711 
01712 #else /* CTL_UNIT */
01713 #include "unit/unit.h"
01714 
01721 void Test_XMLReader(void)
01722 {
01723     const char testtext[] = 
01724         "<?xml version=\"1.0\" ?>\n"
01725         "<DaScope>\n"
01726         "   <Bus Length=\"shorter\"/>\n"
01727         "   <!-- A Comment-->"
01728         "   <contact-info> <!-- A Comment <ignore><me> -->\n"
01729         "       spork <name><![CDATA[  John Doe]]></name> foon.\n\t<hole>AT&amp;T  </hole><!-- A Comment-->\n"
01730         "           <!-- A Comment-->   <email>spam@eggs.org</email>\n"
01731         "       <test param1=\"Sporkle\" param2 = \"&quot;Mittens&quot;\" > Test <!-- A Comment-->Text </test><!-- A Comment--> \n"
01732         "   </contact-info> <!-- A Comment-->\n"
01733         "<!-- A Comment--><SPORK /> \n"
01734         " <FOON /><!-- A Comment--></DaScope>\n"
01735         ;
01736 
01737     ctl_xmlread test;
01738     ctl_gstring_auto(char, buff );
01739     ctl_xmlread_init( &test, testtext, (char*)((uint8*)testtext+sizeof(testtext)), NULL, NULL );
01740 
01741     /* Empty test*/
01742     Test_Error( ctl_xmlread_is( &test, szconst(char,"DaScope") ) );
01743     Test_Error( ctl_xmlread_canrecurse(&test) );
01744     {
01745         ctl_xmlread xmain;
01746         Test_Error( ctl_xmlread_recurse( &xmain, &test ) );
01747         Test_Error( ctl_xmlread_getname( &xmain, &buff ) );
01748         Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"Bus") ) );
01749         Test_Error( ctl_xmlread_getattribute( &xmain, &buff, szconst(char,"Length") ) );
01750         Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"shorter") ) );
01751         Test_Error( ctl_xmlread_next( &xmain ) );
01752         Test_Error( ctl_xmlread_getname( &xmain, &buff ) );
01753         Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"contact-info") ) );
01754         Test_Error( ctl_xmlread_canrecurse(&xmain) );
01755         {
01756             ctl_xmlread contact;
01757             Test_Error( ctl_xmlread_recurse( &contact, &xmain ) );
01758             Test_Error( !ctl_xmlread_canrecurse(&contact) );
01759             Test_Error( ctl_xmlread_getname( &contact, &buff ) );
01760             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"name") ) );
01761             Test_Error( ctl_xmlread_getname( &contact, &buff ) );
01762             Test_Error( ctl_xmlread_getcontent( &contact, &buff, true ) );
01763             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"John Doe") ) );
01764             Test_Error( ctl_xmlread_getcontent( &contact, &buff, false ) );
01765             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"  John Doe") ) );
01766 
01767             /* See about picking out text before current tag*/
01768             ctl_xmlread_gettextbefore( &contact, &buff, true );
01769             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"spork") ) );
01770             /* See about picking out text after current tag*/
01771             ctl_xmlread_gettextafter( &contact, &buff, false );
01772             Test_Error( !cstrcmp(char)( buff.begin, szconst(char," foon.\n\t") ) );
01773 
01774             /* General sequential parsing test*/
01775             Test_Error( ctl_xmlread_next( &contact ) );
01776             Test_Error( ctl_xmlread_getname( &contact, &buff ) );
01777             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"hole") ) );
01778             Test_Error( ctl_xmlread_getcontent( &contact, &buff, true ) );
01779             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"AT&T") ) );
01780 
01781             /* General sequential parsing test*/
01782             Test_Error( ctl_xmlread_next( &contact ) );
01783             Test_Error( ctl_xmlread_getname( &contact, &buff ) );
01784             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"email") ) );
01785             Test_Error( ctl_xmlread_getcontent( &contact, &buff, true ) );
01786             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"spam@eggs.org") ) );
01787 
01788             /* Find tag test*/
01789             Test_Error( ctl_xmlread_find( &contact, szconst(char,"hole") ) );
01790             Test_Error( ctl_xmlread_getname( &contact, &buff ) );
01791             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"hole") ) );
01792             Test_Error( ctl_xmlread_getcontent( &contact, &buff, true ) );
01793             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"AT&T") ) );
01794 
01795             /* Not fount test */
01796             Test_Error( !ctl_xmlread_find( &contact, szconst(char,"spam") ) );
01797 
01798             /* Parameter test*/
01799             Test_Error( ctl_xmlread_find( &contact, szconst(char,"test") ) );
01800             Test_Error( ctl_xmlread_getattribute( &contact, &buff, szconst(char,"param2") ) );
01801             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"\42Mittens\42") ) ); /* MSVC6 chokes on '\"' escape. */
01802             Test_Error( ctl_xmlread_getattribute( &contact, &buff, szconst(char,"param1") ) );
01803             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"Sporkle") ) );
01804             Test_Error( !ctl_xmlread_getattribute( &contact, &buff, szconst(char,"notthere") ) );
01805             Test_Error( ctl_xmlread_getcontent( &contact, &buff, true ) );
01806             Test_Error( !cstrcmp(char)( buff.begin, szconst(char,"Test Text") ) );
01807 
01808             /* These aren't in the body of contact*/
01809             Test_Error( !ctl_xmlread_find( &contact, szconst(char,"SPORK") ) );
01810             Test_Error( !ctl_xmlread_find( &contact, szconst(char,"FOON") ) );
01811         }
01812         /* These are in the body of test */
01813         Test_Error( ctl_xmlread_find( &xmain, szconst(char,"SPORK") ) );
01814         Test_Error( ctl_xmlread_find( &xmain, szconst(char,"FOON") ) );
01815     }
01816     ctl_gstring_destroy(char, &buff );
01817 }
01818 
01819 #endif /* CTL_UNIT */

Generated on Fri Jan 2 15:28:35 2009 for Squat by  doxygen 1.5.6