From 368ade60b2109415ba46f029702e5ec77e56dda0 Mon Sep 17 00:00:00 2001 From: Jeremy Goossen Date: Thu, 15 Jan 2026 17:16:42 -0600 Subject: [PATCH 1/4] Fix lengths of interpreted HTML special-character entities Previously, the closing semicolon was not being removed from the interpreted string, causing `'` to be replaced by `';` instead of `'`. A similar error appeared for the double-quote `"`. --- bsx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bsx.c b/bsx.c index da6938f..9f87347 100644 --- a/bsx.c +++ b/bsx.c @@ -261,20 +261,20 @@ char *xmlDecodeEscapeString( char *string, int length, int *retlength ) *dst++ = '>'; skip = 3; } - else if( ccStrCmpSeq( string, "quot;", 4 ) ) + else if( ccStrCmpSeq( string, "quot;", 5 ) ) { *dst++ = '"'; - skip = 4; + skip = 5; } else if( ccStrCmpSeq( string, "amp;", 4 ) ) { *dst++ = '&'; skip = 4; } - else if( ccStrCmpSeq( string, "apos;", 4 ) ) + else if( ccStrCmpSeq( string, "apos;", 5 ) ) { *dst++ = '\''; - skip = 4; + skip = 5; } else { From bef984d4ea439b55a24b4e8bb3556e6f5cb0f88b Mon Sep 17 00:00:00 2001 From: Jeremy Goossen Date: Thu, 15 Jan 2026 20:14:17 -0600 Subject: [PATCH 2/4] Move XML parsing functions to their own file --- bsoutputxml.c | 1 + bsx.c | 220 +----------------------------------- bsx.h | 9 -- compile | 2 +- compile-win32.bat | 2 +- compile-win64.bat | 2 +- compile32 | 2 +- xml.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++ xml.h | 37 +++++++ 9 files changed, 319 insertions(+), 232 deletions(-) create mode 100644 xml.c create mode 100644 xml.h diff --git a/bsoutputxml.c b/bsoutputxml.c index b5f69d7..88c3b1a 100644 --- a/bsoutputxml.c +++ b/bsoutputxml.c @@ -39,6 +39,7 @@ #include "iolog.h" #include "debugtrack.h" #include "rand.h" +#include "xml.h" #if CC_UNIX #include diff --git a/bsx.c b/bsx.c index 9f87347..05a5829 100644 --- a/bsx.c +++ b/bsx.c @@ -36,6 +36,7 @@ #include "mm.h" #include "mmatomic.h" #include "mmbitmap.h" +#include "xml.h" /* For mkdir() */ #if CC_UNIX @@ -78,225 +79,6 @@ static inline int intMax( int x, int y ) //// -#define XML_CHAR_IS_DELIMITER(c) (((c)<=' ')||((c)=='<')) - -int xmlStrParseInt( char *str, int64_t *retint ) -{ - int negflag; - char c; - int64_t workint; - - if( !( str ) ) - return 0; - negflag = 0; - if( *str == '-' ) - negflag = 1; - str += negflag; - - workint = 0; - for( ; ; str++ ) - { - c = *str; - if( XML_CHAR_IS_DELIMITER( c ) ) - break; - if( ( c < '0' ) || ( c > '9' ) ) - return 0; - workint = ( workint * 10 ) + ( c - '0' ); - } - - if( negflag ) - workint = -workint; - *retint = workint; - return 1; -} - - -int xmlStrParseFloat( char *str, float *retfloat ) -{ - char c; - int negflag; - double workdouble; - double decfactor; - - if( !( str ) ) - return 0; - - negflag = 0; - if( *str == '-' ) - negflag = 1; - str += negflag; - - workdouble = 0.0; - for( ; ; str++ ) - { - c = *str; - if( XML_CHAR_IS_DELIMITER( c ) ) - goto done; - if( c == '.' ) - break; - if( ( c < '0' ) || ( c > '9' ) ) - return 0; - workdouble = ( workdouble * 10.0 ) + (double)( c - '0' ); - } - - str++; - decfactor = 0.1; - for( ; ; str++ ) - { - c = *str; - if( XML_CHAR_IS_DELIMITER( c ) ) - break; - if( ( c < '0' ) || ( c > '9' ) ) - return 0; - workdouble += (double)( c - '0' ) * decfactor; - decfactor *= 0.1; - } - - done: - - if( negflag ) - workdouble = -workdouble; - *retfloat = (float)workdouble; - - return 1; -} - - -//// - - - -/* Build string with escape chars as required, returned string must be free()'d */ -char *xmlEncodeEscapeString( char *string, int length, int *retlength ) -{ - char *dst, *dstbase; - unsigned char c; - - dstbase = malloc( 6*length + 1 ); - for( dst = dstbase ; length ; length--, string++ ) - { - c = *string; - if( c == '&' ) - { - dst[0] = '&'; - dst[1] = 'a'; - dst[2] = 'm'; - dst[3] = 'p'; - dst[4] = ';'; - dst += 5; - } - else if( c == '<' ) - { - dst[0] = '&'; - dst[1] = 'l'; - dst[2] = 't'; - dst[3] = ';'; - dst += 4; - } - else if( c == '>' ) - { - dst[0] = '&'; - dst[1] = 'g'; - dst[2] = 't'; - dst[3] = ';'; - dst += 4; - } - else if( c == '"' ) - { - dst[0] = '&'; - dst[1] = 'q'; - dst[2] = 'u'; - dst[3] = 'o'; - dst[4] = 't'; - dst[5] = ';'; - dst += 6; - } - else if( c == '\'' ) - { - dst[0] = '&'; - dst[1] = 'a'; - dst[2] = 'p'; - dst[3] = 'o'; - dst[4] = 's'; - dst[5] = ';'; - dst += 6; - } - else - *dst++ = c; - } - *dst = 0; - if( retlength ) - *retlength = (int)( dst - dstbase ); - - return dstbase; -} - - -/* Build string with decoded escape chars, returned string must be free()'d */ -char *xmlDecodeEscapeString( char *string, int length, int *retlength ) -{ - int skip; - char *dst, *dstbase; - unsigned char c; - - dstbase = malloc( length + 1 ); - for( dst = dstbase ; length ; length -= skip, string += skip ) - { - c = *string; - skip = 1; - if( c != '&' ) - *dst++ = c; - else - { - if( !( --length ) ) - goto error; - string++; - if( ccStrCmpSeq( string, "lt;", 3 ) ) - { - *dst++ = '<'; - skip = 3; - } - else if( ccStrCmpSeq( string, "gt;", 3 ) ) - { - *dst++ = '>'; - skip = 3; - } - else if( ccStrCmpSeq( string, "quot;", 5 ) ) - { - *dst++ = '"'; - skip = 5; - } - else if( ccStrCmpSeq( string, "amp;", 4 ) ) - { - *dst++ = '&'; - skip = 4; - } - else if( ccStrCmpSeq( string, "apos;", 5 ) ) - { - *dst++ = '\''; - skip = 5; - } - else - { - *dst++ = '&'; - skip = 0; - } - } - } - *dst = 0; - if( retlength ) - *retlength = (int)( dst - dstbase ); - return dstbase; - - error: - free( dstbase ); - return 0; -} - - -//// - - char *bsxReadString( char **readvalue, char *string, char *closestring ) { int len, stringlen; diff --git a/bsx.h b/bsx.h index 2ceb03a..96c33d8 100644 --- a/bsx.h +++ b/bsx.h @@ -21,15 +21,6 @@ * ----------------------------------------------------------------------------- */ -/* Build string with escape chars as required, returned string must be free()'d */ -char *xmlEncodeEscapeString( char *string, int length, int *retlength ); -/* Build string with decoded escape chars, returned string must be free()'d */ -char *xmlDecodeEscapeString( char *string, int length, int *retlength ); - - -//// - - typedef struct { /* ItemID (string) */ diff --git a/compile b/compile index 4203064..92d4ce2 100644 --- a/compile +++ b/compile @@ -1,3 +1,3 @@ gcc -std=gnu99 -m64 cpuconf.c cpuinfo.c -O2 -s -o cpuconf ./cpuconf -h -gcc -std=gnu99 -m64 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c -O2 -s -fvisibility=hidden -o bricksync -lm -lpthread -lssl -lcrypto -DBS_VERSION_BUILDTIME=`date '+%s'` +gcc -std=gnu99 -m64 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c xml.c -O2 -s -fvisibility=hidden -o bricksync -lm -lpthread -lssl -lcrypto -DBS_VERSION_BUILDTIME=`date '+%s'` diff --git a/compile-win32.bat b/compile-win32.bat index 80b9601..c4702bd 100644 --- a/compile-win32.bat +++ b/compile-win32.bat @@ -6,7 +6,7 @@ cpuconf.exe -h windres bricksync.rc -O coff -o bricksync.res REM Added -Wno-implicit-function-declaration to avoid false positive compile warnings REM Added to end to set BS_VERSION_BUILDTIME to dynamic build version in seconds from epoch -DBS_VERSION_BUILDTIME=`date '+%s'` -gcc -std=gnu99 -Wno-implicit-function-declaration -I./build-win32/ -L./build-win32/ -m32 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c bricksync.res -O2 -s -fvisibility=hidden -o bricksync -lm -lwsock32 -lws2_32 -lssl-1_1 -lcrypto-1_1 -DBS_VERSION_BUILDTIME=`date '+%s'` +gcc -std=gnu99 -Wno-implicit-function-declaration -I./build-win32/ -L./build-win32/ -m32 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c xml.c bricksync.res -O2 -s -fvisibility=hidden -o bricksync -lm -lwsock32 -lws2_32 -lssl-1_1 -lcrypto-1_1 -DBS_VERSION_BUILDTIME=`date '+%s'` pause diff --git a/compile-win64.bat b/compile-win64.bat index 81ef0e2..6d27add 100644 --- a/compile-win64.bat +++ b/compile-win64.bat @@ -6,5 +6,5 @@ cpuconf.exe -h windres bricksync.rc -O coff -o bricksync.res REM Added -Wno-implicit-function-declaration to avoid false positive compile warnings REM Added to end to set BS_VERSION_BUILDTIME to dynamic build version in seconds from epoch -DBS_VERSION_BUILDTIME=`date '+%s'` -gcc -std=gnu99 -Wno-implicit-function-declaration -I./build-win64/ -L./build-win64/ -m64 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c bricksync.res -O2 -s -fvisibility=hidden -o bricksync -lm -lwsock32 -lws2_32 -lssl-1_1-x64 -lcrypto-1_1-x64 -DBS_VERSION_BUILDTIME=`date '+%s'` +gcc -std=gnu99 -Wno-implicit-function-declaration -I./build-win64/ -L./build-win64/ -m64 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c ccstr.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c xml.c bricksync.res -O2 -s -fvisibility=hidden -o bricksync -lm -lwsock32 -lws2_32 -lssl-1_1-x64 -lcrypto-1_1-x64 -DBS_VERSION_BUILDTIME=`date '+%s'` diff --git a/compile32 b/compile32 index 4129489..675deca 100644 --- a/compile32 +++ b/compile32 @@ -1,3 +1,3 @@ gcc -std=gnu99 -m32 cpuconf.c cpuinfo.c -O2 -s -o cpuconf ./cpuconf -h -gcc -std=gnu99 -m32 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c -O2 -s -fvisibility=hidden -o bricksync -lm -lpthread -lssl -lcrypto -DBS_VERSION_BUILDTIME=`date '+%s'` +gcc -std=gnu99 -m32 bricksync.c bricksyncconf.c bricksyncnet.c bricksyncinit.c bricksyncinput.c bsantidebug.c bsmessage.c bsmathpuzzle.c bsorder.c bsregister.c bsapihistory.c bstranslation.c bsevalgrade.c bsoutputxml.c bsorderdir.c bspriceguide.c bsmastermode.c bscheck.c bssync.c bsapplydiff.c bsfetchorderinv.c bsresolve.c bscatedit.c bsfetchinv.c bsfetchorderlist.c bsfetchset.c bscheckreg.c bsfetchpriceguide.c tcp.c vtlex.c cpuinfo.c antidebug.c mm.c mmhash.c mmbitmap.c cc.c debugtrack.c tcphttp.c oauth.c bricklink.c brickowl.c brickowlinv.c colortable.c json.c bsx.c bsxpg.c journal.c exclperm.c iolog.c crypthash.c cryptsha1.c rand.c bn512.c bn1024.c rsabn.c xml.c -O2 -s -fvisibility=hidden -o bricksync -lm -lpthread -lssl -lcrypto -DBS_VERSION_BUILDTIME=`date '+%s'` diff --git a/xml.c b/xml.c new file mode 100644 index 0000000..acff7ae --- /dev/null +++ b/xml.c @@ -0,0 +1,276 @@ +/* ----------------------------------------------------------------------------- + * + * Copyright (c) 2014-2019 Alexis Naveros. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * ----------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "ccstr.h" +#include "mm.h" +#include "mmatomic.h" +#include "mmbitmap.h" + +/* For mkdir() */ +#if CC_UNIX + #include + #include + #include +#elif CC_WINDOWS + #include + #include + #include +#else + #error Unknown/Unsupported platform! +#endif + + +#include "xml.h" + + +//// + + +#define XML_CHAR_IS_DELIMITER(c) (((c)<=' ')||((c)=='<')) + +/* Parse an XML string for an integer value */ +int xmlStrParseInt( char *str, int64_t *retint ) +{ + int negflag; + char c; + int64_t workint; + + if( !( str ) ) + return 0; + negflag = 0; + if( *str == '-' ) + negflag = 1; + str += negflag; + + workint = 0; + for( ; ; str++ ) + { + c = *str; + if( XML_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workint = ( workint * 10 ) + ( c - '0' ); + } + + if( negflag ) + workint = -workint; + *retint = workint; + return 1; +} + + +/* Parse an XML string for an float value */ +int xmlStrParseFloat( char *str, float *retfloat ) +{ + char c; + int negflag; + double workdouble; + double decfactor; + + if( !( str ) ) + return 0; + + negflag = 0; + if( *str == '-' ) + negflag = 1; + str += negflag; + + workdouble = 0.0; + for( ; ; str++ ) + { + c = *str; + if( XML_CHAR_IS_DELIMITER( c ) ) + goto done; + if( c == '.' ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble = ( workdouble * 10.0 ) + (double)( c - '0' ); + } + + str++; + decfactor = 0.1; + for( ; ; str++ ) + { + c = *str; + if( XML_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble += (double)( c - '0' ) * decfactor; + decfactor *= 0.1; + } + + done: + + if( negflag ) + workdouble = -workdouble; + *retfloat = (float)workdouble; + + return 1; +} + + +//// + + + +/* Build string with escape chars as required, returned string must be free()'d */ +char *xmlEncodeEscapeString( char *string, int length, int *retlength ) +{ + char *dst, *dstbase; + unsigned char c; + + dstbase = malloc( 6*length + 1 ); + for( dst = dstbase ; length ; length--, string++ ) + { + c = *string; + if( c == '&' ) + { + dst[0] = '&'; + dst[1] = 'a'; + dst[2] = 'm'; + dst[3] = 'p'; + dst[4] = ';'; + dst += 5; + } + else if( c == '<' ) + { + dst[0] = '&'; + dst[1] = 'l'; + dst[2] = 't'; + dst[3] = ';'; + dst += 4; + } + else if( c == '>' ) + { + dst[0] = '&'; + dst[1] = 'g'; + dst[2] = 't'; + dst[3] = ';'; + dst += 4; + } + else if( c == '"' ) + { + dst[0] = '&'; + dst[1] = 'q'; + dst[2] = 'u'; + dst[3] = 'o'; + dst[4] = 't'; + dst[5] = ';'; + dst += 6; + } + else if( c == '\'' ) + { + dst[0] = '&'; + dst[1] = 'a'; + dst[2] = 'p'; + dst[3] = 'o'; + dst[4] = 's'; + dst[5] = ';'; + dst += 6; + } + else + *dst++ = c; + } + *dst = 0; + if( retlength ) + *retlength = (int)( dst - dstbase ); + + return dstbase; +} + + +/* Build string with decoded escape chars, returned string must be free()'d */ +char *xmlDecodeEscapeString( char *string, int length, int *retlength ) +{ + int skip; + char *dst, *dstbase; + unsigned char c; + + dstbase = malloc( length + 1 ); + for( dst = dstbase ; length ; length -= skip, string += skip ) + { + c = *string; + skip = 1; + if( c != '&' ) + *dst++ = c; + else + { + if( !( --length ) ) + goto error; + string++; + if( ccStrCmpSeq( string, "lt;", 3 ) ) + { + *dst++ = '<'; + skip = 3; + } + else if( ccStrCmpSeq( string, "gt;", 3 ) ) + { + *dst++ = '>'; + skip = 3; + } + else if( ccStrCmpSeq( string, "quot;", 5 ) ) + { + *dst++ = '"'; + skip = 5; + } + else if( ccStrCmpSeq( string, "amp;", 4 ) ) + { + *dst++ = '&'; + skip = 4; + } + else if( ccStrCmpSeq( string, "apos;", 5 ) ) + { + *dst++ = '\''; + skip = 5; + } + else + { + *dst++ = '&'; + skip = 0; + } + } + } + *dst = 0; + if( retlength ) + *retlength = (int)( dst - dstbase ); + return dstbase; + + error: + free( dstbase ); + return 0; +} + + +//// \ No newline at end of file diff --git a/xml.h b/xml.h new file mode 100644 index 0000000..7b9165a --- /dev/null +++ b/xml.h @@ -0,0 +1,37 @@ +/* ----------------------------------------------------------------------------- + * + * Copyright (c) 2014-2019 Alexis Naveros. + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * ----------------------------------------------------------------------------- + */ + +/* Build string with escape chars as required, returned string must be free()'d */ +char *xmlEncodeEscapeString( char *string, int length, int *retlength ); +/* Build string with decoded escape chars, returned string must be free()'d */ +char *xmlDecodeEscapeString( char *string, int length, int *retlength ); + +/* Parse an XML string for an integer value */ +int xmlStrParseInt( char *str, int64_t *retint ); +/* Parse an XML string for an float value */ +int xmlStrParseFloat( char *str, float *retfloat ); + + +//// + + From e080df808077565ef985881004997a114faecc21 Mon Sep 17 00:00:00 2001 From: Jeremy Goossen Date: Thu, 15 Jan 2026 21:26:28 -0600 Subject: [PATCH 3/4] Implement helper functions to more robustly encode/decode XML escaped entities --- xml.c | 90 +++++++++++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/xml.c b/xml.c index acff7ae..57f140c 100644 --- a/xml.c +++ b/xml.c @@ -144,6 +144,14 @@ int xmlStrParseFloat( char *str, float *retfloat ) //// +/* Concatenate suffix to string, and move string pointer to after suffix */ +void xmlAppendSuffix( char *suffix, char **string ) +{ + for ( ; *suffix ; suffix++ ) + { + *(*string)++ = *suffix; + } +} /* Build string with escape chars as required, returned string must be free()'d */ char *xmlEncodeEscapeString( char *string, int length, int *retlength ) @@ -155,50 +163,26 @@ char *xmlEncodeEscapeString( char *string, int length, int *retlength ) for( dst = dstbase ; length ; length--, string++ ) { c = *string; + /* Encode predefined entities */ if( c == '&' ) { - dst[0] = '&'; - dst[1] = 'a'; - dst[2] = 'm'; - dst[3] = 'p'; - dst[4] = ';'; - dst += 5; + xmlAppendSuffix("&", &dst); } else if( c == '<' ) { - dst[0] = '&'; - dst[1] = 'l'; - dst[2] = 't'; - dst[3] = ';'; - dst += 4; + xmlAppendSuffix("<", &dst); } else if( c == '>' ) { - dst[0] = '&'; - dst[1] = 'g'; - dst[2] = 't'; - dst[3] = ';'; - dst += 4; + xmlAppendSuffix(">", &dst); } else if( c == '"' ) { - dst[0] = '&'; - dst[1] = 'q'; - dst[2] = 'u'; - dst[3] = 'o'; - dst[4] = 't'; - dst[5] = ';'; - dst += 6; + xmlAppendSuffix(""", &dst); } else if( c == '\'' ) { - dst[0] = '&'; - dst[1] = 'a'; - dst[2] = 'p'; - dst[3] = 'o'; - dst[4] = 's'; - dst[5] = ';'; - dst += 6; + xmlAppendSuffix("'", &dst); } else *dst++ = c; @@ -211,6 +195,21 @@ char *xmlEncodeEscapeString( char *string, int length, int *retlength ) } +/* Check if the prefix matches the given string; otherwise, return 0. + * If the prefix matches, save the replacement character to the output string before inrementing. + * Finally, output the number of characters matched from the input string. */ +int xmlReplacePrefix( char *string, char *prefix, char replacement, char **output, int *skip) +{ + if ( ccStrCmpSeq( string, prefix, strlen(prefix) ) ) + { + *(*output)++ = replacement; + *skip = strlen(prefix); + return 1; + } + + return 0; +} + /* Build string with decoded escape chars, returned string must be free()'d */ char *xmlDecodeEscapeString( char *string, int length, int *retlength ) { @@ -230,31 +229,12 @@ char *xmlDecodeEscapeString( char *string, int length, int *retlength ) if( !( --length ) ) goto error; string++; - if( ccStrCmpSeq( string, "lt;", 3 ) ) - { - *dst++ = '<'; - skip = 3; - } - else if( ccStrCmpSeq( string, "gt;", 3 ) ) - { - *dst++ = '>'; - skip = 3; - } - else if( ccStrCmpSeq( string, "quot;", 5 ) ) - { - *dst++ = '"'; - skip = 5; - } - else if( ccStrCmpSeq( string, "amp;", 4 ) ) - { - *dst++ = '&'; - skip = 4; - } - else if( ccStrCmpSeq( string, "apos;", 5 ) ) - { - *dst++ = '\''; - skip = 5; - } + /* Decode predefined entities */ + if( xmlReplacePrefix( string, "lt;", '<', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "gt;", '>', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "quot;", '"', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "amp;", '&', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "apos;", '\'', &dst, &skip ) ) { } else { *dst++ = '&'; From 494097d2de5cf8092ddb5b344bac98fcec5cd59b Mon Sep 17 00:00:00 2001 From: Jeremy Goossen Date: Thu, 15 Jan 2026 21:38:55 -0600 Subject: [PATCH 4/4] Add XML escape decoding to the logs (files and stdout) and decode more sequences that appear Many items in the catalog contained the sequences '(' and ')' when printed in the file logs (such as with `verify brickowl`). These are now printed with the characters '(' and ')'. Store names with an apostrophe were printed as ''' in the startup info messages. These now print using the correct puctuation. --- iolog.c | 27 +++++++++++++++++++++++++++ xml.c | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/iolog.c b/iolog.c index e316aff..3400278 100644 --- a/iolog.c +++ b/iolog.c @@ -35,6 +35,7 @@ #include "ccstr.h" #include "mm.h" #include "iolog.h" +#include "xml.h" /* For mkdir() */ #if CC_UNIX @@ -399,9 +400,33 @@ static void ioHandleColorCode( ioLog *log, int color ) return; } +/* Interpret escaped XML entities in the given string in-place */ +static void interpretXmlEntities( char *string ) +{ + char *targetstring, *sourcestring, *decodedstring; + + decodedstring = xmlDecodeEscapeString( string, strlen( string ), 0 ); + + targetstring = decodedstring; + sourcestring = string; + for ( ; *targetstring ; targetstring++ ) + { + /* Replace the original string with decoded characters */ + *sourcestring++ = *targetstring; + } + for ( ; *sourcestring ; sourcestring++ ) + { + /* Set residual characters in the input string to \0 */ + *sourcestring = 0; + } + + free( decodedstring ); +} static void ioPrintStdoutString( ioLog *log, char *string ) { + interpretXmlEntities( string ); + int writelength, colorcode, endlinecount; char *writebase; @@ -445,6 +470,8 @@ static void ioPrintStdoutString( ioLog *log, char *string ) static void ioPrintFileString( ioLog *log, FILE *file, char *string ) { + interpretXmlEntities( string ); + int writelength; char *writebase; diff --git a/xml.c b/xml.c index 57f140c..7554aea 100644 --- a/xml.c +++ b/xml.c @@ -235,6 +235,10 @@ char *xmlDecodeEscapeString( char *string, int length, int *retlength ) else if( xmlReplacePrefix( string, "quot;", '"', &dst, &skip ) ) { } else if( xmlReplacePrefix( string, "amp;", '&', &dst, &skip ) ) { } else if( xmlReplacePrefix( string, "apos;", '\'', &dst, &skip ) ) { } + /* Also decode numeric entities */ + else if( xmlReplacePrefix( string, "#039;", '\'', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "#40;", '(', &dst, &skip ) ) { } + else if( xmlReplacePrefix( string, "#41;", ')', &dst, &skip ) ) { } else { *dst++ = '&';