diff options
Diffstat (limited to '')
| -rw-r--r-- | project/dclib/dclib-basics.c | 8065 |
1 files changed, 8065 insertions, 0 deletions
diff --git a/project/dclib/dclib-basics.c b/project/dclib/dclib-basics.c new file mode 100644 index 0000000..74676d0 --- /dev/null +++ b/project/dclib/dclib-basics.c @@ -0,0 +1,8065 @@ + +/*************************************************************************** + * * + * _____ ____ * + * | __ \ / __ \ _ _ _____ * + * | | \ \ / / \_\ | | | | _ \ * + * | | \ \| | | | | | |_| | * + * | | | || | | | | | ___/ * + * | | / /| | __ | | | | _ \ * + * | |__/ / \ \__/ / | |___| | |_| | * + * |_____/ \____/ |_____|_|_____/ * + * * + * Wiimms source code library * + * * + *************************************************************************** + * * + * Copyright (c) 2012-2020 by Dirk Clemens <wiimm@wiimm.de> * + * * + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt * + * * + ***************************************************************************/ + +#define _GNU_SOURCE 1 + +#ifndef WIN_SZS_LIB + // needed for GetTermWidthFD() + #include <sys/ioctl.h> +#endif + +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <stdio.h> +#include <stddef.h> +#include <errno.h> +#include <sys/time.h> +#include <fcntl.h> + +#include "dclib-basics.h" +#include "dclib-debug.h" +#include "dclib-file.h" +#include "dclib-utf8.h" +#include "dclib-network.h" + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// some basic string functions /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char EmptyQuote[] = "\"\""; // Ignored by FreeString() +const char EmptyString[4] = "\0\0\0"; // Ignored by FreeString() +const char MinusString[4] = "-\0\0"; // Ignored by FreeString() + +const char LoDigits[0x41] = + "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ".+"; + +const char HiDigits[0x41] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + ".+"; + +const char Tabs20[21] = // 20 * TABS + NULL + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +const char LF20[21] = // 20 * LF + NULL + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + +const char Space200[201] = // 200 * ' ' + NULL + " " + " " + " " + " "; + +const char Minus300[301] = // 300 * '-' + NULL + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------" + "--------------------------------------------------"; + +const char Underline200[201] = // 200 * '_' + NULL + "__________________________________________________" + "__________________________________________________" + "__________________________________________________" + "__________________________________________________"; + +const char Hash200[201] = // 200 * '#' + NULL + "##################################################" + "##################################################" + "##################################################" + "##################################################"; + +const char Slash200[201] = // 200 * '/' + NULL + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////" + "//////////////////////////////////////////////////"; + +// UTF-8 => 3 bytes per char +const char ThinLine300_3[901] = // 300 * '─' (U+2500) + NULL + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────" + "──────────────────────────────────────────────────"; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void PrintSettingsDCLIB ( FILE *f, int indent ) +{ + DASSERT(f); + indent = NormalizeIndent(indent); + MARK_USED(indent); // for the case, that all other conditions fail! + + #ifdef DCLIB_NETWORK + fprintf(f,"%*s" "DCLIB_NETWORK\n",indent,""); + #endif + + #ifdef DCLIB_TERMINAL + fprintf(f,"%*s" "DCLIB_TERMINAL\n",indent,""); + #endif + + #ifdef DCLIB_MYSQL + fprintf(f,"%*s" "DCLIB_MYSQL\n",indent,""); + #endif + + #ifdef DCLIB_HAVE_USABLE_SIZE + fprintf(f,"%*s" "DCLIB_HAVE_USABLE_SIZE\n",indent,""); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupMultiProcessing() +{ + multi_processing = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupProgname ( int argc, char ** argv, ccp tname, ccp tvers, ccp ttitle ) +{ + if (tname) + toolname = tname; + if (tvers) + toolversion = tvers; + if (ttitle) + tooltitle = ttitle; + + if (!progpath) + { + char path[PATH_MAX]; + int res = GetProgramPath( path, sizeof(path), true, argc > 0 ? argv[0] : 0 ); + if ( res > 0 ) + progpath = STRDUP(path); + } + + if ( argc > 0 && *argv && **argv ) + { + ccp slash = strrchr(*argv,'/'); + progname = slash ? slash + 1 : *argv; + } + else + progname = toolname && *toolname ? toolname : "?"; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp ProgramPath() +{ + static bool done = false; + if ( !progpath && !done ) + { + done = true; + + char path[PATH_MAX]; + int res = GetProgramPath( path, sizeof(path), true, 0 ); + if ( res > 0 ) + progpath = STRDUP(path); + } + return progpath; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp ProgramDirectory() +{ + if (!progdir) + { + ccp path = ProgramPath(); + if (path) + { + ccp end = strrchr(path,'/'); + const uint len = end ? end - path : strlen(path); + progdir = MEMDUP(path,len); + } + } + return progdir; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef __CYGWIN__ + +ccp ProgramPathNoExt() +{ + static ccp res = 0; + if (!res) + { + res = ProgramPath(); + if (res) + { + ccp slash = strrchr(res,'/'); + ccp ext = strrchr( slash ? slash + 1 : res, '.' ); + if (ext) + res = MEMDUP(res,ext-res); + } + } + return res; +} + +#endif // __CYGWIN__ + +/////////////////////////////////////////////////////////////////////////////// + +int GetProgramPath +( + // returns strlen(buf) on success, -1 on failure + + char *buf, // buffer for the result; empty string on failure. + uint buf_size, // size of buffer + bool check_proc, // true: read files of /proc/... to find path + ccp argv0 // not NULL: analyse this argument (argv[0]) +) +{ + char path[PATH_MAX], path2[PATH_MAX]; + + + //--- read files of /proc/... + + if (check_proc) + { + static ccp tab[] = + { + "/proc/self/exe", + "/proc/curproc/file", + "/proc/self/path/a.out", + 0 + }; + + ccp *ptr; + for ( ptr = tab; *ptr; ptr++ ) + if ( realpath(*ptr,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + + + //--- analyze argv0 + + if (argv0) + { + if ( *argv0 == '/' ) + { + // absolute path + if ( realpath(argv0,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + else if (strchr(argv0,'/')) + { + // relative path + if ( getcwd(path2,sizeof(path2)) > 0 ) + { + ccp p2 = PathCatPP(path2,sizeof(path2),path,argv0); + if ( realpath(p2,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + } + else + { + // simple name => search in path environment + uint arglen = strlen(argv0); + if ( arglen < sizeof(path2) - 2 ) + { + ccp dest_end = path2 + sizeof(path2) - 1 - arglen; + ccp env = getenv("PATH"); + while ( env && *env ) + { + while ( *env == ':' ) + env++; + char *dest = path2; + while ( *env && *env != ':' && dest < dest_end ) + *dest++ = *env++; + *dest++ = '/'; + memcpy(dest,argv0,arglen+1); + if ( realpath(path2,path) && *path ) + return StringCopyS(buf,buf_size,path) - buf; + } + } + } + } + + + //--- failed + + *buf = 0; + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeString ( ccp str ) +{ + noTRACE("FreeString(%p) EmptyString=%p MinusString=%p\n", + str, EmptyString, MinusString ); + if ( str != EmptyString && str != EmptyQuote && str != MinusString ) + FREE((char*)str); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * StringCopyE ( char * buf, ccp buf_end, ccp src ) +{ + // RESULT: end of copied string pointing to NULL + // 'src' may be a NULL pointer. + + if ( buf >= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf<buf_end); + buf_end--; + + if (src) + while( buf < buf_end && *src ) + *buf++ = *src++; + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringCopyS ( char * buf, size_t buf_size, ccp src ) +{ + return StringCopyE(buf,buf+buf_size,src); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringCopyEM ( char * buf, ccp buf_end, ccp src, size_t max_copy ) +{ + // RESULT: end of copied string pointing to NULL + // 'src' may be a NULL pointer. + + ASSERT(buf); + ASSERT(buf<buf_end); + buf_end--; + if ( max_copy >= 0 && buf + max_copy < buf_end ) + buf_end = buf + max_copy; + + if (src) + while( buf < buf_end && *src ) + *buf++ = *src++; + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringCopySM ( char * buf, size_t buf_size, ccp src, size_t max_copy ) +{ + return StringCopyEM(buf,buf+buf_size,src,max_copy); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringCat2E ( char * buf, ccp buf_end, ccp src1, ccp src2 ) +{ + // RESULT: end of copied string pointing to NULL + // 'src*' may be a NULL pointer. + + ASSERT(buf); + ASSERT(buf<buf_end); + buf_end--; + + if (src1) + while( buf < buf_end && *src1 ) + *buf++ = *src1++; + + if (src2) + while( buf < buf_end && *src2 ) + *buf++ = *src2++; + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringCat2S ( char * buf, size_t buf_size, ccp src1, ccp src2 ) +{ + return StringCat2E(buf,buf+buf_size,src1,src2); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringCat3E ( char * buf, ccp buf_end, ccp src1, ccp src2, ccp src3 ) +{ + // RESULT: end of copied string pointing to NULL + // 'src*' may be a NULL pointer. + + ASSERT(buf); + ASSERT(buf<buf_end); + buf_end--; + + if (src1) + while( buf < buf_end && *src1 ) + *buf++ = *src1++; + + if (src2) + while( buf < buf_end && *src2 ) + *buf++ = *src2++; + + if (src3) + while( buf < buf_end && *src3 ) + *buf++ = *src3++; + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringCat3S ( char * buf, size_t buf_size, ccp src1, ccp src2, ccp src3 ) +{ + return StringCat3E(buf,buf+buf_size,src1,src2,src3); +} + +/////////////////////////////////////////////////////////////////////////////// +// [[?]] + +char * StringCat2A ( ccp src1, ccp src2 ) +{ + mem_t mem = MemCat2A( MemByString(src1), MemByString(src2) ); + return (char*)mem.ptr; +} + +//----------------------------------------------------------------------------- + +char * StringCat3A ( ccp src1, ccp src2, ccp src3 ) +{ + mem_t mem = MemCat3A( MemByString(src1), MemByString(src2), MemByString(src3) ); + return (char*)mem.ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringLowerE ( char * buf, ccp buf_end, ccp src ) +{ + // RESULT: end of copied string pointing to NULL + // 'src' may be a NULL pointer. + + if ( buf >= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf<buf_end); + buf_end--; + + if (src) + while( buf < buf_end && *src ) + *buf++ = tolower((int)*src++); + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringLowerS ( char * buf, size_t buf_size, ccp src ) +{ + return StringLowerE(buf,buf+buf_size,src); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * StringUpperE ( char * buf, ccp buf_end, ccp src ) +{ + // RESULT: end of copied string pointing to NULL + // 'src' may be a NULL pointer. + + if ( buf >= buf_end ) + return (char*)buf_end - 1; + + DASSERT(buf); + DASSERT(buf<buf_end); + buf_end--; + + if (src) + while( buf < buf_end && *src ) + *buf++ = toupper((int)*src++); + + *buf = 0; + return buf; +} + +//----------------------------------------------------------------------------- + +char * StringUpperS ( char * buf, size_t buf_size, ccp src ) +{ + return StringUpperE(buf,buf+buf_size,src); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// PathCat*() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufPP ( char * buf, size_t bufsize, ccp path1, ccp path2 ) +{ + // concatenate path + path; returns buf + + DASSERT(buf); + DASSERT(bufsize); + + if ( !path1 || !*path1 ) + { + if (path2) + StringCopyS(buf,bufsize,path2); + else + *buf = 0; + return buf; + } + + char * ptr = StringCopyS(buf,bufsize-1,path1); + DASSERT( ptr > buf ); + if ( ptr[-1] != '/' ) + *ptr++ = '/'; + if (path2) + { + while ( *path2 == '/' ) + path2++; + StringCopyE(ptr,buf+bufsize,path2); + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatPP ( char * buf, size_t bufsize, ccp path1, ccp path2 ) +{ + // concatenate path + path; returns path1 | path2 | buf + + if ( !path1 || !*path1 ) + return path2 ? path2 : ""; + + if ( !path2 || !*path2 ) + return path1; + + char * ptr = StringCopyS(buf,bufsize-1,path1); + DASSERT( ptr > buf ); + if ( ptr[-1] != '/' ) + *ptr++ = '/'; + while ( *path2 == '/' ) + path2++; + StringCopyE(ptr,buf+bufsize,path2); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocPP ( ccp path1, ccp path2 ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatPP(buf,sizeof(buf),path1,path2)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufPPE ( char * buf, size_t bufsize, ccp path1, ccp path2, ccp ext ) +{ + // concatenate path + path; returns (by definition) path1 | path2 | buf + + DASSERT(buf); + DASSERT(bufsize); + + char *ptr = path1 ? StringCopyS(buf,bufsize-1,path1) : buf; + *ptr = 0; + + if (path2) + { + if ( ptr > buf && ptr[-1] != '/' ) + *ptr++ = '/'; + while ( *path2 == '/' ) + path2++; + ptr = StringCopyE(ptr,buf+bufsize,path2); + } + + if (ext) + StringCopyE(ptr,buf+bufsize,ext); + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +// PathCatPPE() is inline + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocPPE ( ccp path1, ccp path2, ccp ext ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatPPE(buf,sizeof(buf),path1,path2,ext)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBP ( char *buf, size_t bufsize, ccp base, ccp path ) +{ + // PathCatBP() is special: path can be part of buf + + if (!path) + path = ""; + if ( *path == '/' || base && !*base ) + { + // already absolute | no base to add + StringCopyS(buf,bufsize,path); + return buf; + } + + + //--- now copy base or cwd + + while ( path[0] == '.' && path[1] == '/' ) + path += 2; + + char buf2[PATH_MAX]; + StringCopyS(buf2,sizeof(buf2),path); + + char *dest; + if (base) + dest = StringCopyS(buf,bufsize,base); + else + { + char *cwd = getcwd(buf,bufsize); + if (cwd) + dest = buf + strlen(buf); + else + { + // on error: return fallback + dest = StringCopyS(buf,bufsize,"./"); + } + } + StringCat2E(dest,buf+bufsize,"/",buf2); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBP ( char *buf, size_t bufsize, ccp base, ccp path ) +{ + // PathCatBP() is special: path can be part of buf + + if ( path && *path == '/' || base && !*base ) + return path; // already absolute | no base to add + + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBP ( ccp base, ccp path ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBP(buf,sizeof(buf),base,path)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBPP ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2 ) +{ + ccp path = PathCatPP(buf,bufsize,path1,path2); + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBPP ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2 ) +{ + ccp path = PathCatPP(buf,bufsize,path1,path2); + return PathCatBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBPP ( ccp base, ccp path1, ccp path2 ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBPP(buf,sizeof(buf),base,path1,path2)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCatBufBPPE ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ) +{ + ccp path = PathCatPPE(buf,bufsize,path1,path2,ext); + return PathCatBufBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathCatBPPE ( char * buf, size_t bufsize, ccp base, ccp path1, ccp path2, ccp ext ) +{ + ccp path = PathCatPPE(buf,bufsize,path1,path2,ext); + return PathCatBP(buf,bufsize,base,path); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PathAllocBPPE ( ccp base, ccp path1, ccp path2, ccp ext ) +{ + char buf[PATH_MAX]; + return STRDUP(PathCatBPPE(buf,sizeof(buf),base,path1,path2,ext)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PathCombine ( char *buf, uint buf_size, ccp path, ccp base_path ) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + + if (!path) + path = ""; + + char *dest = buf; + if ( base_path && *base_path && *path != '/' ) + { + StringCopyS(buf,buf_size,base_path); + char *p = strrchr(buf,'/'); + if (p) + dest = p+1; + } + + StringCopyE(dest,buf+buf_size,path); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PathCombineFast ( char *buf, uint buf_size, ccp path, ccp base_path ) +{ + DASSERT(buf); + DASSERT( buf_size > 10 ); + + if (!path) + path = ""; + + if ( !base_path || !*base_path || *path == '/' ) + return (char*)path; + + StringCopyS(buf,buf_size,base_path); + char *p = strrchr(buf,'/'); + if (!p) + return (char*)path; + + StringCopyE(p+1,buf+buf_size,path); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * NewFileExtE ( char * buf, ccp buf_end, ccp path, ccp ext ) +{ + DASSERT( buf); + DASSERT( buf_end); + DASSERT( buf_end > buf ); + + char * end = path && path != buf + ? StringCopyE(buf,buf_end-1,path) + : buf + strlen(buf); + DASSERT( end < buf_end ); + + char * ptr = end; + while ( ptr > buf && *--ptr != '/' ) + if ( *ptr == '.' ) + { + end = ptr; + break; + } + + if ( ext && *ext ) + { + if ( *ext != '.' ) + *end++ = '.'; + end = StringCopyE(end,buf_end,ext); + } + + *end = 0; + return end; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * NewFileExtS ( char * buf, size_t bufsize, ccp path, ccp ext ) +{ + return NewFileExtE(buf,buf+bufsize,path,ext); +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrPathCmp ( ccp path1, ccp path2 ) +{ + if ( path1[0] == '.' && path1[1] == '/' ) + path1 += 2; + if ( path2[0] == '.' && path2[1] == '/' ) + path2 += 2; + return strcmp(path1,path2); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int PathCmp +( + ccp path1, // NULL or first path + ccp path2, // NULL or second path + uint mode // bit field: + // 1: skip (multiple) leading './' + // 2: compare case insensitive + // 4: sort with respect for unsigned decimal numbers +) +{ + if (!path2) + return path1 != 0; + if (!path1) + return -1; + + noPRINT("PathCmp( | %s | %s | %u )\n",path1,path2,mode); + + //--- skip './' + + if ( mode & 1 ) + { + while ( path1[0] == '.' && path1[1] == '/' ) + path1 += 2; + while ( path2[0] == '.' && path2[1] == '/' ) + path2 += 2; + } + + + //--- eliminate equal characters + + while ( *path1 == *path2 ) + { + if (!*path1) + return 0; + path1++; + path2++; + } + + + //--- start the case+path test + + ccp p1 = path1; + ccp p2 = path2; + + const uint ignore_case = mode & 2; + + while ( *p1 || *p2 ) + { + int ch1, ch2; + if (ignore_case) + { + ch1 = (uchar)tolower((int)*p1++); + ch2 = (uchar)tolower((int)*p2++); + } + else + { + ch1 = (uchar)*p1++; + ch2 = (uchar)*p2++; + } + int stat = ch1 - ch2; + noPRINT("%02x,%02x,%c %02x,%02x,%c -> %2d\n", + p1[-1], ch1, ch1, p2[-1], ch2, ch2, stat ); + + if (stat) + { + if ( mode & 4 ) + { + p1--; + while ( *p1 == '0' ) + p1++; + ccp q1 = p1; + while ( *q1 >= '0' && *q1 <= '9' ) + q1++; + const int len1 = q1 - p1; + + if (len1) + { + p2--; + while ( *p2 == '0' ) + p2++; + ccp q2 = p2; + while ( *q2 >= '0' && *q2 <= '9' ) + q2++; + const int len2 = q2 - p2; + + if (len2) + { + int stat2 = len1 - len2; + if (!stat2) + stat2 = memcmp(p1,p2,len1); + if (stat2) + return stat2; + } + } + } + return ch1 == '/' ? -1 : ch2 == '/' ? 1 : stat; + } + + if ( ch1 == '/' ) + break; + } + + return strcmp(path1,path2); +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrNumCmp ( ccp a, ccp b ) +{ + if (!b) + return a != 0; + if (!a) + return -1; + + for(;;) + { + const int va = *a++ & 0xff; + const int vb = *b++ & 0xff; + const int delta = va - vb; + + if (delta) + { + if ( va < '0' || va > '9' || vb < '0' || vb > '9' ) + return delta; // no number + + //--- both are numbers + + a--; + while ( *a == '0' ) + a++; + ccp start_a = a; + while ( *a >= '0' && *a <= '9' ) + a++; + + b--; + while ( *b == '0' ) + b++; + ccp start_b = b; + while ( *b >= '0' && *b <= '9' ) + b++; + + // compare num length + int num_delta = ( a - start_a ) - ( b - start_b ); + if (num_delta) + return num_delta; + + // compare numeric values + while ( start_a < a ) + { + num_delta = *start_a++ - *start_b++; + if (num_delta) + return num_delta; + } + + // number identical, but not string + return delta; + } + else if (!va) + return 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CountEqual ( cvp m1, cvp m2, uint size ) +{ + const u8 *p1 = m1; + const u8 *p2 = m2; + while ( size-- ) + if ( *p1++ != *p2++ ) + return p1 - (u8*)m1 - 1; + return p1 - (u8*)m1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ReplaceToBuf +( + // returns -1 on error (buf to small) or the length of the resulting string. + // In the later case, a 0 byte is appended, but not counting for the length. + + char *buf, // pointer to buf + uint buf_size, // size of 'buf' + + ccp src1, // NULL or source, maybe part of 'buf' + int src1_len, // length of 'src1', if -1: determine by strlen() + int pos, // robust replace position, <0: relative to end + int rm_len, // robust num of chars to remove, <0: rm before 'pos' + + ccp src2, // NULL or source to insert, must not be part of 'buf' + int src2_len // lengh of 'src2', if -1: determine by strlen() +) +{ + DASSERT(buf); + + //--- normalize source #2 + + if (!src2) + src2_len = 0; + else if ( src2_len < 0 ) + src2_len = strlen(src2); + + + //--- if source #1 available + + if (src1) + { + //--- check src1 length + + if ( src1_len < 0 ) + src1_len = strlen(src1); + + if (!src1_len) + goto no_src1; + + + //--- normalize 'pos' + + if ( pos < 0 ) + { + pos += src1_len; + if ( pos < 0 ) + { + if ( rm_len > 0 ) + { + rm_len += pos; + if ( rm_len < 0 ) + rm_len = 0; + } + pos = 0; + } + } + else if ( pos > src1_len ) + pos = src1_len; + + + //--- normalize 'rm_len' + + if ( rm_len < 0 ) + { + rm_len = -rm_len; + if ( rm_len < pos ) + pos -= rm_len; + else + { + rm_len = pos; + pos = 0; + } + } + else + { + const int max = src1_len - pos; + if ( rm_len > max ) + rm_len = max; + } + + const int res_len = src1_len + src2_len - rm_len; + if ( res_len >= buf_size ) + return -1; + + + //--- copy src1 + + if ( src1 > buf ) + { + memmove( buf, src1, pos ); + const int pos2 = pos + rm_len; + memmove( buf+pos+src2_len, src1+pos2, src1_len-pos2 ); + } + else + { + const int pos2 = pos + rm_len; + memmove( buf+pos+src2_len, src1+pos2, src1_len-pos2 ); + memmove( buf, src1, pos ); + } + + + //--- copy src2 & terminate + + memcpy( buf+pos, src2, src2_len ); + buf[res_len] = 0; + return res_len; + } + + no_src1: + if ( src2_len < buf_size ) + { + memcpy(buf,src2,src2_len); + buf[src2_len] = 0; + return src2_len; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int NormalizeIndent ( int indent ) +{ + return indent < 0 ? 0 : indent < 50 ? indent : 50; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls ( ccp src ) +{ + // skips all character 1..SPACE + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE ( ccp src, ccp end ) +{ + // skips all character 0..SPACE + // if end==NULL: Use SkipControls() + // 'src' can be NULL + + if (!end) + return SkipControls(src); + + if (src) + while ( src < end && (uchar)*src <= ' ' ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls1 ( ccp src, char ch1 ) +{ + // skips all character 1..SPACE and 'ch1' + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' || *src == ch1 ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE1 ( ccp src, ccp end, char ch1 ) +{ + // skips all character 0..SPACE and 'ch1' + // if end==NULL: Use SkipControls1() + // 'src' can be NULL + + if (!end) + return SkipControls1(src,ch1); + + if (src) + while ( src < end && ( (uchar)*src <= ' ' || *src == ch1 )) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControls2 ( ccp src, char ch1, char ch2 ) +{ + // skips all character 1..SPACE and 'ch1' and 'ch2' + // 'src' can be NULL + + if (src) + while ( *src > 0 && *src <= ' ' || *src == ch1 || *src == ch2 ) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * SkipControlsE2 ( ccp src, ccp end, char ch1, char ch2 ) +{ + // skips all character 0..SPACE and 'ch1' and 'ch2' + // if end==NULL: Use SkipControls2() + // 'src' can be NULL + + if (!end) + return SkipControls2(src,ch1,ch2); + + if (src) + while ( src < end && ( (uchar)*src <= ' ' || *src == ch1 || *src == ch2 )) + src++; + + return (char*)src; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// return the number of chars until first EOL ( \0 | \r | \n ); +// pointer are NULL save + +uint LineLen ( ccp ptr ) +{ + if (!ptr) + return 0; + + ccp p = ptr; + while ( *p && *p != '\n' && *p != '\r' ) + p++; + return p - ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint LineLenE ( ccp ptr, ccp end ) +{ + if (!ptr) + return 0; + if (!end) + return LineLen(ptr); + + ccp p = ptr; + while ( p < end && *p && *p != '\n' && *p != '\r' ) + p++; + return p - ptr; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint TrimBlanks +( + char *str, // pointer to string, modified + ccp end // NULL or end of string +) +{ + char *src = str; + char *dest = str; + + bool space = false; + while ( end ? src < end : *src ) + { + DASSERT( dest <= src ); + uchar ch = *src++; + if ( ch > ' ' ) + { + if (space) + { + space = false; + if ( dest > str ) + *dest++ = ' '; + } + *dest++ = ch; + } + else if ( ch == ' ' ) + space = true; + } + + if ( !end || dest < end ) + *dest = 0; + + return dest - str; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanName +( + // return a pointer to the first unused character + char *buf, // destination buffer, terminated with NULL + uint buf_size, // size of 'buf', must be >0 + ccp str, // source string + ccp end, // NULL or end of 'str' + bool allow_signs // true: allow '-' and '+' +) +{ + DASSERT(buf); + DASSERT(buf_size); + if (!str) + { + *buf = 0; + return 0; + } + + //--- skip leading blanks + + if (end) + while ( str < end && (uchar)*str <= ' ' ) + str++; + else + while ( str < end && *str && (uchar)*str <= ' ' ) + str++; + + char *dest = buf, *dest_end = buf + buf_size - 1; + while ( end ? str < end : *str ) + { + const int ch = *str; + if ( isalnum(ch) || ch == '_' || allow_signs && ( ch =='-' || ch == '+' ) ) + { + str++; + if ( dest < dest_end ) + *dest++ = toupper(ch); + } + else + break; + } + *dest = 0; + + if (end) + while ( str < end && (uchar)*str <= ' ' ) + str++; + else + while ( str < end && *str && (uchar)*str <= ' ' ) + str++; + + return (char*)str; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * PrintVersion +( + char * buf, // result buffer + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 version // version number to print +) +{ + if (!buf) + buf = GetCircBuf( buf_size = 20 ); + + version = htonl(version); // we use big endian here + const u8 * v = (const u8 *)&version; + + if (v[2]) + { + if ( !v[3] || v[3] == 0xff ) + snprintf(buf,buf_size,"%u.%02x.%02x",v[0],v[1],v[2]); + else + snprintf(buf,buf_size,"%u.%02x.%02x.beta%u",v[0],v[1],v[2],v[3]); + } + else + { + if ( !v[3] || v[3] == 0xff ) + snprintf(buf,buf_size,"%u.%02x",v[0],v[1]); + else + snprintf(buf,buf_size,"%u.%02x.beta%u",v[0],v[1],v[3]); + } + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintID +( + const void * id, // ID to convert in printable format + size_t id_len, // len of 'id' + void * buf // Pointer to buffer, size >= id_len + 1 + // If NULL, a local circulary static buffer is used +) +{ + if (!buf) + buf = GetCircBuf( id_len + 1); + + ccp src = id; + char * dest = buf; + while ( id_len-- > 0 ) + { + const char ch = *src++; + *dest++ = ch >= ' ' && ch < 0x7f ? ch : '.'; + } + *dest = 0; + + return buf; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// circ buf /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char circ_buf[CIRC_BUF_SIZE]; +static char *circ_ptr = circ_buf; + +/////////////////////////////////////////////////////////////////////////////// + +char * GetCircBuf +( + // never returns NULL, but always ALIGN(4) + + u32 buf_size // wanted buffer size, caller must add 1 for NULL-term + // if buf_size > CIRC_BUF_MAX_ALLOC: + // ==> ERROR0(ERR_OUT_OF_MEMORY) +) +{ + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + buf_size = buf_size + 3 & ~3; + if ( buf_size > CIRC_BUF_MAX_ALLOC ) + { + ERROR0(ERR_OUT_OF_MEMORY, + "Circulary buffer too small: needed=%u, half-size=%zu\n", + buf_size, sizeof(circ_buf)/2 ); + ASSERT(0); + exit(ERR_OUT_OF_MEMORY); + } + + if ( circ_buf + sizeof(circ_buf) - circ_ptr < buf_size ) + circ_ptr = circ_buf; + + char *result = circ_ptr; + circ_ptr = result + buf_size; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + noPRINT("CIRC-BUF: %3u -> %3zu / %3zu / %3zu\n", + buf_size, result-circ_buf, circ_ptr-circ_buf, sizeof(circ_buf) ); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * CopyCircBuf +( + // never returns NULL, but always ALIGN(4) + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +) +{ + char *dest = GetCircBuf(data_size); + memcpy(dest,data,data_size); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * CopyCircBuf0 +( + // never returns NULL, but always ALIGN(4) + // an additional char is alloced and set to NULL + + cvp data, // source to copy + u32 data_size // see GetCircBuf() +) +{ + char *dest = GetCircBuf(data_size+1); + memcpy(dest,data,data_size); + dest[data_size] = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintCircBuf +( + ccp format, // format string for vsnprintf() + ... // arguments for 'format' +) +{ + char buf[CIRC_BUF_MAX_ALLOC-1]; + va_list arg; + va_start(arg,format); + const int len = vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + + return len > 0 ? CopyCircBuf0(buf,len) : (char*)EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ReleaseCircBuf +( + ccp end_buf, // pointer to end of previous alloced buffer + uint release_size // number of bytes to give back from end +) +{ + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + + if ( end_buf == circ_ptr + && release_size <= CIRC_BUF_MAX_ALLOC + && circ_ptr >= circ_buf + release_size ) + { + circ_ptr -= release_size; + DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct FastBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +FastBuf_t * InitializeFastBuf ( cvp mem, uint size ) +{ + DASSERT(mem); + DASSERT( size >= sizeof(FastBuf_t) ); + + FastBuf_t *fb = (FastBuf_t*)mem; + + int buf_size = (int)size - offsetof(FastBuf_t,fast_buf); + if ( buf_size < sizeof(fb->fast_buf) ) + { + PrintError(__FUNCTION__,__FILE__,__LINE__,0,ERR_OUT_OF_MEMORY, + "Out of memory, FastBuf_t is at least %zd bytes to short.", + sizeof(fb->fast_buf) - size ); + ASSERT(0); + return 0; + } + + fb->fast_buf_size = buf_size; + fb->buf = fb->ptr = fb->fast_buf; + fb->end = fb->buf + buf_size - 1; + DASSERT( fb->buf < fb->end ); + return fb; +} + +//----------------------------------------------------------------------------- + +FastBuf_t * InitializeFastBufAlloc ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + InitializeFastBuf(fb,sizeof(*fb)); + + const uint new_size = GetGoodAllocSize(size+10); + fb->buf = MALLOC(new_size); + fb->ptr = fb->buf; + fb->end = fb->buf + new_size - 1; + return fb; +} + +/////////////////////////////////////////////////////////////////////////////// + +#define RESET_FASTBUF(fb) fb->buf = fb->ptr = fb->fast_buf; \ + fb->end = fb->buf + fb->fast_buf_size - 1; + +void ResetFastBuf ( FastBuf_t *fb ) +{ + DASSERT(fb); + if ( fb->buf != fb->fast_buf ) + FREE(fb->buf); + + RESET_FASTBUF(fb) +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetFastBufStatus ( const FastBuf_t *fb ) +{ + char info[100]; + int len = snprintf(info,sizeof(info),"%s=%u/%u", + fb->buf == fb->fast_buf ? "FastBuf" : "Alloc", + (uint)(fb->ptr - fb->buf), (uint)(fb->end - fb->buf) ); + return CopyCircBuf(info,len+1); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * MoveFromFastBufString ( FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + + char *res; + if ( fb->buf == fb->fast_buf ) + { + res = MEMDUP(fb->buf,fb->ptr-fb->buf); + fb->ptr = fb->buf; + } + else + { + res = fb->buf; + RESET_FASTBUF(fb) + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MoveFromFastBufMem ( FastBuf_t *fb ) +{ + DASSERT(fb); + *fb->ptr = 0; + + mem_t res; + res.len = fb->ptr - fb->buf; + + if ( fb->buf == fb->fast_buf ) + { + res.ptr = MEMDUP(fb->buf,res.len); + fb->ptr = fb->buf; + } + else + { + res.ptr = fb->buf; + RESET_FASTBUF(fb) + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * GetSpaceFastBuf ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + if ( size > fb->end - fb->ptr ) + { + const uint used = fb->ptr - fb->buf; + const uint new_size = GetGoodAllocSize( used + used/4 + size + 1000 ); + if ( fb->buf == fb->fast_buf ) + { + fb->buf = MALLOC(new_size); + memcpy(fb->buf,fb->fast_buf,used); + } + else + fb->buf = REALLOC(fb->buf,new_size); + fb->ptr = fb->buf + used; + fb->end = fb->buf + new_size - 1; + } + + char *res = fb->ptr; + fb->ptr += size; + return res; +} + +//----------------------------------------------------------------------------- + +int ReserveSpaceFastBuf ( FastBuf_t *fb, uint size ) +{ + DASSERT(fb); + if ( size > 0 ) + fb->ptr = GetSpaceFastBuf(fb,size); + return fb->end - fb->ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertFastBuf ( FastBuf_t *fb, int index, cvp source, int size ) +{ + DASSERT(fb); + DASSERT(source||!size); + + if ( size < 0 ) + size = strlen(source); + + const uint len = fb->ptr - fb->buf; + index = CheckIndex1(len,index); + noPRINT("index=%d/%d/%zd, size=%d\n",index,len,fb->end-fb->buf,size); + + char *ptr = GetSpaceFastBuf(fb,size); + char *dest = fb->buf + index; + DASSERT( dest >= fb->buf ); + DASSERT( dest + size + len <= fb->end ); + + memmove(dest+size,dest,ptr-dest); + memcpy(dest,source,size); + return index; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * AppendFastBuf ( FastBuf_t *fb, cvp source, int size ) +{ + DASSERT(fb); + DASSERT(source||!size); + + if ( size < 0 ) + size = strlen(source); + + char *dest = GetSpaceFastBuf(fb,size); + memcpy(dest,source,size); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendUTF8CharFastBuf ( FastBuf_t *fb, u32 code ) +{ + char buf[8]; + const uint len = PrintUTF8Char(buf,code) - buf; + + char *dest = GetSpaceFastBuf(fb,len); + memcpy(dest,buf,len); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendBE16FastBuf ( FastBuf_t *fb, u16 num ) +{ + DASSERT(fb); + + char *dest = GetSpaceFastBuf(fb,sizeof(num)); + write_be16(dest,num); +} + +//----------------------------------------------------------------------------- + +void AppendBE32FastBuf ( FastBuf_t *fb, u32 num ) +{ + DASSERT(fb); + + char *dest = GetSpaceFastBuf(fb,sizeof(num)); + write_be32(dest,num); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendInt64FastBuf ( FastBuf_t *fb, u64 val, IntMode_t mode ) +{ + DASSERT(fb); + + switch(mode) + { + case IMD_UNSET: + AppendFastBuf(fb,&val,sizeof(val)); + break; + + case IMD_BE1: + case IMD_LE1: AppendCharFastBuf(fb,val); break; + + case IMD_BE2: write_be16(GetSpaceFastBuf(fb,2),val); break; + case IMD_BE3: write_be24(GetSpaceFastBuf(fb,3),val); break; + case IMD_BE4: write_be32(GetSpaceFastBuf(fb,4),val); break; + case IMD_BE5: write_be40(GetSpaceFastBuf(fb,5),val); break; + case IMD_BE6: write_be48(GetSpaceFastBuf(fb,6),val); break; + case IMD_BE7: write_be56(GetSpaceFastBuf(fb,7),val); break; + case IMD_BE0: + case IMD_BE8: write_be64(GetSpaceFastBuf(fb,8),val); break; + + case IMD_LE2: write_le16(GetSpaceFastBuf(fb,2),val); break; + case IMD_LE3: write_le24(GetSpaceFastBuf(fb,3),val); break; + case IMD_LE4: write_le32(GetSpaceFastBuf(fb,4),val); break; + case IMD_LE5: write_le40(GetSpaceFastBuf(fb,5),val); break; + case IMD_LE6: write_le48(GetSpaceFastBuf(fb,6),val); break; + case IMD_LE7: write_le56(GetSpaceFastBuf(fb,7),val); break; + case IMD_LE0: + case IMD_LE8: write_le64(GetSpaceFastBuf(fb,8),val); break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +char * WriteFastBuf ( FastBuf_t *fb, uint offset, cvp source, int size ) +{ + DASSERT(fb); + + uint oldlen = fb->ptr - fb->buf; + uint minlen = offset + size; + if ( minlen > oldlen ) + { + char *ptr = GetSpaceFastBuf(fb,minlen-oldlen); + memset(ptr,0,minlen-oldlen); + } + DASSERT( fb->ptr - fb->buf >= minlen ); + char *dest = fb->buf + offset; + if (source) + memcpy( dest, source, size ); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AssignFastBuf ( FastBuf_t *fb, cvp source, int size ) +{ + DASSERT(fb); + + if ( size < 0 ) + size = strlen(source); + + if ( source == fb->buf ) + { + const uint used = fb->ptr - fb->buf; + if ( size < used ) + fb->ptr = fb->buf + size; + else + size = used; + } + else + { + fb->ptr = fb->buf; + if (source) + AppendFastBuf(fb,source,size); + *fb->ptr = 0; + } + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropFastBuf ( FastBuf_t *fb, int index, int size ) +{ + DASSERT(fb); + if (size) + { + const uint len = fb->ptr - fb->buf; + index = CheckIndex1(len,index); + if ( size < 0 ) + { + size = -size; + if ( size > index ) + size = index; + index -= size; + } + int index2 = CheckIndex1(len,index+size); + size = index2 - index; + if (size) + { + memmove(fb->buf+index,fb->buf+index2,len-index2+1); + fb->ptr -= size; + } + } + + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CopyFastBuf ( FastBuf_t *dest, const FastBuf_t *src ) +{ + if ( dest && dest != src ) + { + if (!src) + ClearFastBuf(dest); + else + AssignFastBuf( dest, src->buf, src->ptr - src->buf ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveFastBuf ( FastBuf_t *dest, FastBuf_t *src ) +{ + if ( dest && src && dest != src ) + { + if ( src->buf != src->fast_buf ) + { + // src is allocated + if ( dest->buf != dest->fast_buf ) + FREE(dest->buf); + dest->buf = src->buf; + dest->ptr = src->ptr; + dest->end = src->end; + InitializeFastBuf( src, offsetof(FastBuf_t,fast_buf) + src->fast_buf_size ); + } + else + AssignFastBuf( dest, src->buf, src->ptr - src->buf ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// alloc info /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool alloc_info_calced = false; +uint alloc_info_overhead = 8; // default values +uint alloc_info_block_size = 16; +uint alloc_info_add1 = 7; +uint alloc_info_add2 = 8; +uint alloc_info_mask = ~15; + +/////////////////////////////////////////////////////////////////////////////// + +uint SetupAllocInfo() +{ + if (!alloc_info_calced) + { + alloc_info_calced = true; + + enum { MAX = 200 }; + + char *alloc[MAX+1]; + memset(alloc,0,sizeof(alloc)); + + uint i, mask_found = ~0; + for ( i = 1; i <= MAX; i++ ) + { + alloc[i] = (char*)MALLOC(i); + noPRINT("%4x: %p [%zx]\n",i,alloc[i],alloc[i]-alloc[ i>0 ? i-1 : 0 ] ); + if ( i > 1 ) + { + int diff = abs ( alloc[i-1] - alloc[i] ); + mask_found &= diff - 1; + } + } + const int lo0 = FindLowest0Bit(&mask_found,1); + const int hi1 = FindHighest1Bit(&mask_found,1); + PRINT("mask: %08x, lo0=%d, hi1=%d\n",mask_found,lo0,hi1); + + if ( hi1 + 1 == lo0 ) + { + // -> maske besteht aus hi1 '1' gefolgt von ausnahmslos '0' + + alloc_info_mask = ~mask_found; + alloc_info_block_size = mask_found + 1; + PRINT("alloc_info_block_size = %d, alloc_info_mask = 0x%x\n", + alloc_info_block_size, alloc_info_mask ); + + int found = 0x100; + for ( i = 2; i <= MAX; i++ ) + { + const int diff = abs ( alloc[i-1] - alloc[i] ) - i; + if ( diff >= 0 && found > diff ) + found = diff; + } + + if ( found < MAX ) + { + alloc_info_overhead = found; + found %= alloc_info_block_size; + alloc_info_add1 = found - 1; + alloc_info_add2 = alloc_info_block_size - found; + PRINT("alloc_info_overhead = %d, alloc_info_add = %d, %d\n", + alloc_info_overhead, alloc_info_add1, alloc_info_add2 ); + } + } + + for ( i = 1; i <= MAX; i++ ) + FREE(alloc[i]); + } + + return alloc_info_block_size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// SplitArg_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetSplitArg ( SplitArg_t *sa ) +{ + DASSERT(sa); + FREE(sa->argv); + FREE(sa->temp); + InitializeSplitArg(sa); +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanSplitArg +( + // returns arg->argc; + + SplitArg_t *arg, // pointer to object + bool init_arg, // true: call InitializeSplitArg() + ccp src, // NULL or source string + ccp src_end, // end of 'src'; if NULL: calculated by strlen(src) + char *work_buf // pointer to a buffer for strings, may be 'src' + // size must be: length of 'src' + 1 + // if NULL, a temporary buffer is alloced. +) +{ + DASSERT(arg); + if (init_arg) + InitializeSplitArg(arg); + else + ResetSplitArg(arg); + + if (!src) + src = EmptyString; + if (!src_end) + src_end = src + strlen(src); + + #ifdef TEST0 + enum { N_PREDEF = 5 }; + #else + enum { N_PREDEF = 250 }; + #endif + char *argv_predef[N_PREDEF+1], + **argv_buf = argv_predef, + **argv_dest = argv_predef, + **argv_end = argv_predef + N_PREDEF; + *argv_dest++ = progname ? (char*)progname : ""; + uint argv_size = 0; + + if (!work_buf) + { + arg->temp_size = src_end - src + 1; + arg->temp = work_buf = MALLOC(arg->temp_size); + } + char *dest = work_buf; + + + //--- main loop + + for(;;) + { + while ( src < src_end + && ( !*src || *src == ' ' || *src == '\t' || *src == '\r' || *src == '\n' )) + { + src++; + } + if ( src == src_end ) + break; + + if ( argv_dest == argv_end ) + { + PRINT("NEED MORE: %zu/%zu size=%u: |%s|\n", + argv_dest-argv_buf, argv_end-argv_buf, argv_size, src ); + const uint idx = argv_dest - argv_buf; + if (argv_size) + { + argv_size += N_PREDEF; + argv_buf = REALLOC(argv_buf,(argv_size+1)*sizeof(*argv_buf)); + } + else + { + argv_size = 2*N_PREDEF; + char **temp = MALLOC((argv_size+1)*sizeof(*argv_buf)); + memcpy(temp,argv_buf,idx*sizeof(*argv_buf)); + argv_buf = temp; + } + argv_dest = argv_buf + idx; + argv_end = argv_buf + argv_size; + } + + *argv_dest++ = dest; + while ( src < src_end ) + { + const char ch = *src; + if ( !ch || ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' ) + break; + src++; + if ( ch == '\\' && src < src_end ) + *dest++ = *src++; + else if ( ch == '\'' ) + { + while ( src < src_end && *src != '\'' ) + *dest++ = *src++; + if ( src < src_end ) + src++; + } + else if ( ch == '"' ) + { + while ( src < src_end && *src != '"' ) + { + char ch = *src++; + if ( ch == '\\' ) + { + uint code; + src = ScanEscape(&code,src,src_end); + ch = code; + } + *dest++ = ch; + } + if ( src < src_end ) + src++; + } + else + *dest++ = ch; + } + *dest++ = 0; + } + + *argv_dest = 0; + arg->argc = argv_dest - argv_buf; + if ( argv_size ) + { + DASSERT( argv_buf != argv_predef ); + arg->argv_size = argv_size; + arg->argv = argv_buf; + } + else + { + DASSERT( argv_buf == argv_predef ); + arg->argv_size = arg->argc; + arg->argv = MEMDUP(argv_buf,(arg->argv_size+1)*sizeof(*argv_dest)); + } + + return arg->argc; +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +mem_t EmptyMem = {EmptyString,0}; +mem_t NullMem = {0,0}; + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MidMem ( const mem_t src, int begin, int count ) +{ + begin = CheckIndex1(src.len,begin); + if ( count < 0 ) + { + count = -count; + begin -= count; + if ( begin < 0 ) + { + count += begin; + begin = 0; + } + } + + mem_t res; + res.ptr = src.ptr + begin; + const int max = src.len - begin; + res.len = count < max ? count : max; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t BeforeMem ( const mem_t src, ccp ref ) +{ + mem_t res; + res.ptr = src.ptr; + res.len = ref < src.ptr ? 0 + : ref <= src.ptr + src.len ? ref - src.ptr + : src.len; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t BehindMem ( const mem_t src, ccp ref ) +{ + mem_t res; + if ( ref <= src.ptr ) + res = src; + else if ( ref <= src.ptr + src.len ) + { + res.ptr = ref; + res.len = src.ptr + src.len - ref; + } + else + { + res.ptr = src.ptr + src.len; + res.len = 0; + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +int CmpMem ( const mem_t s1, const mem_t s2 ) +{ + if ( s1.len < s2.len ) + { + const int stat = memcmp(s1.ptr,s2.ptr,s1.len); + return stat ? stat : -1; + } + + const int stat = memcmp(s1.ptr,s2.ptr,s2.len); + return stat ? stat : s1.len > s2.len; +} + +/////////////////////////////////////////////////////////////////////////////// + +int StrCmpMem ( const mem_t mem, ccp str ) +{ + const uint slen = str ? strlen(str) : 0; + + if ( mem.len < slen ) + { + const int stat = memcmp(mem.ptr,str,mem.len); + return stat ? stat : -1; + } + + const int stat = memcmp(mem.ptr,str,slen); + return stat ? stat : mem.len > slen; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool LeftStrCmpMemEQ ( const mem_t mem, ccp str ) +{ + const uint slen = str ? strlen(str) : 0; + return mem.len <= slen && !memcmp(mem.ptr,str,mem.len); +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t MemCat2A ( const mem_t m1, const mem_t m2 ) +{ + const uint len1 = m1.len >= 0 ? m1.len : strlen(m1.ptr); + const uint len2 = m2.len >= 0 ? m2.len : strlen(m2.ptr); + + char *dest; + mem_t res; + res.len = len1 + len2; + res.ptr = dest = MALLOC(res.len+1); + dest[res.len] = 0; + + if (len1) + memcpy(dest,m1.ptr,len1); + if (len2) + memcpy(dest+len1,m2.ptr,len2); + + return res; +} + +//----------------------------------------------------------------------------- + +mem_t MemCat3A ( const mem_t m1, const mem_t m2, const mem_t m3 ) +{ + const uint len1 = m1.len >= 0 ? m1.len : strlen(m1.ptr); + const uint len2 = m2.len >= 0 ? m2.len : strlen(m2.ptr); + const uint len3 = m3.len >= 0 ? m3.len : strlen(m3.ptr); + + char *dest; + mem_t res; + res.len = len1 + len2 + len3; + res.ptr = dest = MALLOC(res.len+1); + dest[res.len] = 0; + + if (len1) + memcpy(dest,m1.ptr,len1); + if (len2) + memcpy(dest+len1,m2.ptr,len2); + if (len3) + memcpy(dest+len1+len2,m3.ptr,len3); + + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct mem_list_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMemList ( mem_list_t *ml ) +{ + DASSERT(ml); + memset(ml,0,sizeof(*ml)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemList ( mem_list_t *ml ) +{ + DASSERT(ml); + FREE(ml->list); + FREE(ml->buf); + memset(ml,0,sizeof(*ml)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveMemList ( mem_list_t *dest, mem_list_t *src ) +{ + if ( dest != src ) + { + if (!dest) + ResetMemList(src); + else + { + ResetMemList(dest); + if (src) + { + memcpy(dest,src,sizeof(*dest)); + memset(src,0,sizeof(*src)); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrowBufMemList ( mem_list_t *ml, uint need_size ) +{ + if ( ml->buf_size < need_size ) + { + PRINT("MEM-LIST: MALLOC/BUF(%u)\n",need_size); + char *new_buf = MALLOC(need_size); + + //--- copy existing strings + + char *bufptr = new_buf; + mem_t *dest = ml->list; + uint i; + for ( i = 0; i < ml->used; i++, dest++ ) + if ( dest->len ) + { + memcpy(bufptr,dest->ptr,dest->len); + dest->ptr = bufptr; + bufptr += dest->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf + need_size ); + noPRINT("OLD: %p,%u\n",dest->ptr,dest->len); + } + FREE(ml->buf); + ml->buf = new_buf; + ml->buf_used = bufptr - new_buf; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrepareMemList ( mem_list_t *ml, uint n_elem, uint buf_size ) +{ + DASSERT(ml); + if ( ml->size < n_elem ) + { + ml->size = n_elem; + PRINT("MEM-LIST: REALLOC/LIST(%u)\n",ml->size); + ml->list = REALLOC(ml->list,sizeof(*ml->list)*ml->size); + } + + GrowBufMemList(ml,buf_size); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InsertMemListN +( + mem_list_t *ml, // valid destination mem_list + int pos, // insert position => CheckIndex1() + const mem_t *src, // source list + uint src_len, // number of elements in source list + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(ml); + DASSERT( src || !src_len ); + + //--- count needed size and elements + + const mem_t *sptr, *src_end = src + src_len; + + uint need_size = 0, add_elem = 0; + for ( sptr = src; sptr < src_end; sptr++ ) + { + if ( sptr->len ) + { + need_size += sptr->len + 1; // always terminate with NULL + add_elem++; + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr || src_ignore == 3 ) + add_elem++; + } + + if (!add_elem) + return; + + + //--- string pool management + + if (!ml->used) + ml->buf_used = 0; + + char *new_buf, *bufptr; + if ( ml->buf_used + need_size <= ml->buf_size ) + { + new_buf = 0; + bufptr = ml->buf + ml->buf_used; + ml->buf_used += need_size; + } + else + { + need_size += ml->buf_used + 20; + need_size += need_size/10; + + // don't use GrowBufMemList() here, because the old buffer is needed + // BUT: char old_buf = ml->buf; ml->buf = 0; GrowBufMemList(ml,need_size) + + PRINT("MEM-LIST: MALLOC/BUF(%u)\n",need_size); + bufptr = new_buf = MALLOC(need_size); + + //--- copy existing strings + + mem_t *dest = ml->list; + uint i; + for ( i = 0; i < ml->used; i++, dest++ ) + if ( dest->len ) + { + memcpy(bufptr,dest->ptr,dest->len); + dest->ptr = bufptr; + bufptr += dest->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf+need_size ); + noPRINT("OLD: %p,%u\n",dest->ptr,dest->len); + } + } + + + //--- realloc list if necessary + + const uint need_elem = ml->used + add_elem; + if ( need_elem > ml->size ) + { + ml->size = need_elem + 5 + need_elem/10; + PRINT("MEM-LIST: REALLOC/LIST(%u)\n",ml->size); + ml->list = REALLOC(ml->list,sizeof(*ml->list)*ml->size); + } + + pos = CheckIndex1(ml->used,pos); + mem_t *dest = ml->list + pos; + if ( pos < ml->used ) + memmove(dest+add_elem,dest,sizeof(*dest)*(ml->used-pos)); + ml->used += add_elem; + + + //--- iterate source list + + for ( sptr = src; sptr < src_end; sptr++ ) + { + if (sptr->len) + { + dest->ptr = bufptr; + memcpy(bufptr,sptr->ptr,sptr->len); + dest->len = sptr->len; + bufptr += sptr->len; + *bufptr++ = 0; + DASSERT( bufptr <= ( new_buf ? new_buf+need_size : ml->buf + ml->buf_size ) ); + noPRINT("NEW: %p,%u\n",dest->ptr,dest->len); + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr ) + { + dest->ptr = sptr->ptr ? EmptyString : NULL; + dest->len = 0; + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + else if ( src_ignore == 3 ) + { + dest->ptr = EmptyString; + dest->len = 0; + dest++; + DASSERT( dest <= ml->list + ml->used ); + } + } + DASSERT_MSG( dest == ml->list + pos + add_elem, + "DEST= %zu != %u+%u == %u", + dest-ml->list, pos, add_elem, pos+add_elem ); + + + //--- clean + + if (new_buf) + { + DASSERT(ml->buf != new_buf); + FREE(ml->buf); + ml->buf = new_buf; + ml->buf_size = need_size; + } + ml->buf_used = bufptr - ml->buf; + DASSERT( ml->buf_used <= ml->buf_size ); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void CatMemListN +( + mem_list_t *dest, // valid destination mem_list + const mem_list_t **src_list, // list with mem lists, element may be NULL + uint n_src_list, // number of elements in 'src' + uint src_ignore // 0: insert all + // 1: ignore NULL + // 2: ignore NULL and empty + // 3: replace NULL by EmptyString +) +{ + DASSERT(dest); + DASSERT( src_list || !n_src_list ); + + + //--- count needed size and elements + + uint need_size = 0, need_elem = 0; + bool dest_is_source = false; + + const mem_list_t **list_ptr, **src_list_end = src_list + n_src_list; + for ( list_ptr = src_list; list_ptr < src_list_end; list_ptr++ ) + { + if (!*list_ptr) + continue; + + if ( *list_ptr == dest ) + dest_is_source = true; + + const mem_t *sptr = (*list_ptr)->list, *send = sptr + (*list_ptr)->used; + for ( ; sptr < send; sptr++ ) + { + if ( sptr->len ) + { + need_size += sptr->len + 1; // always terminate with NULL + need_elem++; + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr || src_ignore == 3 ) + need_elem++; + } + } + + PRINT("CatMemList(N=%u) n_elem=%u, size=%u, dest_is_source=%d\n", + n_src_list,need_elem,need_size,dest_is_source); + if (!need_elem) + { + dest->used = 0; + return; + } + + + //--- setup dest buffers + + char *new_buf = dest_is_source || need_size > dest->buf_size + ? MALLOC(need_size) : dest->buf; + mem_t *new_list = dest_is_source || need_elem > dest->size + ? MALLOC(sizeof(*new_list)*need_elem) : dest->list; + + + + //--- assign values + + char *bufptr = new_buf; + mem_t *listptr = new_list; + + for ( list_ptr = src_list; list_ptr < src_list_end; list_ptr++ ) + { + if (!*list_ptr) + continue; + + const mem_t *sptr = (*list_ptr)->list, *send = sptr + (*list_ptr)->used; + for ( ; sptr < send; sptr++ ) + { + if (sptr->len) + { + listptr->ptr = bufptr; + memcpy(bufptr,sptr->ptr,sptr->len); + listptr->len = sptr->len; + bufptr += sptr->len; + *bufptr++ = 0; + DASSERT( bufptr <= new_buf + need_size ); + noPRINT("NEW: %p,%u\n",dest->ptr,dest->len); + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + else if ( !src_ignore || src_ignore == 1 && sptr->ptr ) + { + listptr->ptr = sptr->ptr ? EmptyString : NULL; + listptr->len = 0; + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + else if ( src_ignore == 3 ) + { + listptr->ptr = EmptyString; + listptr->len = 0; + listptr++; + DASSERT( listptr <= new_list + need_elem ); + } + } + } + DASSERT( bufptr == new_buf + need_size ); + DASSERT( listptr == new_list + need_elem ); + + + //--- terminate + + dest->buf_used = need_size; + if ( new_buf != dest->buf ) + { + FREE(dest->buf); + dest->buf = new_buf; + dest->buf_size = need_size; + } + + dest->used = need_elem; + if ( new_list != dest->list ) + { + FREE(dest->list); + dest->list = new_list; + dest->size = need_elem; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint LeftMemList ( mem_list_t *ml, int count ) +{ + DASSERT(ml); + return ml->used = CheckIndex1(ml->used,count); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RightMemList ( mem_list_t *ml, int count ) +{ + DASSERT(ml); + + const uint begin = CheckIndex1End(ml->used,-count); + if (begin) + { + ml->used -= begin; + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + } + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint MidMemList ( mem_list_t *ml, int begin, int count ) +{ + DASSERT(ml); + + begin = CheckIndex1(ml->used,begin); + if ( count < 0 ) + { + count = -count; + begin -= count; + if ( begin < 0 ) + { + count += begin; + begin = 0; + } + } + + const uint max = ml->used - begin; + if ( count > max ) + count = max; + memmove( ml->list, ml->list + begin, count * sizeof(*ml->list) ); + return ml->used = count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ExtractMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + ml->used = CheckIndex2(ml->used,&begin,&end); + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ExtractEndMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + ml->used = CheckIndex2End(ml->used,&begin,&end); + memmove( ml->list, ml->list + begin, ml->used * sizeof(*ml->list) ); + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + const uint sub = CheckIndex2(ml->used,&begin,&end); + memmove( ml->list + begin, ml->list + end, ( ml->used - end ) * sizeof(*ml->list) ); + return ml->used -= sub; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveEndMemList ( mem_list_t *ml, int begin, int end ) +{ + DASSERT(ml); + const uint sub = CheckIndex2End(ml->used,&begin,&end); + memmove( ml->list + begin, ml->list + end, ( ml->used - end ) * sizeof(*ml->list) ); + return ml->used -= sub; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MemPool_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemPool ( MemPool_t *mp ) +{ + DASSERT(mp); + MemPoolChunk_t *ptr = mp->chunk; + while (ptr) + { + MemPoolChunk_t *next = ptr->next; + FREE(ptr); + ptr = next; + } + InitializeMemPool(mp,mp->chunk_size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MallocMemPool ( MemPool_t *mp, uint size ) +{ + DASSERT(mp); + if (!size) + return (char*)EmptyString; + + if ( size > mp->space ) + { + uint data_size = size > mp->chunk_size ? size : mp->chunk_size; + if ( data_size < 0x400 ) + data_size = 0x400; + MemPoolChunk_t *chunk = MALLOC(sizeof(MemPoolChunk_t)+data_size); + DASSERT(chunk); + chunk->next = mp->chunk; + mp->chunk = chunk; + mp->space = data_size; + } + + DASSERT(mp->chunk); + DASSERT(mp->space >= size ); + + mp->space -= size; + return mp->chunk->data + mp->space; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * CallocMemPool ( MemPool_t *mp, uint size ) +{ + DASSERT(mp); + void *res = MallocMemPool(mp,size); + DASSERT(res); + memset(res,0,size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MallocMemPoolA ( MemPool_t *mp, uint size, uint align ) +{ + DASSERT(mp); + char *res = MallocMemPool(mp,size); + DASSERT(res); + const uint fix = mp->space - mp->space / align * align; + res -= fix; + mp->space -= fix; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * CallocMemPoolA ( MemPool_t *mp, uint size, uint align ) +{ + DASSERT(mp); + void *res = MallocMemPoolA(mp,size,align); + DASSERT(res); + memset(res,0,size); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void * MemDupMemPool ( MemPool_t *mp, cvp source, uint size ) +{ + DASSERT(mp); + DASSERT(source||!size); + + char *res = MallocMemPool(mp,size+1); + memcpy(res,source,size); + res[size] = 0; + return res; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// CircBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[CircBuf_t]] + +void InitializeCircBuf ( CircBuf_t *cb, uint size ) +{ + DASSERT(cb); + memset(cb,0,sizeof(*cb)); + cb->size = size; //data is alloced on first insertion +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + FREE(cb->buf); + cb->buf = 0; + cb->used = cb->pos = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint WriteCircBuf ( CircBuf_t *cb, cvp data, uint size ) +{ + DASSERT(cb); + DASSERT(data||!size); + + if ( !size || !cb->size ) + return 0; + + if (!cb->buf) + { + cb->buf = MALLOC(cb->size); + DASSERT(cb->buf); + cb->used = cb->pos = 0; + } + + ccp d = data; + if ( size >= cb->size ) + { + cb->used = cb->size; + cb->pos = 0; + memcpy( cb->buf, d + (size-cb->size), cb->size ); + //fprintf(stderr,"INS*: %d+%d = %d / %d\n",cb->pos,cb->used,cb->pos+cb->used,cb->size); + return cb->size; + } + + const int ins_count = size; + int ins_pos = cb->pos + cb->used; + if ( ins_pos >= cb->size ) + ins_pos -= cb->size; + + if ( ins_pos + size > cb->size ) + { + const uint copy_len = cb->size - ins_pos; + //fprintf(stderr,"COPY %u..%u\n",ins_pos,ins_pos+copy_len); + memcpy( cb->buf + ins_pos, d, copy_len ); + size -= copy_len; + d += copy_len; + ins_pos = 0; + } + + DASSERT( size > 0 ); + DASSERT( ins_pos <= cb->size ); + DASSERT( ins_pos + size <= cb->size ); + //fprintf(stderr,"COPY %u..%u\n",ins_pos,ins_pos+size); + memcpy( cb->buf + ins_pos, d, size ); + + cb->used += ins_count; + if ( cb->used > cb->size ) + { + cb->pos += cb->used - cb->size; + if ( cb->pos >= cb->size ) + cb->pos -= cb->size; + cb->used = cb->size; + } + + //fprintf(stderr,"INS: %d+%d = %d / %d\n",cb->pos,cb->used,cb->pos+cb->used,cb->size); + return ins_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PeakCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ) +{ + DASSERT(cb); + DASSERT(buf||!buf_size); + + if ( !cb->size || !buf_size ) + return 0; + + if ( buf_size > cb->used ) + buf_size = cb->used; + + uint count = cb->size - cb->pos; + if ( count > buf_size ) + count -= buf_size; + + memcpy( buf, cb->buf + cb->pos, count ); + if ( count < buf_size ) + { + const uint copy_len = buf_size - count; + memcpy( buf+count, cb->buf, copy_len ); + count += copy_len; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ReadCircBuf ( CircBuf_t *cb, char *buf, uint buf_size ) +{ + DASSERT(cb); + const uint count = PeakCircBuf(cb,buf,buf_size); + return DropCircBuf(cb,count); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropCircBuf ( CircBuf_t *cb, uint size ) +{ + DASSERT(cb); + + if ( size >= cb->used ) + { + const uint res = cb->used; + cb->pos = cb->used = 0; + return res; + } + + cb->used -= size; + cb->pos += size; + if ( cb->pos >= cb->size ) + cb->pos -= cb->size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + cb->used = cb->pos = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PurgeCircBuf ( CircBuf_t *cb ) +{ + DASSERT(cb); + + if ( cb->used && cb->pos ) + { + char *temp = MALLOC(cb->size); + PeakCircBuf(cb,temp,cb->used); + FREE(cb->buf); + cb->buf = temp; + } + cb->pos = 0; + return cb->used; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum CopyMode_t interface /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * CopyData +( + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode, // copy mode + bool *res_alloced // not NULL: + // store true, if data must be freed + // otherwise store false +) +{ + DASSERT( data || !size ); + if ( !data || !size ) + { + if (res_alloced) + *res_alloced = false; + return (void*)EmptyString; + } + + if (res_alloced) + *res_alloced = mode != CPM_LINK; + + return mode == CPM_MOVE || mode == CPM_LINK + ? (void*)data + : MEMDUP(data,size); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeData +( + const void *data, // data to free + CopyMode_t mode // copy mode +) +{ + if ( mode != CPM_LINK && data != EmptyString ) + FREE((void*)data); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ContainerData_t, Container_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +Container_t * CreateContainer +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if (!c) + c = MALLOC(sizeof(*c)); + memset(c,0,sizeof(*c)); + c->protect_level = protect; + + ContainerData_t *cdata = CALLOC(sizeof(*cdata),1); + c->cdata = cdata; + + cdata->data = CopyData(data,size,mode,&cdata->data_alloced); + cdata->size = size; + cdata->ref_count = 1; + if ( protect > 0 ) + cdata->protect_count++; + return c; +} + +/////////////////////////////////////////////////////////////////////////////// + +Container_t * InheritContainer +( + // returns 'c' or the alloced container + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + const Container_t *parent, // NULL or valid parent container + const void *data, // data to copy/move/link + uint size // size of 'data' +) +{ + if (!c) + c = CALLOC(sizeof(*c),1); + + if ( InContainerS(parent,data,size) || !InContainerS(c,data,size) ) + { + UnlinkContainerData(c); + UseContainerData(c,protect,parent); + } + return c; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignContainer +( + // return TRUE on new ContainerData_t + + Container_t *c, // valid container; if NULL: only FREE(data) + int protect, // new protection level + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if (c) + { + SetProtectContainer(c,protect); + + if (!InContainerS(c,data,size)) + { + UnlinkContainerData(c); + ContainerData_t *cdata = CALLOC(sizeof(*cdata),1); + c->cdata = cdata; + cdata->data = CopyData(data,size,mode,&cdata->data_alloced); + cdata->size = size; + cdata->ref_count = 1; + if ( protect > 0 ) + cdata->protect_count++; + return true; + } + } + + if ( mode == CPM_MOVE ) + FREE((void*)data); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetContainer +( + Container_t *c // container to reset => no data +) +{ + if (c) + { + UnlinkContainerData(c); + memset(c,0,sizeof(*c)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void DeleteContainer +( + Container_t *c // container to reset and to free => no data +) +{ + if (c) + { + UnlinkContainerData(c); + FREE(c); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UnlinkContainerData +( + Container_t *c // container to reset => no data +) +{ + if ( c && c->cdata ) + { + ContainerData_t *cdata = c->cdata; + if ( c->protect_level > 0 ) + cdata->protect_count--; + c->cdata = 0; + if ( !--cdata->ref_count ) + { + if (cdata->data_alloced) + FREE((void*)cdata->data); + FREE(cdata); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * LinkContainerData +( + // increment 'ref_count' and return NULL or current container-data + // => use CatchContainerData() to complete operation + + const Container_t *c // NULL or valid container +) +{ + if ( !c || !c->cdata ) + return 0; + + c->cdata->ref_count++; + return c->cdata; +} + +//----------------------------------------------------------------------------- + +ContainerData_t * MoveContainerData +( + // return NULL or unlinked current container-data + // => use CatchContainerData() to complete operation + Container_t *c // NULL or valid container +) +{ + if ( !c || !c->cdata ) + return 0; + + ContainerData_t *cdata = c->cdata; + cdata->ref_count++; + UnlinkContainerData(c); + return cdata; +} + +//----------------------------------------------------------------------------- + +Container_t * CatchContainerData +( + // returns 'c' or the alloced container + // 'c' is always initialized + + Container_t *c, // valid container, alloc one if NULL + int protect, // initial value for protection + ContainerData_t *cdata // if not NULL: catch this +) +{ + if (!c) + c = MALLOC(sizeof(*c)); + memset(c,0,sizeof(*c)); + c->protect_level = protect; + + if (cdata) + { + c->cdata = cdata; + if ( protect > 0 ) + cdata->protect_count++; + } + return c; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ContainerData_t * DisjoinContainerData +( + // Disjoin data container date without freeing it. Call either + // JoinContainerData() or FreeContainerData() to finish operation. + // Reference counters are not modified. + // Return the data container or NULL + + Container_t *c // NULL or valid container +) +{ + ContainerData_t *res = 0; + if ( c && c->cdata ) + { + const int old_protect = SetProtectContainer(c,0); + res = c->cdata; + c->cdata = 0; + SetProtectContainer(c,old_protect); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void JoinContainerData +( + // Join a data container, that was diosjoined by DisjoinContainerData() + // Reference counters are not modified. + + Container_t *c, // container to reset => no data + ContainerData_t *cdata // NULL or container-data to join +) +{ + if (c) + { + UnlinkContainerData(c); + DASSERT(!c->cdata); + if (cdata) + { + const int old_protect = SetProtectContainer(c,0); + c->cdata = cdata; + SetProtectContainer(c,old_protect); + } + } + else + FreeContainerData(cdata); +} + +/////////////////////////////////////////////////////////////////////////////// + +void FreeContainerData +( + // Decrement the reference counter of an already disjoined data container + // and free it if unused. + + ContainerData_t *cdata // NULL or container-data to free +) +{ + if ( cdata && !--cdata->ref_count ) + { + if (cdata->data_alloced) + FREE((void*)cdata->data); + FREE(cdata); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool ModifyAllContainer +( + // prepare modification of container-data, create a copy if necessary + // return true, if a new container-data is used + + Container_t *c // NULL or valid container +) +{ + if ( c && c->cdata && !ModificationAllowed(c) ) + { + ContainerData_t *oldcd = c->cdata; + ContainerData_t *newcd = CALLOC(sizeof(*newcd),1); + newcd->data = MEMDUP(oldcd->data,oldcd->size); + newcd->size = oldcd->size; + newcd->data_alloced = true; + newcd->ref_count = 1; + if ( c->protect_level > 0 ) + newcd->protect_count++; + UnlinkContainerData(c); + c->cdata = newcd; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool ModifyContainer +( + // prepare modification of container-data, create an extract if necessary + // return true, if a new container-data is used + + Container_t *c, // NULL or valid container + const void *data, // data to copy/move/link + uint size, // size of 'data' + CopyMode_t mode // copy mode on creation +) +{ + if ( c && c->cdata && ( !ModificationAllowed(c) || !InContainerS(c,data,size) )) + { + ContainerData_t *newcd = CALLOC(sizeof(*newcd),1); + newcd->data = CopyData(data,size,mode,&newcd->data_alloced); + newcd->size = size; + newcd->ref_count = 1; + if ( c->protect_level > 0 ) + newcd->protect_count++; + UnlinkContainerData(c); + c->cdata = newcd; + return true; + } + + if ( mode == CPM_MOVE ) + FREE((void*)data); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SetProtectContainer +( + // returns 'c' new protection level + Container_t *c, // valid container, alloc one if NULL + int new_protect // new protection value +) +{ + DASSERT(c); + if (c->cdata) + { + if ( new_protect > 0 ) + { + if ( c->protect_level <= 0 ) + c->cdata->protect_count++; + } + else + { + if ( c->protect_level > 0 ) + c->cdata->protect_count--; + } + } + return c->protect_level = new_protect; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint DumpInfoContainer +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent the output + ccp prefix, // not NULL: use it as prefix behind indention + const Container_t *c, // dump infos for this container, NULL allowed + uint hexdump_len // max number of bytes used for a hexdump +) +{ + DASSERT(f); + if (!c) + return 0; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f,"%*s%s%s%smod=%d, PL=%d, ", + indent,"", + colset->caption, prefix ? prefix : "", + colset->status, ModificationAllowed(c), c->protect_level ); + uint line_count = DumpInfoContainerData(f,0,0,0,c->cdata,hexdump_len,indent+2); + if (!line_count) + { + fputs("<no-data>",f); + line_count++; + } + fprintf(f,"%s\n",colset->reset); + return line_count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DumpInfoContainerData +( + // return the number of printed lines + + FILE *f, // valid output file + const ColorSet_t *colset, // NULL or color set + int indent, // indent of output + ccp prefix, // not NULL: use it as prefix behind indention + const + ContainerData_t *cdata, // dump infos for this container-data, NULL allowed + uint hexdump_len, // max number of bytes used for a hexdump + int hexdump_indent // indent of hexdump +) +{ + DASSERT(f); + if (!cdata) + return 0; + + indent = NormalizeIndent(indent); + if (!colset) + colset = GetColorSet0(); + + fprintf(f,"%*s%s%s%sPC=%d, RC=%d, alloc=%d, size=%u=0x%x, addr=%p\n", + indent,"", + colset->caption, prefix ? prefix : "", colset->status, + cdata->protect_count, cdata->ref_count, cdata->data_alloced, + cdata->size, cdata->size, cdata->data ); + + if ( hexdump_len > 0 && cdata->data && cdata->size ) + { + if ( hexdump_len > cdata->size ) + hexdump_len = cdata->size; + return 1 + HexDump16(f,hexdump_indent,0,cdata->data,hexdump_len); + } + return 1; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// split strings /////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByCharMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + char sep // separator character +) +{ + DASSERT(mem_list); + DASSERT(n_mem>1); + + ccp src = source.ptr; + ccp end = src + source.len; + + mem_t *mem = mem_list; + mem_t *last = mem + n_mem - 1; + + while ( mem < last ) + { + mem->ptr = src; + while ( src < end && *src != sep ) + src++; + + mem->len = src - mem->ptr; + mem++; + + if ( ++src > end ) + break; + } + + if ( src <= end ) + { + mem->ptr = src; + mem->len = end - src; + mem++; + } + const uint result = mem - mem_list; + + while ( mem <= last ) + { + mem->ptr = EmptyString; + mem->len = 0; + mem++; + } + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByTextMem +( + // 'mem_list' will be padded with {EmptyString,0} + + mem_t *mem_list, // array of mem_t + uint n_mem, // number of elements of 'mem_list' + mem_t source, // source string + mem_t sep // separator text +) +{ + DASSERT(mem_list); + DASSERT(n_mem>0); + + ccp src = source.ptr; + ccp end = src + source.len; + + mem_t *mem = mem_list; + mem_t *last = mem + n_mem - 1; + + if ( sep.len > 0 && sep.len <= source.len ) + { + ccp cmp_max = end - sep.len; + while ( mem < last ) + { + mem->ptr = src; + while ( src <= cmp_max && memcmp(src,sep.ptr,sep.len) ) + src++; + + if ( src > cmp_max ) + { + mem->len = end - mem->ptr; + mem++; + src = end + 1; + break; + } + + mem->len = src - mem->ptr; + mem++; + src += sep.len; + } + } + + if ( src <= end ) + { + mem->ptr = src; + mem->len = end - src; + mem++; + } + const uint result = mem - mem_list; + + while ( mem <= last ) + { + mem->ptr = EmptyString; + mem->len = 0; + mem++; + } + + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByCharMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + char sep, // separator character + int max_fields // >0: max number of result fields +) +{ + DASSERT(ml); + if (init_ml) + InitializeMemList(ml); + ml->used = 0; + + if ( max_fields <= 0 ) + max_fields = INT_MAX; + GrowBufMemList(ml,source.len+1); + + ccp src = source.ptr; + ccp end = src + source.len; + + while ( max_fields-- > 1 ) + { + mem_t mem; + mem.ptr = src; + while ( src < end && *src != sep ) + src++; + + mem.len = src - mem.ptr; + AppendMemListN(ml,&mem,1,0); + if ( ++src > end ) + break; + } + + if ( src <= end ) + { + mem_t mem = { src, end - src }; + AppendMemListN(ml,&mem,1,0); + } + + return ml->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SplitByTextMemList +( + mem_list_t *ml, // valid destination mem_list + bool init_ml, // true: initialize 'ml' first + mem_t source, // source string + mem_t sep, // separator text + int max_fields // >0: max number of result fields +) +{ + DASSERT(ml); + if (init_ml) + InitializeMemList(ml); + ml->used = 0; + + if ( max_fields <= 0 ) + max_fields = INT_MAX; + GrowBufMemList(ml,source.len+1); + + ccp src = source.ptr; + ccp end = src + source.len; + + if ( sep.len > 0 && sep.len <= source.len ) + { + ccp cmp_max = end - sep.len; + while ( max_fields-- > 1 ) + { + mem_t mem; + mem.ptr = src; + while ( src <= cmp_max && memcmp(src,sep.ptr,sep.len) ) + src++; + + if ( src > cmp_max ) + { + mem.len = end - mem.ptr; + AppendMemListN(ml,&mem,1,0); + src = end + 1; + break; + } + + mem.len = src - mem.ptr; + AppendMemListN(ml,&mem,1,0); + src += sep.len; + } + } + + if ( src <= end ) + { + mem_t mem = { src, end - src }; + AppendMemListN(ml,&mem,1,0); + } + + return ml->used; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MatchPattern() /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp AnalyseBrackets +( + ccp pattern, + ccp * p_start, + bool * p_negate, + int * p_multiple +) +{ + ASSERT(pattern); + + bool negate = false; + if ( *pattern == '^' ) + { + pattern++; + negate = true; + } + if (p_negate) + *p_negate = negate; + + int multiple = 0; + if ( *pattern == '+' ) + { + pattern++; + multiple = 1; + } + else if ( *pattern == '*' ) + { + pattern++; + multiple = 2; + } + if (p_multiple) + *p_multiple = multiple; + + if (p_start) + *p_start = pattern; + + if (*pattern) // ']' allowed in first position + pattern++; + while ( *pattern && *pattern++ != ']' ) // find end + ; + + return pattern; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool MatchBracktes +( + char ch, + ccp pattern, + bool negate +) +{ + if (!ch) + return false; + + bool ok = false; + ccp p = pattern; + for (; !ok && *p && ( p == pattern || *p != ']' ); p++ ) + { + if ( *p == '-' ) + { + if ( ch <= *++p && ch >= p[-2] ) + { + if (negate) + return false; + ok = true; + } + } + else + { + if ( *p == '\\' ) + p++; + + if ( *p == ch ) + { + if (negate) + return false; + ok = true; + } + } + } + return ok || negate; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool MatchPatternHelper +( + ccp pattern, + ccp text, + bool skip_end, + int alt_depth, + char path_sep // path separator character, standard is '/' +) +{ + ASSERT(pattern); + ASSERT(text); + noTRACE(" - %d,%d |%s|%s|\n",skip_end,alt_depth,pattern,text); + + char ch; + while ( ( ch = *pattern++ ) != 0 ) + { + switch (ch) + { + case '*': + if ( *pattern == '*' ) + { + pattern++; + if (*pattern) + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if (!*text++) + return false; + } + else + { + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if ( *text == path_sep || !*text++ ) + return false; + } + return true; + + case '#': + if ( *text < '0' || *text > '9' ) + return false; + while ( *text >= '0' && *text <= '9' ) + if (MatchPatternHelper(pattern,++text,skip_end,alt_depth,path_sep)) + return true; + return false; + + case ' ': + if ( *text < 1 || * text > ' ' ) + return false; + text++; + break; + + case '?': + if ( !*text || *text == path_sep ) + return false; + text++; + break; + + case '[': + { + ccp start; + bool negate; + int multiple; + TRACELINE; + pattern = AnalyseBrackets(pattern,&start,&negate,&multiple); + TRACELINE; + + if ( multiple < 2 && !MatchBracktes(*text++,start,negate) ) + return false; + + if (multiple) + { + while (!MatchPatternHelper(pattern,text,skip_end,alt_depth,path_sep)) + if (!MatchBracktes(*text++,start,negate)) + return false; + return true; + } + } + break; + + case '{': + for(;;) + { + if (MatchPatternHelper(pattern,text,skip_end,alt_depth+1,path_sep)) + return true; + // skip until next ',' || '}' + int skip_depth = 1; + while ( skip_depth > 0 ) + { + ch = *pattern++; + switch(ch) + { + case 0: + return false; + + case '\\': + if (!*pattern) + return false; + pattern++; + break; + + case '{': + skip_depth++; + break; + + case ',': + if ( skip_depth == 1 ) + skip_depth--; + break; + + case '}': + if (!--skip_depth) + return false; + break; + + case '[': // [2do] forgotten marker? + pattern = AnalyseBrackets(pattern,0,0,0); + break; + } + } + } + + case ',': + if (alt_depth) + { + alt_depth--; + int skip_depth = 1; + while ( skip_depth > 0 ) + { + ch = *pattern++; + switch(ch) + { + case 0: + return false; + + case '\\': + if (!*pattern) + return false; + pattern++; + break; + + case '{': + skip_depth++; + break; + + case '}': + skip_depth--; + break; + + case '[': // [2do] forgotten marker? + pattern = AnalyseBrackets(pattern,0,0,0); + break; + } + } + } + else if ( *text++ != ch ) + return false; + break; + + case '}': + if ( !alt_depth && *text++ != ch ) + return false; + break; + + case '$': + if ( !*pattern && !*text ) + return true; + if ( *text++ != ch ) + return false; + break; + + case '\\': + ch = *pattern++; + // fall through + + default: + if ( *text++ != ch ) + return false; + break; + } + } + return skip_end || *text == 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MatchPatternFull +( + ccp pattern, // pattern text + ccp text // raw text +) +{ + TRACE("MatchPattern(|%s|%s|)\n",pattern,text); + if ( !pattern || !*pattern ) + return true; + + if (!text) + text = ""; + + if ( *pattern == '^' ) + pattern++; + + return MatchPatternHelper(pattern,text,false,0,'/'); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool MatchPattern +( + ccp pattern, // pattern text + ccp text, // raw text + char path_sep // path separator character, standard is '/' +) +{ + TRACE("MatchPattern(|%s|%s|%c|)\n",pattern,text,path_sep); + if ( !pattern || !*pattern ) + return true; + + if (!text) + text = ""; + + const size_t plen = strlen(pattern); + ccp last = pattern + plen - 1; + char last_ch = *last; + int count = 0; + while ( last > pattern && *--last == '\\' ) + count++; + if ( count & 1 ) + last_ch = 0; // no special char! + + if ( *pattern == path_sep || *pattern == '^' ) + { + pattern++; + return MatchPatternHelper(pattern,text,last_ch!='$',0,path_sep); + } + + while (*text) + if (MatchPatternHelper(pattern,text++,last_ch!='$',0,path_sep)) + return true; + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp MatchStringField ( const StringField_t * sf, ccp key ) +{ + DASSERT(sf); + DASSERT(key); + + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + if (MatchPattern(*ptr,key,'/')) + return *ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * MatchParamField ( const ParamField_t * pf, ccp key ) +{ + DASSERT(pf); + DASSERT(key); + + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + if (MatchPattern(ptr->key,key,'/')) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * MatchRuleLine +( + // returns a pointer to the first non scanned char + + int *status, // not NULL: return match status here + // -2: no prefix found (no ruile found) + // -1: empty line (no ruile found) + // 0: rule found, but don't match + // 1: rule found and match + ccp src, // source line, scanned until CONTROL + char rule_prefix, // NULL or a rule-beginning-char' + ccp path, // path to verify + char path_sep // path separator character, standard is '/' +) +{ + int local_status; + if (!status) + status = &local_status; + + while ( *src > 0 && *src <= ' ' && *src != '\n' ) + src++; + + if (rule_prefix) + { + if ( *src != rule_prefix ) + { + *status = -2; + return (char*)src; + } + src++; + while ( *src > 0 && *src <= ' ' && *src != '\n' ) + src++; + } + + ccp start = src; + while ( (uchar)*src >= ' ' ) + src++; + + char buf[1000]; + uint len = src - start; + if (!len) + { + *status = -1; + return (char*)src; + } + + if ( len > sizeof(buf)-1 ) + len = sizeof(buf)-1; + memcpy(buf,start,len); + buf[len] = 0; + + *status = MatchPattern(buf,path,path_sep); + return (char*)src; +} + + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan keywords /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t *GetKewordById +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id // id to search +) +{ + if (key_tab) + { + const KeywordTab_t *key; + for ( key = key_tab; *key->name1; key++ ) + if ( key->id == id ) + return key; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t *GetKewordByIdAndOpt +( + const KeywordTab_t *key_tab, // NULL or pointer to command table + s64 id, // id to search + s64 opt // opt to search +) +{ + if (key_tab) + { + const KeywordTab_t *key; + for ( key = key_tab; *key->name1; key++ ) + if ( key->id == id && key->opt == opt ) + return key; + } + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t * ScanKeyword +( + int * res_abbrev, // NULL or pointer to result 'abbrev_count' + ccp arg, // argument to scan + const KeywordTab_t * key_tab // valid pointer to command table +) +{ + DASSERT(arg); + char key_buf[KEYWORD_NAME_MAX]; + + char *dest = key_buf; + char *end = key_buf + sizeof(key_buf) - 1; + while ( *arg && *arg != ',' && *arg != ';' && dest < end ) + *dest++ = toupper((int)*arg++); + *dest = 0; + const int key_len = dest - key_buf; + + int favor_count = 0, abbrev_count = 0; + const KeywordTab_t *ct, *key_ct = 0; + const KeywordTab_t *favor_ct = 0, *favor_prev = 0, *abbrev_ct = 0, *abbrev_prev = 0; + for ( ct = key_tab; ct->name1; ct++ ) + { + if ( !strcmp(ct->name1,key_buf) ) + { + key_ct = ct; + break; + } + + bool name2_abbrev = false; + if (ct->name2) + { + if (!strcmp(ct->name2,key_buf)) + { + key_ct = ct; + break; + } + name2_abbrev = !memcmp(ct->name2,key_buf,key_len); + } + + if ( name2_abbrev && !memcmp(ct->name1,key_buf,strlen(ct->name1)) ) + { + if (!favor_ct) + { + favor_prev = favor_ct = ct; + favor_count++; + } + else if ( favor_prev->id != ct->id || favor_prev->opt != ct->opt ) + { + favor_prev = ct; + favor_count++; + } + } + + if ( *key_buf == '_' ) // no abbreviations for commands beginning with '_' + continue; + + if ( name2_abbrev || !memcmp(ct->name1,key_buf,key_len) ) + { + if (!abbrev_ct) + { + abbrev_prev = abbrev_ct = ct; + abbrev_count++; + } + else if ( abbrev_prev->id != ct->id || abbrev_prev->opt != ct->opt ) + { + abbrev_prev = ct; + abbrev_count++; + } + } + } + + if (key_ct) + abbrev_count = 0; + else if ( favor_count == 1 ) + { + key_ct = favor_ct; + abbrev_count = 0; + } + else if ( abbrev_count == 1 ) + key_ct = abbrev_ct; + else if (!abbrev_count) + abbrev_count = -1; + + if (res_abbrev) + *res_abbrev = abbrev_count; + + return key_ct; +} + +/////////////////////////////////////////////////////////////////////////////// + +s64 ScanKeywordListEx +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // NULL or calculation function + bool allow_prefix, // allow '-' | '+' | '=' as prefix + u32 max_number, // allow numbers < 'max_number' (0=disabled) + s64 result, // start value for result + + uint err_mode, // bit field: + // 1: continue on error + ccp err_msg, // not NULL: print a warning message: + // "<ERR_MSG>: Unknown keyword: <KEY>" + enumError err_code, // use 'err_code' for the 'err_msg' + uint *err_count // not NULL: store errors here +) +{ + ASSERT(arg); + + char key_buf[KEYWORD_NAME_MAX]; + char *end = key_buf + sizeof(key_buf) - 1; + uint err_cnt = 0; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == '.' ) + arg++; + + if ( !*arg || *arg == '#' ) + break; + + char *dest = key_buf; + while ( *arg > ' ' && *arg != ',' && *arg != '.' && *arg != '#' + && ( *arg != '+' || dest == key_buf ) && dest < end ) + *dest++ = *arg++; + *dest = 0; + char prefix = 0; + int abbrev_count; + const KeywordTab_t * cptr = ScanKeyword(&abbrev_count,key_buf,key_tab); + if ( !cptr && allow_prefix && key_buf[1] + && ( *key_buf == '+' || *key_buf == '-' + || *key_buf == '/' || *key_buf == '=' )) + { + prefix = *key_buf == '/' ? '-' : *key_buf; + cptr = ScanKeyword(&abbrev_count,key_buf+1,key_tab); + } + + KeywordTab_t ct_num = { 0, key_buf, 0, 0 }; + if ( max_number && abbrev_count ) + { + char * start = key_buf + (prefix!=0); + ulong num = str2l(start,&dest,10); + if ( num < max_number && dest > start && !*dest ) + { + ct_num.id = num; + cptr = &ct_num; + } + } + + noPRINT("cptr=%p [%s], opt=%llx, prefix=%02x\n", + cptr, cptr ? cptr->name1 : "", cptr ? cptr->opt : 0, prefix ); + if ( !cptr || cptr->opt && prefix && prefix != '+' ) + { + if (err_msg) + { + if ( abbrev_count > 1 ) + ERROR0(err_code,"%s: Ambiguous keyword: %s\n",err_msg,key_buf); + else + ERROR0(err_code,"%s: Unknown keyword: %s\n",err_msg,key_buf); + } + err_cnt++; + if ( err_mode & 1 ) + continue; + result = M1(result); + break; + } + + if (func) + { + const s64 res = func(0,key_buf,key_tab,cptr,prefix,result); + if ( res == M1(res) ) + { + if ( !(err_mode & 1) ) + { + result = res; + break; + } + } + else + result = res; + } + else + { + switch (prefix) + { + case 0: + case '+': result = result & ~cptr->opt | cptr->id; break; + case '-': result &= ~cptr->id; break; + case '=': result = cptr->id; break; + } + } + } + + if (err_count) + *err_count = err_cnt; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError ScanKeywordListFunc +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab, // valid pointer to command table + KeywordCallbackFunc func, // calculation function + void * param, // used define parameter for 'func' + bool allow_prefix // allow '-' | '+' | '=' as prefix +) +{ + ASSERT(arg); + ASSERT(func); + + char key_buf[KEYWORD_NAME_MAX]; + char *end = key_buf + sizeof(key_buf) - 1; + + for(;;) + { + while ( *arg > 0 && *arg <= ' ' || *arg == ',' || *arg == '.' ) + arg++; + + if (!*arg) + return ERR_OK; + + char *dest = key_buf; + while ( *arg > ' ' && *arg != ',' && *arg != '.' + && ( *arg != '+' || dest == key_buf ) && dest < end ) + *dest++ = *arg++; + *dest = 0; + char prefix = 0; + int abbrev_count; + const KeywordTab_t * cptr = ScanKeyword(&abbrev_count,key_buf,key_tab); + if ( !cptr && allow_prefix && key_buf[1] + && ( *key_buf == '+' || *key_buf == '-' || *key_buf == '/' || *key_buf == '=' )) + { + prefix = *key_buf == '/' ? '-' : *key_buf; + cptr = ScanKeyword(&abbrev_count,key_buf+1,key_tab); + } + + const enumError err = func(param,key_buf,key_tab,cptr,prefix,0); + if (err) + return err; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static s64 ScanKeywordListMaskHelper +( + void * param, // NULL or user defined parameter + ccp name, // normalized name of option + const KeywordTab_t * key_tab, // valid pointer to command table + const KeywordTab_t * key, // valid pointer to found command + char prefix, // 0 | '-' | '+' | '=' + s64 result // current value of result +) +{ + return key->opt + ? result & ~key->opt | key->id + : key->id; +} + +//----------------------------------------------------------------------------- + +s64 ScanKeywordListMask +( + ccp arg, // argument to scan + const KeywordTab_t * key_tab // valid pointer to command table +) +{ + return ScanKeywordList(arg,key_tab,ScanKeywordListMaskHelper,false,0,0,0,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CollectAmbiguousKeywords +( + char *buf, // destination buffer, 200 bytes are good + uint buf_size, // size of buffer + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg // analyzed command +) +{ + DASSERT(buf); + DASSERT(buf_size>10); + DASSERT(key_tab); + DASSERT(key_arg); + + int n = 0; + char *dest = buf, *buf_end = buf + buf_size - 2; + const int arg_len = strlen(key_arg); + int last_id = -1; + + const KeywordTab_t *ct; + for ( ct = key_tab; ct->name1 && dest < buf_end; ct++ ) + { + if ( ct->id != last_id ) + { + ccp ok = 0; + if (!strncasecmp(key_arg,ct->name1,arg_len)) + ok = ct->name1; + else if ( ct->name2 && !strncasecmp(key_arg,ct->name2,arg_len)) + ok = ct->name2; + if (ok) + { + if (!n++) + { + *dest++ = ' '; + *dest++ = '['; + } + else if ( n > 5 ) + { + dest = StringCopyE(dest,buf_end,",..."); + break; + } + else + *dest++ = ','; + dest = StringCopyE(dest,buf_end,ok); + last_id = ct->id; + } + } + } + if ( dest > buf+1 ) + *dest++ = ']'; + else + dest = buf; + *dest = 0; + return dest - buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError PrintKeywordError +( + const KeywordTab_t * key_tab, // NULL or pointer to command table + ccp key_arg, // analyzed command + int key_stat, // status of ScanKeyword() + ccp prefix, // NULL or first prefix for messages + ccp object // NULL or object for error messages + // default= 'command' +) +{ + DASSERT(key_arg); + + if ( !object || !*object ) + object = "command"; + + if ( key_stat <= 0 ) + return ERROR0(ERR_SYNTAX,"%sUnknown %s: %s\n", + prefix ? prefix : "", object, key_arg ); + + char buf[1000], *dest = buf; + if (key_tab) + { + int n = 0; + ccp buf_end = buf + sizeof(buf) - 2; + const int arg_len = strlen(key_arg); + const KeywordTab_t *ct; + int last_id = -1; + + for ( ct = key_tab; ct->name1 && dest < buf_end; ct++ ) + { + if ( ct->id != last_id ) + { + ccp ok = 0; + if (!strncasecmp(key_arg,ct->name1,arg_len)) + ok = ct->name1; + else if ( ct->name2 && !strncasecmp(key_arg,ct->name2,arg_len)) + ok = ct->name2; + if (ok) + { + if (!n++) + { + *dest++ = ' '; + *dest++ = '['; + } + else if ( n > 5 ) + { + dest = StringCopyE(dest,buf_end,",..."); + break; + } + else + *dest++ = ','; + dest = StringCopyE(dest,buf_end,ok); + last_id = ct->id; + } + } + } + if ( dest > buf+1 ) + *dest++ = ']'; + else + dest = buf; + } + *dest = 0; + + return ERROR0(ERR_SYNTAX,"%s%c%s abbreviation is ambiguous: %s%s\n", + prefix ? prefix : "", + toupper((int)*object), object+1, key_arg, buf ); +} + +/////////////////////////////////////////////////////////////////////////////// + +char * PrintKeywordList +( + // returns a pointer to the result buffer + + char *buf, // result buffer + // If NULL, a local circulary static buffer + // with max CIRC_BUF_MAX_ALLOC bytes is used + uint buf_size, // size of 'buf', at least 10 bytes if buf is set + uint *ret_length, // not NULL: store result length here + + const KeywordTab_t *tab, // related keyword table + u64 mode, // mode to print + u64 default_mode, // NULL or default mode + u64 hide_mode // bit field to hide parameters +) +{ + DASSERT( !buf || buf_size >= 10 ); + char temp[CIRC_BUF_MAX_ALLOC]; + if (!buf) + { + buf = temp; + buf_size = sizeof(temp); + } + + char *dest = buf; + char *end = buf + buf_size - 1; + + mode = mode | hide_mode; + u64 mode1 = mode; + + for ( ; tab->name1 && dest < end; tab++ ) + { + if ( !tab->id || tab->opt & hide_mode ) + continue; + + if ( tab->opt ? (mode & tab->opt) == tab->id : mode & tab->id ) + { + if ( dest > buf ) + *dest++ = ','; + dest = StringCopyE(dest,end,tab->name1); + mode &= ~(tab->id|tab->opt); + } + } + + if ( default_mode && mode1 == (default_mode|hide_mode) ) + dest = StringCopyE(dest,end," (default)"); + else if (!mode1) + dest = StringCopyE(dest,end,"(none)"); + + *dest = 0; + const uint len = dest - buf; + if (ret_length) + *ret_length = len; + + if ( buf == temp ) + { + buf = GetCircBuf(len+1); + memcpy(buf,temp,len+1); + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanKeywordOffOn +( + // returns 0 for '0|OFF'; 1 for '1|ON; -1 for empty; -2 on error + + ccp arg, // argument to scan + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +) +{ + if (!arg||!*arg) + return -1; + + static const KeywordTab_t keytab[] = + { + { 0, "OFF", "0", 0 }, + { 1, "ON", "1", 0 }, + {0,0,0,0} + }; + + int status; + const KeywordTab_t *key = ScanKeyword(&status,arg,keytab); + if (key) + return key->id; + + if ( max_num > 0 ) + { + char *end; + const uint num = str2ul(arg,&end,10); + if ( !*end && num <= max_num ) + return num; + } + + if (object) + PrintKeywordError(keytab,arg,status,0,object); + + return -2; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// OFF/AUTO/ON/FORCE /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const KeywordTab_t KeyTab_OFF_AUTO_ON[] = +{ + { OFFON_OFF, "OFF", "-1", 0 }, + { OFFON_OFF, "DISABLED", 0, 0 }, + { OFFON_AUTO, "AUTO", "0", 0 }, + { OFFON_ON, "ON", "1", 0 }, + { OFFON_ON, "ENABLED", 0, 0 }, + { OFFON_FORCE, "FORCE", "2", 0 }, + { 0,0,0,0 } +}; + +//----------------------------------------------------------------------------- + +int ScanKeywordOffAutoOn +( + // returns one of OFFON_* + + ccp arg, // argument to scan + int on_empty, // return this value on empty + uint max_num, // >0: additionally accept+return number <= max_num + ccp object // NULL (silent) or object for error messages +) +{ + if ( !arg || !*arg ) + return on_empty; + + int status; + const KeywordTab_t *key = ScanKeyword(&status,arg,KeyTab_OFF_AUTO_ON); + if (key) + return key->id; + + if ( max_num > 0 ) + { + char *end; + const int num = str2l(arg,&end,10); + if ( !*end && num >= OFFON_OFF && num <= max_num ) + return num; + } + + if (object) + PrintKeywordError(KeyTab_OFF_AUTO_ON,arg,status,0,object); + + return OFFON_ERROR; +} + +//----------------------------------------------------------------------------- + +ccp GetKeywordOffAutoOn ( OffOn_t value ) +{ + const KeywordTab_t *key = GetKewordById(KeyTab_OFF_AUTO_ON,value); + return key ? key->name1 : "?"; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// scan command lists /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int ExecCommandOfList ( CommandList_t *cli ) +{ + DASSERT(cli); + if ( cli->n_bin ) + { + printf("%3u. %s [n_bin=%u] = |%.*s|\n", + cli->cmd_count, cli->cmd, cli->n_bin, cli->param_len, cli->param ); + uint i; + for ( i = 0; i < cli->n_bin; i++ ) + { + printf(" > binary #%u, len=%u\n",i,cli->bin[0].len); + HexDump16( stdout,5,0, cli->bin[i].ptr, + cli->bin[i].len < 0x20 ? cli->bin[i].len : 0x20 ); + } + } + else + printf("%3u. %s = |%.*s|\n", + cli->cmd_count, cli->cmd, cli->param_len, cli->param ); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanCommandList +( + // returns -1 on 'cli->fail_count'; otherwise 'cli->cmd_count' + + CommandList_t *cli, // valid command list + CommandListFunc func // NULL or function for each command +) +{ + DASSERT(cli); + + static uint log_pid = 0; + static uint log_seq = 0; + static FILE *log = 0; + + if ( cli->log_fname && cli->log_level && !log_pid ) + { + log_pid = getpid(); + log = fopen(cli->log_fname,"ab"); + if (log) + { + fcntl(fileno(log),F_SETFD,FD_CLOEXEC); + fprintf(log,"\n#\f\n#OPEN %s, pid=%d\n\n", + PrintTimeByFormat("%F %T %z",time(0)), log_pid ); + } + } + uint log_level = log ? cli->log_level : 0; + + cli->read_cmd_len = 0; + const char sep = cli->command_sep ? cli->command_sep : ';'; + ccp cmd = cli->command, end = cmd + cli->command_len; + if ( log_level > 0 ) + { + fprintf(log,"\n++ %u.%u len=%u\n", + log_pid, ++log_seq, cli->command_len ); + fflush(log); + } + + for(;;) + { + cli->n_bin = 0; + memset(cli->bin,0,sizeof(cli->bin)); + + //--- skip blanks and terminators => find begin of record + + while ( cmd < end && ( (uchar)*cmd <= ' ' || *cmd == sep )) + cmd++; + cli->read_cmd_len = cmd - cli->command; // definitley scanned! + + if ( cmd >= end ) + break; + + cli->record = (u8*)cmd; + + + //--- find end of command+param + + ccp endcmd = memchr(cmd,sep,end-cmd); + if (!endcmd) + { + if (!cli->is_terminated) + goto abort; + endcmd = end; + } + + + //--- scan and copy command + + ccp param = cmd; + while ( param < endcmd && ( isalnum(*param) || *param == '-' || *param == '_' ) ) + param++; + cli->cmd_len = param - cmd; + if ( cli->cmd_len > sizeof(cli->cmd)-1 ) + cli->cmd_len = sizeof(cli->cmd)-1; + + if ( log_level > 1 ) + { + fprintf(log,">%.*s\n",cli->cmd_len,cmd); + fflush(log); + } + + if (cli->change_case) + { + char *dest = cli->cmd; + uint count = cli->cmd_len; + if ( cli->change_case == 1 ) + while ( count-- > 0 ) + *dest++ = tolower((int)*cmd++); + else + while ( count-- > 0 ) + *dest++ = toupper((int)*cmd++); + *dest = 0; + } + else + { + memcpy(cli->cmd,cmd,cli->cmd_len); + cli->cmd[cli->cmd_len] = 0; + } + + + //--- scan binary + + while ( cli->n_bin < COMMAND_LIST_N_BIN ) + { + while ( param < endcmd && ( *param == ' ' || *param == '\t' )) + param++; + + if ( param == endcmd || *param != '\1' ) + break; // no binary! + + //-- From now on for binary examintation, only 'end' is relevant, + //-- because binary data may contain the separator character. + + if ( param+3 > end ) + { + if ( log_level > 1 ) + { + fprintf(log,"bin incomplete\n"); + fflush(log); + } + + if (cli->is_terminated) + { + // now we have a conflict: + fprintf(stderr, + "#WARN: Conflict in ExecCommandOfList():" + " Stream is terminated, but binary not: %s\n", + cli->user_ts ? cli->user_ts->info : "" ); + + if (log) + { + fprintf(log,"#WARN: Conflict: Stream is terminated, but binary not!\n"); + fflush(log); + } + } + goto abort; // incomplete + } + + const uint bin_len = be16(++param); + param += 2; + if ( log_level > 2 ) + { + fprintf(log,"bin %u,%u..%u/%u%s\n", + bin_len, + (uint)(param - cli->command), + (uint)(param - cli->command + bin_len), + cli->command_len, + param + bin_len > end ? " incomplete" : "" ); + fflush(log); + } + if ( param + bin_len > end ) + goto abort; // incomplete + + cli->bin[cli->n_bin].ptr = param; + cli->bin[cli->n_bin].len = bin_len; + param += bin_len; + cli->n_bin++; + + //-- rescan 'endcmd' after binary block + + endcmd = memchr(param,sep,end-param); + if (!endcmd) + { + if (!cli->is_terminated) + goto abort; // incomplete + endcmd = end; + } + } + + + //--- calc record length + + cli->input_len = (u8*)endcmd - cli->record; + if ( endcmd < end ) + cli->input_len++; + + + //--- scan param + + while ( param < endcmd && (uchar)*param <= ' ' ) + param++; + if ( param < endcmd && *param == '=' ) + { + param++; + while ( param < endcmd && (uchar)*param <= ' ' ) + param++; + } + + ccp endparam = endcmd-1; + while ( endparam > param && (uchar)*endparam <= ' ' ) + endparam--; + endparam++; + + if ( log_level > 2 && endparam > param ) + { + const uint plen = endparam - param, max = 25; + if ( plen <= max ) + fprintf(log,"par %.*s\n",plen,param); + else + fprintf(log,"par %.*s…+%u\n",max,param,plen-max); + fflush(log); + } + + if (cli->term_param) + { + if ( cli->term_param > 1 ) + { + ((char*)endparam)[0] = sep; + endparam++; + } + else + ((char*)endparam)[0] = 0; + } + + cli->param = param; + cli->param_len = endparam - param; + cli->record_len = endparam - (ccp)cli->record; + cli->read_cmd_len = endcmd - (ccp)cli->command; + cmd = endcmd; + + + //--- execute command + + if ( log_level > 3 ) + { + fprintf(log,"func\n"); + fflush(log); + const int stat = func(cli); + fprintf(log,"s=%d\n",stat); + fflush(log); + if ( stat < 0 ) + break; + } + else if ( func && func(cli) < 0 ) + break; + + cli->cmd_count++; + } + + abort:; + if ( log_level > 1 || log && cli->read_cmd_len > cli->command_len ) + { + fprintf(log,"-- %u,%u/%u%s\n", + cli->cmd_count, + cli->read_cmd_len, cli->command_len, + cli->read_cmd_len > cli->command_len ? " #FAIL!" : "" ); + fflush(log); + } + + // should never happen + if ( cli->read_cmd_len > cli->command_len ) + cli->read_cmd_len = cli->command_len; + + return cli->fail_count ? -1 : cli->cmd_count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global commands /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_ARGTEST ( int argc, char ** argv ) +{ + printf("ARGUMENT TEST: %u arguments:\n",argc); + + int idx; + for ( idx = 0; idx < argc; idx++ ) + printf("%4u.: |%s|\n",idx,argv[idx]); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_COLORS +( + int level, // only used, if mode==NULL + // < 0: status message (ignore mode) + // >= 1: include names + // >= 2: include alt names + // >= 3: include color names incl bold + // >= 4: include background color names + uint mode, // output mode => see PrintColorSetHelper() + uint format // output format => see PrintColorSetEx() +) +{ + if (!colout) + SetupStdMsg(); + + if ( level < 0 ) + { + printf("%s--color=%d [%s], colorize=%d [%s]\n" + "term=%s\n" + "stdout: tty=%d, mode=%d [%s], have-color=%d, n-colors=%u%s\n", + colout->status, + opt_colorize, GetColorModeName(opt_colorize,"?"), + colorize_stdout, GetColorModeName(colorize_stdout,"?"), + getenv("TERM"), isatty(fileno(stdout)), + colout->col_mode, GetColorModeName(colout->col_mode,"?"), + colout->colorize, colout->n_colors, colout->reset ); + return ERR_OK; + } + + if ( !mode && level > 0 ) + switch (level) + { + case 1: mode = 0x08; break; + case 2: mode = 0x18; break; + case 3: mode = 0x1b; break; + default: mode = 0x1f; break; + } + + if (!format) + { + const ColorMode_t col_mode = colout ? colout->col_mode : COLMD_AUTO; + PrintTextModes( stdout, 4, col_mode ); + PrintColorModes( stdout, 4, col_mode, GCM_ALT ); + #ifdef TEST + PrintColorModes( stdout, 4, col_mode, GCM_ALT|GCM_SHORT ); + #endif + } + if ( mode || format ) + PrintColorSetEx(stdout,4,colout,mode,format); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// global commands: TESTCOLORS /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[color_info_t]] + +typedef struct color_info_t +{ + bool valid; // >0: data below is valid + + u8 m_index; // index of 0..255 for "\e[*m" + + s8 m_red; // -1:invalid, 0..5: red value of 'm_index' + s8 m_green; // -1:invalid, 0..5: green value of 'm_index' + s8 m_blue; // -1:invalid, 0..5: blue value of 'm_index' + s8 m_gray; // -1:invalid, 0..23: gray value of 'm_index' + + u32 ref_color; // reference colors, based on 'm_index'; + u32 src_color; // scanned color +} +color_info_t; + +/////////////////////////////////////////////////////////////////////////////// + +static bool AssignColorInfo ( color_info_t *ci, u32 rgb ) +{ + DASSERT(ci); + memset(ci,0,sizeof(*ci)); + + ci->src_color = rgb & 0xffffff; + ci->m_index = ConvertColorRGBToM256(ci->src_color); + ci->ref_color = ConvertColorM256ToRGB(ci->m_index); + + const uint r = ci->ref_color >> 16 & 0xff; + const uint g = ci->ref_color >> 8 & 0xff; + const uint b = ci->ref_color & 0xff; + + ci->m_red = SingleColorToM6(r); + ci->m_green = SingleColorToM6(g); + ci->m_blue = SingleColorToM6(b); + + ci->m_gray = ( r + g + b + 21 ) / 30; + if ( ci->m_gray > 23 ) + ci->m_gray = 23; + else if ( ci->m_gray > 0 ) + ci->m_gray--; + + return ci->valid = 1; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool ScanColorInfo ( color_info_t *ci, ccp arg ) +{ + DASSERT(ci); + memset(ci,0,sizeof(*ci)); + if ( arg && *arg ) + { + arg = SkipControls(arg); + + ulong num, r, g, b; + if ( *arg >= 'g' && *arg <= 'z' ) + { + const char mode = *arg; + arg = SkipControls(arg+1); + + num = str2ul(arg,0,10); + switch (mode) + { + case 'm': + return AssignColorInfo(ci,ConvertColorM256ToRGB(num)); + + case 'g': + return AssignColorInfo(ci,ConvertColorM256ToRGB(232 + num % 24)); + + case 'c': + r = num / 100 % 10; + g = num / 10 % 10; + b = num % 10; + goto rgb5; + } + } + else + { + char *end; + num = str2ul(arg,&end,16); + + ci->valid = true; + if ( end - arg <= 3 ) + { + r = num >> 8 & 15; + g = num >> 4 & 15; + b = num & 15; + + rgb5: + if ( r > 5 ) r = 5; + if ( g > 5 ) g = 5; + if ( b > 5 ) b = 5; + return AssignColorInfo(ci,ConvertColorM256ToRGB(16 + 36*r + 6*g + b)); + } + return AssignColorInfo(ci,num); + } + } + + return ci->valid; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void PrintColorInfo ( FILE *f, color_info_t *ci ) +{ + DASSERT(f); + DASSERT(ci); + + if (ci->valid) + fprintf(f," %06x -> %06x : %3u : %u,%u,%u : %2u" + " \e[48;5;16;38;5;%um test \e[0m" + " \e[48;5;244;38;5;%um test \e[0m" + " \e[48;5;231;38;5;%um test \e[0m " + " \e[38;5;16;48;5;%um test \e[0m" + " \e[38;5;244;48;5;%um test \e[0m" + " \e[38;5;231;48;5;%um test \e[0m\n", + ci->src_color, ci->ref_color, + ci->m_index, ci->m_red, ci->m_green, ci->m_blue, ci->m_gray, + ci->m_index, ci->m_index, ci->m_index, + ci->m_index, ci->m_index, ci->m_index ); + else + fputs(" --\n",f); +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError Command_TESTCOLORS ( int argc, char ** argv ) +{ + #if HAVE_PRINT + GetColorByName(colout,"blue"); + #endif + + printf("TEST COLORS: %u arguments:\n",argc); + + bool sep = true; + int idx; + for ( idx = 0; idx < argc; idx++ ) + { + ccp minus = strchr(argv[idx],'-'); + if ( minus || sep ) + { + putchar('\n'); + sep = false; + if ( strlen(argv[idx]) == 1 ) + continue; + } + + color_info_t ci1; + ScanColorInfo(&ci1,argv[idx]); + PrintColorInfo(stdout,&ci1); + + if (!minus) + continue; + + color_info_t ci2; + ScanColorInfo(&ci2,minus+1); + if ( ci2.ref_color == ci1.ref_color ) + continue; + + int r1 = ci1.ref_color >> 16 & 0xff; + int g1 = ci1.ref_color >> 8 & 0xff; + int b1 = ci1.ref_color & 0xff; + int r2 = ci2.ref_color >> 16 & 0xff; + int g2 = ci2.ref_color >> 8 & 0xff; + int b2 = ci2.ref_color & 0xff; + + int n_steps = abs( r2 - r1 ); + int diff = abs( g2 - g1 ); + if ( n_steps < diff ) + n_steps = diff; + diff = abs( b2 - b1 ); + if ( n_steps < diff ) + n_steps = diff; + + u8 prev_m = ci1.m_index; + u32 prev_rgb = ci1.ref_color; + int i; + for ( i = 1; i <= n_steps; i++ ) + { + uint r = ( r2 - r1 ) * i / n_steps + r1; + uint g = ( g2 - g1 ) * i / n_steps + g1; + uint b = ( b2 - b1 ) * i / n_steps + b1; + u8 new_m = ConvertColorRGB3ToM256(r,g,b); + if ( prev_m != new_m ) + { + prev_m = new_m; + color_info_t ci3; + AssignColorInfo(&ci3,r<<16|g<<8|b); + if ( prev_rgb != ci3.ref_color ) + { + prev_rgb = ci3.src_color = ci3.ref_color; + PrintColorInfo(stdout,&ci3); + } + } + } + sep = true; + } + putchar('\n'); + return ERR_OK; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// color helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// https://en.wikipedia.org/wiki/ANSI_escape_code +// https://jonasjacek.github.io/colors/ +// +// 0..15: 000000 800000 008000 808000 000080 800080 008080 c0c0c0 +// 808080 ff0000 00ff00 ffff00 0000ff ff00ff 00ffff ffffff +// => real colors varies on implementation +// +// 6x6x6: 0 95 135 175 215 255 == 00 5f 87 af d7 ff +// +// gray: 8 18 28 ... 238 == 08 12 1c 26 30 3a ... e4 ee f8 +// +/////////////////////////////////////////////////////////////////////////////// +// +// mkw-ana testcol 000000-ffffff +// mkw-ana testcol 500-550 550-050 050-055 055-005 005-505 505-500 +// +/////////////////////////////////////////////////////////////////////////////// + +u32 ColorTab_M0_M15[16] = +{ + 0x000000, 0x800000, 0x008000, 0x808000, + 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, + 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, +}; + +/////////////////////////////////////////////////////////////////////////////// + +u8 ConvertColorRGB3ToM256 ( u8 r, u8 g, u8 b ) +{ + // 0..5 + const uint r6 = SingleColorToM6(r); + const uint g6 = SingleColorToM6(g); + const uint b6 = SingleColorToM6(b); + + u8 m256 = 36*r6 + 6*g6 + b6 + 16; + uint delta = abs ( ( r6 ? 55 + 40 * r6 : 0 ) - r ) + + abs ( ( g6 ? 55 + 40 * g6 : 0 ) - g ) + + abs ( ( b6 ? 55 + 40 * b6 : 0 ) - b ); + + //printf("%02x>%u %02x>%u %02x>%u : m=%u delta=%d\n",r,r6,g,g6,b,b6,m256,delta); + + uint m, gray, prev = 0x1000000; + for ( m = 232, gray = 8; m < 256; m++, gray += 10 ) + { + const uint d = abs( gray - r ) + abs( gray - g ) + abs( gray - b ); + if ( d > prev ) + break; + if ( d <= delta ) + { + delta = d; + m256 = m; + } + prev = d; + } + + return m256; +} + +/////////////////////////////////////////////////////////////////////////////// + +u8 ConvertColorRGBToM256 ( u32 rgb ) +{ + return ConvertColorRGB3ToM256( rgb >> 16, rgb >> 8, rgb ); +} + +/////////////////////////////////////////////////////////////////////////////// + +u32 ConvertColorM256ToRGB ( u8 m256 ) +{ + if ( m256 < 16 ) + return ColorTab_M0_M15[m256]; + + if ( m256 < 232 ) + { + m256 -= 16; + const uint r = m256 / 36; + const uint g = m256 / 6 % 6; + const uint b = m256 % 6; + + u32 col = 0; + if (r) col |= 0x370000 + 0x280000 * r; + if (g) col |= 0x003700 + 0x002800 * g; + if (b) col |= 0x000037 + 0x000028 * b; + return col; + } + + m256 -= 232; + return 0x080808 + m256 * 0x0a0a0a; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// dynamic data support /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetDD ( DynData_t *dd ) +{ + if (dd) + { + FREE(dd->data); + dd->data = 0; + dd->len = dd->size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertDD ( DynData_t *dd, const void *data, uint data_size ) +{ + DASSERT(dd); + DASSERT( !dd->data == !dd->size ); + DASSERT( dd->len <= dd->size ); + + if ( !data || !data_size ) + dd->len = 0; + else + { + if ( data_size > dd->size ) + { + FREE(dd->data); + dd->data = MALLOC(data_size); + dd->size = data_size; + } + + memcpy(dd->data,data,data_size); + dd->len = data_size; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetDDL ( DynDataList_t *ddl ) +{ + if (ddl) + { + DynData_t *dd = ddl->list; + DynData_t *end = dd + ddl->used; + for ( ; dd < end; dd++ ) + FREE(dd->data); + + FREE(ddl->list); + ddl->list = 0; + ddl->used = ddl->size = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +DynData_t *InsertDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to change + const void *data, // NULL or data to assign + uint data_size // size of data +) +{ + DASSERT(ddl); + DASSERT( !ddl->list == !ddl->size ); + DASSERT( ddl->used <= ddl->size ); + + if ( index >= ddl->size ) + { + const uint new_size = 11 * index / 10 + 10; + ddl->list = REALLOC( ddl->list, sizeof(*ddl->list) * new_size ); + memset( ddl->list + ddl->size, 0, (new_size - ddl->size) * sizeof(*ddl->list) ); + ddl->size = new_size; + } + + if ( ddl->used <= index ) + { + uint idx; + for ( idx = ddl->used; idx < index; idx++ ) + ddl->list[idx].len = 0; + ddl->used = index+1; + } + + DynData_t *dd = ddl->list + index; + InsertDD(dd,data,data_size); + return dd; +} + +/////////////////////////////////////////////////////////////////////////////// + +DynData_t *GetDDL +( + DynDataList_t *ddl, // valid dynamic data list + uint index, // element index to get + uint create // create an empty element if not exist +) +{ + DASSERT(ddl); + DASSERT( !ddl->list == !ddl->size ); + DASSERT( ddl->used <= ddl->size ); + + return index < ddl->used + ? ddl->list + ddl->used + : create + ? InsertDDL(ddl, index >= create ? index : create-1, 0,0) + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// StringField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetStringField ( StringField_t * sf ) +{ + DASSERT(sf); + if (sf) + { + if ( sf->used > 0 ) + { + DASSERT(sf->field); + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + FreeString(*ptr); + } + FREE(sf->field); + + int (*func_cmp)( ccp s1, ccp s2 ) = sf->func_cmp; + InitializeStringField(sf); + sf->func_cmp = func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveStringField ( StringField_t * dest, StringField_t * src ) +{ + DASSERT(src); + DASSERT(dest); + if ( src != dest ) + { + ResetStringField(dest); + dest->field = src->field; + dest->used = src->used; + dest->size = src->size; + dest->func_cmp = src->func_cmp; + + InitializeStringField(src); + src->func_cmp = dest->func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindStringFieldIndex ( const StringField_t * sf, ccp key, int not_found_value ) +{ + bool found; + const int idx = FindStringFieldHelper(sf,&found,key); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp FindStringField ( const StringField_t * sf, ccp key ) +{ + bool found; + const int idx = FindStringFieldHelper(sf,&found,key); + return found ? sf->field[idx] : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp * InsertStringFieldHelper ( StringField_t * sf, int idx ) +{ + DASSERT(sf); + DASSERT( sf->used <= sf->size ); + noPRINT("+SF: %u/%u/%u\n",idx,sf->used,sf->size); + if ( sf->used == sf->size ) + { + sf->size += 0x100; + sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field)); + } + DASSERT( idx <= sf->used ); + ccp * dest = sf->field + idx; + memmove(dest+1,dest,(sf->used-idx)*sizeof(*dest)); + sf->used++; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool InsertStringField ( StringField_t * sf, ccp key, bool move_key ) +{ + if (!key) + return 0; + + bool found; + int idx = FindStringFieldHelper(sf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + } + else + { + ccp * dest = InsertStringFieldHelper(sf,idx); + *dest = move_key ? key : STRDUP(key); + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveStringField ( StringField_t * sf, ccp key ) +{ + bool found; + uint idx = FindStringFieldHelper(sf,&found,key); + if (found) + { + sf->used--; + ASSERT( idx <= sf->used ); + ccp * dest = sf->field + idx; + FreeString(*dest); + memmove(dest,dest+1,(sf->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveStringFieldByIndex ( StringField_t * sf, int index, int n ) +{ + DASSERT(sf); + + if ( index < 0 ) + index += sf->used; + if ( n < 0 ) + { + index += n; + n -= n; + } + + if ( index < 0 ) + index = 0; + int last = index + n; + if ( last > sf->used ) + last = sf->used; + if ( index >= last ) + return 0; + + ccp * src = sf->field + index; + ccp * end = sf->field + last; + while ( src < end ) + FreeString(*src++); + + memmove( sf->field + index, src, ( sf->used - last) * sizeof(*sf->field) ); + const uint rm = last - index; + sf->used -= rm; + return rm; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AppendStringField ( StringField_t * sf, ccp key, bool move_key ) +{ + DASSERT(sf); + if (key) + { + DASSERT( sf->used <= sf->size ); + noPRINT(">SF: %u/%u\n",sf->used,sf->size); + if ( sf->used == sf->size ) + { + sf->size += 0x100; + sf->field = REALLOC(sf->field,sf->size*sizeof(*sf->field)); + } + TRACE("AppendStringField(%s,%d) %d/%d\n",key,move_key,sf->used,sf->size); + ccp *dest = sf->field + sf->used++; + *dest = move_key ? key : STRDUP(key); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SortStringField ( StringField_t * sf ) +{ + DASSERT(sf); + if ( sf->used > 1 ) + { + StringField_t temp; + InitializeStringField(&temp); + temp.func_cmp = sf->func_cmp; + uint i; + for ( i = 0; i < sf->used; i++ ) + InsertStringField(&temp,sf->field[i],true); + sf->used = 0; + MoveStringField(sf,&temp); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindStringFieldHelper ( const StringField_t * sf, bool * p_found, ccp key ) +{ + DASSERT(sf); + + int (*cmp)( ccp s1, ccp s2 ) = sf->func_cmp ? sf->func_cmp : strcmp; + + int beg = 0; + if ( sf && key ) + { + int end = sf->used - 1; + while ( beg <= end ) + { + const uint idx = (beg+end)/2; + const int stat = cmp(key,sf->field[idx]); + if ( stat < 0 ) + end = idx - 1; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noTRACE("FindStringFieldHelper(%s) FOUND=%d/%d/%d\n", + key, idx, sf->used, sf->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noTRACE("FindStringFieldHelper(%s) failed=%d/%d/%d\n", + key, beg, sf->used, sf->size ); + + if (p_found) + *p_found = false; + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +enumError LoadStringField +( + StringField_t * sf, // string field + bool init_sf, // true: initialize 'sf' first + bool sort, // true: sort 'sf' + ccp filename, // filename of source file + bool silent // true: don't print open/read errors +) +{ + ASSERT(sf); + DASSERT(filename); + DASSERT(*filename); + + TRACE("LoadStringField(%p,%d,%d,%s,%d)\n",sf,init_sf,sort,filename,silent); + + if (init_sf) + InitializeStringField(sf); + + FILE * f = fopen(filename,"rb"); + if (!f) + { + if (!silent) + ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",filename); + return ERR_CANT_OPEN; + } + + char iobuf[10000]; + while (fgets(iobuf,sizeof(iobuf)-1,f)) + { + char *ptr = iobuf; + while (*ptr) + ptr++; + if ( ptr > iobuf && ptr[-1] == '\n' ) + { + ptr--; + if ( ptr > iobuf && ptr[-1] == '\r' ) + ptr--; + } + + if ( ptr > iobuf ) + { + *ptr++ = 0; + const size_t len = ptr-iobuf; + ptr = MALLOC(len); + memcpy(ptr,iobuf,len); + if (sort) + InsertStringField(sf,ptr,true); + else + AppendStringField(sf,ptr,true); + } + } + + fclose(f); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError SaveStringField +( + StringField_t * sf, // valid string field + ccp filename, // filename of dest file + bool rm_if_empty // true: rm dest file if 'sf' is empty +) +{ + ASSERT(sf); + ASSERT(filename); + ASSERT(*filename); + + TRACE("SaveStringField(%p,%s,%d)\n",sf,filename,rm_if_empty); + + if ( !sf->used && rm_if_empty ) + { + unlink(filename); + return ERR_OK; + } + + FILE * f = fopen(filename,"wb"); + if (!f) + return ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",filename); + + enumError err = WriteStringField(f,filename,sf,0,0); + fclose(f); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +enumError WriteStringField +( + FILE * f, // open destination file + ccp filename, // NULL or filename (needed on write error) + StringField_t * sf, // valid string field + ccp prefix, // not NULL: insert prefix before each line + ccp eol // end of line text (if NULL: use LF) +) +{ + if (!prefix) + prefix = ""; + if (!eol) + eol = "\n"; + + ccp *ptr = sf->field, *end; + for ( end = ptr + sf->used; ptr < end; ptr++ ) + if ( fprintf(f,"%s%s%s",prefix,*ptr,eol) < 0 ) + return ERROR1(ERR_WRITE_FAILED, + "Error while writing string list: %s\n", + filename ? filename : "?" ); + return ERR_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateStringField +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name, up to 40 chars + uint tab_pos, // tab pos of '=' + const StringField_t *sf, // valid string field + EncodeMode_t emode // encoding mode +) +{ + DASSERT(f); + DASSERT(sf); + + if (!name_prefix) + name_prefix = EmptyString; + + int base = 8*tab_pos + 7 - strlen(name_prefix); + fprintf(f,"%sn%.*s= %u\n", name_prefix, (base-1)/8, Tabs20, sf->used ); + + char buf[5000], name[10]; + + uint i; + for ( i = 0; i < sf->used; i++ ) + { + const uint len = snprintf(name,sizeof(name),"%u",i); + mem_t mem = EncodeByModeMem(buf,sizeof(buf),sf->field[i],-1,emode); + fprintf(f, "%s%s%.*s= %s\n", + name_prefix, name, (base-len)/8, Tabs20, mem.ptr ); + if ( mem.ptr != buf ) + FreeMem(&mem); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateStringField +( + RestoreState_t *rs, // info data, may be modified + ccp name_prefix, // NULL or prefix of name, up to 40 chars + StringField_t *sf, // string field + bool init_sf, // true: initialize 'sf', otherwise reset + EncodeMode_t emode // encoding mode +) +{ + DASSERT(rs); + DASSERT(sf); + + if (init_sf) + InitializeStringField(sf); + else + ResetStringField(sf); + + if (!name_prefix) + name_prefix = EmptyString; + + char name[50]; + snprintf(name,sizeof(name),"%sn",name_prefix); + int n = GetParamFieldINT(rs,name,0); + PRINT("RestoreStateStringField(), N=%d\n",n); + if ( n > 0 ) + { + sf->size = n; + sf->field = MALLOC(sf->size*sizeof(*sf->field)); + + uint i; + for ( i = 0; i < n; i++ ) + { + snprintf(name,sizeof(name),"%s%u",name_prefix,i); + ParamFieldItem_t *pfi = GetParamField(rs,name); + if (pfi) + { + mem_t mem = DecodeByModeMem(0,0,pfi->data,-1,emode); + AppendStringField(sf,mem.ptr,true); + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ParamField_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeParamField ( ParamField_t * pf ) +{ + DASSERT(pf); + memset(pf,0,sizeof(*pf)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetParamField ( ParamField_t * pf ) +{ + DASSERT(pf); + if (pf) + { + if ( pf->used > 0 ) + { + DASSERT(pf->field); + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + { + FreeString(ptr->key); + if (pf->free_data) + FREE(ptr->data); + } + } + FREE(pf->field); + + const bool free_data = pf->free_data; + int (*func_cmp)( ccp s1, ccp s2 ) = pf->func_cmp; + InitializeParamField(pf); + pf->free_data = free_data; + pf->func_cmp = func_cmp; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveParamField ( ParamField_t * dest, ParamField_t * src ) +{ + DASSERT(src); + DASSERT(dest); + if ( src != dest ) + { + ResetParamField(dest); + dest->field = src->field; + dest->used = src->used; + dest->size = src->size; + dest->free_data = src->free_data; + InitializeParamField(src); + src->free_data = dest->free_data; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int FindParamFieldIndex ( const ParamField_t * pf, ccp key, int not_found_value ) +{ + bool found; + const int idx = FindParamFieldHelper(pf,&found,key); + return found ? idx : not_found_value; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindParamField ( const ParamField_t * pf, ccp key ) +{ + bool found; + const int idx = FindParamFieldHelper(pf,&found,key); + return found ? pf->field + idx : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindParamFieldByNum ( const ParamField_t * pf, uint num ) +{ + DASSERT(pf); + noTRACE("FindParamFieldByNum(%x)\n",num); + + ParamFieldItem_t *ptr = pf->field, *end; + for ( end = ptr + pf->used; ptr < end; ptr++ ) + if ( ptr->num == num ) + return ptr; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +static ParamFieldItem_t * InsertParamFieldHelper ( ParamField_t * pf, int idx ) +{ + DASSERT(pf); + DASSERT( pf->used <= pf->size ); + noPRINT("+SF: %u/%u/%u\n",idx,pf->used,pf->size); + if ( pf->used == pf->size ) + { + pf->size += 0x100; + pf->field = REALLOC(pf->field,pf->size*sizeof(*pf->field)); + } + DASSERT( idx <= pf->used ); + ParamFieldItem_t * dest = pf->field + idx; + memmove(dest+1,dest,(pf->used-idx)*sizeof(*dest)); + pf->used++; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * FindInsertParamField + ( ParamField_t * pf, ccp key, bool move_key, bool *old_found ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + ParamFieldItem_t *item; + if (found) + { + if (move_key) + FreeString(key); + item = pf->field + idx; + } + else + { + item = InsertParamFieldHelper(pf,idx); + item->key = move_key ? key : STRDUP(key); + item->num = 0; + item->data = 0; + } + + if (old_found) + *old_found = found; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * CountParamField ( ParamField_t * pf, ccp key, bool move_key ) +{ + ParamFieldItem_t *item = FindInsertParamField(pf,key,move_key,0); + if (item) + item->num++; + return item; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool InsertParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + if (pf->free_data) + FREE((void*)data); + } + else + { + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * InsertParamFieldEx + ( ParamField_t * pf, ccp key, bool move_key, uint num, bool *p_found ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (p_found) + *p_found = found; + + if (found) + { + if (move_key) + FreeString(key); + return pf->field + idx; + } + + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = 0; + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ReplaceParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + if (!key) + return 0; + + noTRACE("InsertParamField(%s,%x)\n",key,num); + + bool found; + int idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + if (move_key) + FreeString(key); + + ParamFieldItem_t * dest = pf->field + idx; + dest->num = num; + FREE(dest->data); + dest->data = (void*)data; + } + else + { + ParamFieldItem_t * dest = InsertParamFieldHelper(pf,idx); + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + } + + return !found; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveParamField ( ParamField_t * pf, ccp key ) +{ + bool found; + uint idx = FindParamFieldHelper(pf,&found,key); + if (found) + { + pf->used--; + ASSERT( idx <= pf->used ); + ParamFieldItem_t * dest = pf->field + idx; + FreeString(dest->key); + if (pf->free_data) + FREE(dest->data); + memmove(dest,dest+1,(pf->used-idx)*sizeof(*dest)); + } + return found; +} + +/////////////////////////////////////////////////////////////////////////////// + +ParamFieldItem_t * AppendParamField + ( ParamField_t * pf, ccp key, bool move_key, uint num, cvp data ) +{ + DASSERT(pf); + if (key) + { + DASSERT( pf->used <= pf->size ); + noPRINT(">SF: %u/%u\n",pf->used,pf->size); + if ( pf->used == pf->size ) + { + pf->size += 0x100; + pf->field = REALLOC(pf->field,pf->size*sizeof(*pf->field)); + } + TRACE("AppendParamField(%s,%d) %d/%d\n",key,move_key,pf->used,pf->size); + ParamFieldItem_t * dest = pf->field + pf->used++; + dest->key = move_key ? key : STRDUP(key); + dest->num = num; + dest->data = (void*)data; + return dest; + } + + if (pf->free_data) + FREE((void*)data); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindParamFieldHelper ( const ParamField_t * pf, bool * p_found, ccp key ) +{ + DASSERT(pf); + + int (*cmp)( ccp s1, ccp s2 ) = pf->func_cmp ? pf->func_cmp : strcmp; + + int beg = 0; + if ( pf && key ) + { + int end = pf->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + int stat = cmp(key,pf->field[idx].key); + if ( stat < 0 ) + end = idx - 1 ; + else if ( stat > 0 ) + beg = idx + 1; + else + { + noTRACE("FindParamFieldHelper(%s) FOUND=%d/%d/%d\n", + key, idx, pf->used, pf->size ); + if (p_found) + *p_found = true; + return idx; + } + } + } + + noTRACE("FindParamFieldHelper(%s) failed=%d/%d/%d\n", + key, beg, pf->used, pf->size ); + + if (p_found) + *p_found = false; + return beg; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Maps /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + memset(mm,0,sizeof(*mm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + + uint i; + if (mm->field) + { + for ( i = 0; i < mm->used; i++ ) + FREE(mm->field[i]); + FREE(mm->field); + } + memset(mm,0,sizeof(*mm)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +) +{ + DASSERT(mm1); + ResetMemMap(mm1); + MergeMemMap(mm1,mm2,use_tie); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MergeMemMap +( + MemMap_t * mm1, // merged mem map, not NULL, not cleared + const MemMap_t * mm2, // NULL or second mem map + bool use_tie // TRUE: use InsertMemMapTie() +) +{ + DASSERT(mm1); + if (mm2) + { + uint i; + for ( i = 0; i < mm2->used; i++ ) + { + const MemMapItem_t *src = mm2->field[i]; + if (src) + { + MemMapItem_t *it = use_tie + ? InsertMemMapTie(mm1,src->off,src->size) + : InsertMemMap(mm1,src->off,src->size); + if (it) + StringCopyS(it->info,sizeof(it->info),src->info); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * FindMemMap ( MemMap_t * mm, off_t off, off_t size ) +{ + DASSERT(mm); + + off_t off_end = off + size; + int beg = 0; + int end = mm->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + MemMapItem_t * mi = mm->field[idx]; + if ( off_end <= mi->off ) + end = idx - 1 ; + else if ( off >= mi->off + mi->size ) + beg = idx + 1; + else + return mi; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertMemMapIndex +( + // returns the index of the new item + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + DASSERT(mm); + uint idx = FindMemMapHelper(mm,off,size); + + DASSERT( mm->used <= mm->size ); + if ( mm->used == mm->size ) + { + mm->size += 64; + mm->field = REALLOC(mm->field,mm->size*sizeof(*mm->field)); + } + + DASSERT( idx <= mm->used ); + MemMapItem_t ** dest = mm->field + idx; + memmove(dest+1,dest,(mm->used-idx)*sizeof(*dest)); + mm->used++; + + MemMapItem_t * mi = MALLOC(sizeof(MemMapItem_t)); + mi->off = off; + mi->size = size; + mi->overlap = 0; + *dest = mi; + return idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * InsertMemMap +( + // returns a pointer to a new item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + const uint idx = InsertMemMapIndex(mm,off,size); + // a C sequence point is important here + return mm->field[idx]; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool TieMemMap +( + // returns true if element are tied togehther + + MemMap_t * mm, // mem map pointer + uint idx, // tie element 'idx' and 'idx+1' + bool force // always tie and not only if overlapped +) +{ + DASSERT(mm); + DASSERT( idx+1 < mm->used ); + + MemMapItem_t * i1 = mm->field[idx]; + MemMapItem_t * i2 = mm->field[idx+1]; + if ( force || i1->off + i1->size >= i2->off ) + { + const off_t new_size = i2->off + i2->size - i1->off; + if ( i1->size < new_size ) + i1->size = new_size; + FREE(i2); + idx++; + mm->used--; + memmove( mm->field + idx, + mm->field + idx + 1, + ( mm->used - idx ) * sizeof(MemMapItem_t*) ); + + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +MemMapItem_t * InsertMemMapTie +( + // returns a pointer to a new or existing item (never NULL) + + MemMap_t * mm, // mem map pointer + off_t off, // offset of area + off_t size // size of area +) +{ + uint idx = InsertMemMapIndex(mm,off,size); + + if ( idx > 0 && TieMemMap(mm,idx-1,false) ) + idx--; + + while ( idx + 1 < mm->used && TieMemMap(mm,idx,false) ) + ; + + return mm->field[idx]; +} + +/////////////////////////////////////////////////////////////////////////////// + +void InsertMemMapWrapper +( + void * param, // user defined parameter + u64 offset, // offset of object + u64 size, // size of object + ccp info // info about object +) +{ + noTRACE("InsertMemMapWrapper(%p,%llx,%llx,%s)\n",param,offset,size,info); + DASSERT(param); + MemMapItem_t * mi = InsertMemMap(param,offset,size); + StringCopyS(mi->info,sizeof(mi->info),info); +} + +/////////////////////////////////////////////////////////////////////////////// + +uint FindMemMapHelper ( MemMap_t * mm, off_t off, off_t size ) +{ + DASSERT(mm); + + int beg = 0; + int end = mm->used - 1; + while ( beg <= end ) + { + uint idx = (beg+end)/2; + MemMapItem_t * mi = mm->field[idx]; + if ( off < mi->off ) + end = idx - 1 ; + else if ( off > mi->off ) + beg = idx + 1; + else if ( size < mi->size ) + end = idx - 1 ; + else if ( size > mi->size ) + beg = idx + 1; + else + { + TRACE("FindMemMapHelper(%llx,%llx) FOUND=%d/%d/%d\n", + (u64)off, (u64)size, idx, mm->used, mm->size ); + return idx; + } + } + + TRACE("FindStringFieldHelper(%llx,%llx) failed=%d/%d/%d\n", + (u64)off, (u64)size, beg, mm->used, mm->size ); + return beg; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CalcOverlapMemMap ( MemMap_t * mm ) +{ + DASSERT(mm); + + uint i, count = 0; + MemMapItem_t * prev = 0; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + ptr->overlap = 0; + if ( prev && ptr->off < prev->off + prev->size ) + { + ptr ->overlap |= 1; + prev->overlap |= 2; + count++; + } + prev = ptr; + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +u64 FindFreeSpaceMemMap +( + // return address or NULL on not-found + + const MemMap_t * mm, + u64 addr_beg, // first possible address + u64 addr_end, // last possible address + u64 size, // minimal size + u64 align, // aligning + u64 *space // not NULL: store available space here +) +{ + DASSERT(mm); + + if (!size) + goto term; + + if (!align) + align = 1; + addr_beg = ALIGN64(addr_beg,align); + + uint i; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t *mi = mm->field[i]; + const u64 end = ALIGN64( mi->off + mi->size, align ); + if ( mi->off > addr_beg ) + { + u64 found_space = ALIGNOFF64(mi->off,align) - addr_beg; + if ( size <= found_space ) + { + if (space) + *space = found_space; + return addr_beg; + } + } + if ( addr_beg < end ) + addr_beg = end; + } + + u64 found_space = ALIGNOFF64(addr_end,align) - addr_beg; + if ( size <= found_space ) + { + if (space) + *space = found_space; + return addr_beg; + } + + term: + if (space) + *space = 0; + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrintMemMap ( MemMap_t * mm, FILE * f, int indent, ccp info_head ) +{ + DASSERT(mm); + if ( !f || !mm->used ) + return; + + CalcOverlapMemMap(mm); + indent = NormalizeIndent(indent); + + static char ovl[][3] = { " ", "!.", ".!", "!!" }; + + if (!info_head) + info_head = "info"; + int i, max_ilen = strlen(info_head); + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + ptr->info[sizeof(ptr->info)-1] = 0; + const int ilen = strlen(ptr->info); + if ( max_ilen < ilen ) + max_ilen = ilen; + } + + fprintf(f,"%*s unused : off(beg) .. off(end) : size : %s\n%*s%.*s\n", + indent, "", info_head, + indent, "", max_ilen+52, Minus300 ); + + off_t max_end = mm->begin; + for ( i = 0; i < mm->used; i++ ) + { + MemMapItem_t * ptr = mm->field[i]; + if ( !i && max_end > ptr->off ) + max_end = ptr->off; + const off_t end = ptr->off + ptr->size; + if ( ptr->off > max_end ) + fprintf(f,"%*s%s%10llx :%10llx ..%10llx :%10llx : %s\n", + indent, "", ovl[ptr->overlap&3], (u64)( ptr->off - max_end ), + (u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info ); + else + fprintf(f,"%*s%s :%10llx ..%10llx :%10llx : %s\n", + indent, "", ovl[ptr->overlap&3], + (u64)ptr->off, (u64)end, (u64)ptr->size, ptr->info ); + if ( max_end < end ) + max_end = end; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Memory Map: Scan addresses /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanAddress +( + // returns first not scanned char of 'arg' + + ScanAddr_t * result, // result, initialized by ScanAddress() + ccp arg, // format: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + DASSERT(result); + memset(result,0,sizeof(*result)); + if (!arg) + return 0; + + ccp end = ScanUINT(&result->addr,arg,default_base); + if ( end == arg ) + return (char*)arg; + arg = end; + while ( *arg == ' ' || *arg == '\t' ) + arg++; + result->size = 1; + result->stat = 1; + + if ( *arg == ':' ) + { + uint addr2; + end = ScanUINT(&addr2,++arg,default_base); + if ( end == arg ) + return(char*) arg-1; + + result->size = addr2 > result->addr ? addr2 - result->addr : 0; + result->stat = 2; + } + else if ( *arg == '#' ) + { + end = ScanUINT(&result->size,++arg,default_base); + result->stat = 2; + } + + return (char*)end; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertAddressMemMap +( + // returns the number of added addresses + + MemMap_t * mm, // mem map pointer + bool use_tie, // TRUE: use InsertMemMapTie() + ccp arg, // comma separated list of addresses + // formats: ADDR | ADDR:END | ADDR#SIZE + uint default_base // base for numbers without '0x' prefix + // 0: C like with octal support + // 10: standard value for decimal numbers + // 16: standard value for hex numbers +) +{ + DASSERT(mm); + if (!arg) + return 0; + + uint count = 0; + while (*arg) + { + ScanAddr_t addr; + arg = ScanAddress(&addr,arg,default_base); + if (addr.stat) + { + if (use_tie) + InsertMemMapTie(mm,addr.addr,addr.size); + else + InsertMemMap(mm,addr.addr,addr.size); + count++; + } + + while ( *arg && *arg != ' ' && *arg != '\t' && *arg != ',' ) + arg++; + while ( *arg == ' ' || *arg == '\t' || *arg == ',' ) + arg++; + } + + return count; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// DataBuf_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeDataBuf + ( DataBuf_t *db, uint buf_size, DataBufFill_t func, void *user_data ) +{ + DASSERT(db); + memset(db,0,sizeof(*db)); + + if (buf_size) + { + db->size = buf_size; + db->buf = MALLOC(buf_size); + db->buf_end = db->buf + buf_size; + db->data = db->data_end = db->buf; + } + + db->fill_func = func; + db->user_data = user_data; + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetDataBuf ( DataBuf_t *db ) +{ + if (db) + { + DASSERT(!DumpDataBuf(stderr,0,db,true)); + FREE(db->buf); + memset(db,0,sizeof(*db)); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResizeDataBuf ( DataBuf_t *db, uint new_buf_size ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + DASSERT(new_buf_size>0); + + if (!db->buf) + return; + + PackDataBuf(db); + db->buf = REALLOC(db->buf,new_buf_size); + db->buf_end = db->buf + new_buf_size; + db->size = new_buf_size; + if ( db->used > new_buf_size ) + db->used = new_buf_size; + db->data = db->buf; + db->data_end = db->data + db->used; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearDataBuf ( DataBuf_t *db, uint new_buf_size_if_not_null ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + db->offset = 0; + db->data = db->data_end = db->buf; + db->used = 0; + db->offset = 0; + if (new_buf_size_if_not_null) + ResizeDataBuf(db,new_buf_size_if_not_null); + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void EmptyDataBuf ( DataBuf_t *db ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + db->data = db->data_end = db->buf; + db->used = 0; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void PackDataBuf ( DataBuf_t *db ) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( !db->used || !db->buf ) + { + db->data = db->data_end = db->buf; + } + else if ( db->data > db->buf ) + { + if ( db->data < db->data_end ) + { + // easy case: FREE1 DATA FREE2 + + memmove(db->buf,db->data,db->used); + } + else + { + // case: DATA2 FREE DATA1 + + uint d1len = db->buf_end - db->data; + uint d2len = db->data_end - db->buf; + DASSERT( d1len + d2len == db->used ); + + #ifdef TEST0 + u8 temp[10]; + #else + u8 temp[0x8000]; + #endif + if ( d2len <= sizeof(temp) && d2len < d1len ) + { + memcpy(temp,db->buf,d2len); + memmove(db->buf,db->data,d1len); + memcpy(db->buf+d1len,temp,d2len); + } + else if ( d1len <= sizeof(temp) ) + { + memcpy(temp,db->data,d1len); + memmove(db->buf+d1len,db->buf,d2len); + memcpy(db->buf,temp,d1len); + } + else + { + noPRINT("PACK %u+%u/%u, temp=%zu\n",d2len,d1len,db->size,sizeof(temp)); + // use a dynamic buffer => [2do]] optimize later + u8 *temp = MEMDUP(db->buf,d2len); + memmove(db->buf,db->data,d1len); + memcpy(db->buf+d1len,temp,d2len); + FREE(temp); + } + } + db->data = db->buf; + db->data_end = db->data + db->used; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// debugging + +bool IsValidDataBuf ( DataBuf_t *db ) +{ + if ( !db || db->used > db->size ) + return false; + + if (db->buf) + { + if ( db->buf_end != db->buf + db->size + || db->data < db->buf || db->data >= db->buf_end ) + return false; + + u8 *end = db->data + db->used; + if ( end > db->buf_end ) + end -= db->size; + return end == db->data_end; + } + + return !db->size && !db->buf_end && !db->data && !db->data_end; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool DumpDataBuf ( FILE *f, uint indent, DataBuf_t *db, bool if_invalid ) +{ + DASSERT(f); + DASSERT(db); + if ( if_invalid && IsValidDataBuf(db) ) + return false; + + fflush(stdout); + fflush(stderr); + indent = NormalizeIndent(indent); + if (!db) + { + fprintf(f,"%*sdata_buf_t: NULL\n",indent,""); + return true; + } + + if (!db->buf) + { + fprintf(f,"%*sdata_buf_t buf: Invalid buf\n",indent,""); + return true; + } + + fprintf(f,"%*sdata_buf_t %p .. %d, data %d .. %d, used %u/%u, off=%llu\n", + indent,"", db->buf, (int)(db->buf_end - db->buf), + (int)(db->data - db->buf), (int)(db->data_end - db->buf), + db->used, db->size, db->offset ); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void FillPatternDataBuf ( DataBuf_t *db ) +{ + // fill the buffer with a test pattern: + // '-' for unused bytes + // ">0123456789<" in a longer or shorter form for used bytes + + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + memset(db->buf,'-',db->size); + + if ( db->used <= 1 ) + { + if ( db->used == 1 ) + *db->data = '+'; + return; + } + + u8 *ptr = db->data; + u8 *end = ptr + db->used - 1; + if ( end >= db->buf_end ) + end -= db->size; + *ptr++ = '>'; + *end = '<'; + uint i = 0, len = end - ptr; + if ( len > db->size ) + len += db->size; + for(;;) + { + if ( ptr == db->buf_end ) + ptr = db->buf; + if ( ptr == end ) + break; + *ptr++ = 'a' + i/len; + i += 26; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint InsertDataBuf +( + // Insert at top of buffer. + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + uint space = db->size - db->used; + if ( data_size > space ) + { + if (all_or_none) + return 0; + data_size = space; + } + + if (!db->used) + db->data = db->data_end = db->buf + data_size; + db->used += data_size; + + uint copy_len = db->data - db->buf; + if ( copy_len > data_size ) + copy_len = data_size; + memcpy(db->data-copy_len,data,copy_len); + db->data -= copy_len; + if ( data_size > copy_len ) + { + const uint copy_len2 = data_size - copy_len; + memcpy(db->buf_end-copy_len2,(u8*)data+copy_len,copy_len2); + db->data = db->buf_end - copy_len2; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return data_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendDataBuf +( + // Append data at end of buffer + // Return the number of inserted bytes + + DataBuf_t *db, // valid pointer to data buffer + const void *data, // data to insert + uint data_size, // size of 'data' + bool all_or_none // true: insert complete data or nothing +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + uint space = db->size - db->used; + noPRINT("APPEND %u/%u [%u/%u]\n",data_size,space,db->used,db->size); + if ( data_size > space ) + { + if (all_or_none) + return 0; + data_size = space; + } + + if (!db->used) + db->data = db->data_end = db->buf; + db->used += data_size; + + uint copy_len = db->buf_end - db->data_end; + if ( copy_len > data_size ) + copy_len = data_size; + memcpy(db->data_end,data,copy_len); + db->data_end += copy_len; + if ( data_size > copy_len ) + { + const uint copy_len2 = data_size - copy_len; + memcpy(db->buf,(u8*)data+copy_len,copy_len2); + db->data_end = db->buf + copy_len2; + } + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return data_size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void * GetPtrDataBuf +( + // Return a pointer to the data and make sure, + // that the data is not wrapped. Return NULL if failed. + + DataBuf_t *db, // valid pointer to data buffer + uint data_size, // number of needed bytes + uint align, // >1: Data alignment is also needed. + // Only 2,4,8,16,... are allowed, but not verified. + // Begin of buffer is always well aligned. + bool drop // true: On success, drop data from buffer + // In real, the data is available and only + // the pointers and counters are updated. +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if (!data_size) + return db->data; + if ( data_size > db->used ) + { + if ( data_size <= db->size && db->fill_func ) + db->fill_func(db,data_size-db->used,0); + if ( data_size > db->used ) + return 0; + } + + if ( db->data + data_size > db->buf_end ) + PackDataBuf(db); + else if ( align > 1 ) + { + const uint offset = db->data - db->buf; + if ( offset & (align-1) ) + PackDataBuf(db); + } + + u8 *res = db->data; + if (drop) + { + db->data += data_size; + db->used -= data_size; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + } + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint CopyDataBuf +( + // Copy data from buffer, but don't remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + // if 'min_read' > 'size', NULL is returned + uint max_read // try to read upto 'max_read' bytes +) +{ + DASSERT(db); + DASSERT(dest); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( min_read > db->used ) + { + if ( min_read <= db->size && db->fill_func ) + db->fill_func(db,min_read-db->used,0); + if ( min_read > db->used ) + return 0; + } + + if ( max_read > db->used ) + max_read = db->used; + uint copy_len1 = db->buf_end - db->data; + if ( copy_len1 >= max_read ) + memcpy(dest,db->data,max_read); + else + { + memcpy(dest,db->data,copy_len1); + memcpy((u8*)dest+copy_len1,db->buf,max_read-copy_len1); + } + + return max_read; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetDataBuf +( + // Copy data from buffer and remove it + // Return the number of copied bytes + + DataBuf_t *db, // valid pointer to data buffer + void *dest, // destination buffer wit at least 'max_size' space + uint min_read, // return with 0 read bytes if less bytes available + uint max_read // try to read upto 'max_read' bytes +) +{ + DASSERT(db); + DASSERT(dest); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( min_read > db->used ) + { + if ( min_read <= db->size && db->fill_func ) + db->fill_func(db,min_read-db->used,0); + if ( min_read > db->used ) + return 0; + } + + if ( max_read > db->used ) + max_read = db->used; + uint copy_len1 = db->buf_end - db->data; + if ( copy_len1 >= max_read ) + memcpy(dest,db->data,max_read); + else + { + memcpy(dest,db->data,copy_len1); + memcpy((u8*)dest+copy_len1,db->buf,max_read-copy_len1); + } + + db->used -= max_read; + if (db->used) + { + db->data += max_read; + if ( db->data >= db->buf_end ) + db->data -= db->size; + } + else + db->data = db->data_end = db->buf; + + db->offset += max_read; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return max_read; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint SkipDataBuf +( + // Skip 'skip_size' from input stream. Read more data if needed. + // Return the number of really skiped bytes + // If <skip_size is returned, no more data is available. + + DataBuf_t *db, // valid pointer to data buffer + uint skip_size // number of bytes to drop +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + uint skipped = 0; + for(;;) + { + uint stat = DropDataBuf(db,skip_size); + skipped += stat; + skip_size -= stat; + if (!skip_size) + break; + + if (db->fill_func) + { + const uint max_fill = skip_size < db->size ? skip_size : db->size; + db->fill_func(db,max_fill,0); + } + + if (!db->used) + break; + } + + db->offset += skipped; + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return skipped; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropDataBuf +( + // Remove upto 'drop_size' bytes from buffer. + // Return the number of dropped bytes. + + DataBuf_t *db, // valid pointer to data buffer + uint drop_size // number of bytes to drop +) +{ + DASSERT(db); + DASSERT(!DumpDataBuf(stderr,0,db,true)); + + if ( drop_size >= db->used ) + { + drop_size = db->used; + db->used = 0; + db->data = db->data_end = db->buf; + } + else + { + db->data += drop_size; + if ( db->data >= db->buf_end ) + db->data -= db->size; + db->used -= drop_size; + } + db->offset += drop_size; + + DASSERT(!DumpDataBuf(stderr,0,db,true)); + return drop_size; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ResizeElement_t, ResizeHelper_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void InitializeResize +( + ResizeHelper_t *r, // data structure + uint s_size, // size of source + uint d_size // size of dest +) +{ + DASSERT(r); + memset(r,0,sizeof(*r)); + + r->src_size = s_size; + r->dest_size= d_size; + if ( !s_size || !d_size ) + return; + + const uint divisor = gcd(s_size,d_size); + s_size /= divisor; + d_size /= divisor; + + r->sum_factor = s_size; + r->half_factor = r->sum_factor/2; + + r->idx_inc = s_size / d_size; + r->frac_inc = s_size % d_size; + r->frac_next = d_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +static bool CalcResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + ResizeElement_t *re = r->elem; + re[0].n_elem = re[1].n_elem = re[2].n_elem = 0; + DASSERT(!re[3].n_elem); + + if ( r->src_idx >= r->src_size || r->dest_idx >= r->dest_size ) + return false; + + uint sum = r->sum_factor; + if (r->src_frac) + { + uint max = r->frac_next - r->src_frac; + if ( max > sum ) + max = sum; + re->n_elem = 1; + re->factor = max; + re++; + sum -= max; + } + + if ( sum >= r->frac_next ) + { + const uint n = sum / r->frac_next; + re->n_elem = n; + re->factor = r->frac_next; + re++; + sum -= n * r->frac_next; + } + + if ( sum > 0 ) + { + re->n_elem = 1; + re->factor = sum; + re++; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool FirstResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + r->dest_idx = r->src_idx = r->src_frac = 0; + memset(r->elem,0,sizeof(r->elem)); + return CalcResize(r); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool NextResize ( ResizeHelper_t *r ) +{ + DASSERT(r); + + r->dest_idx++; + r->src_idx += r->idx_inc; + r->src_frac += r->frac_inc; + if ( r->src_frac >= r->frac_next ) + { + r->src_frac -= r->frac_next; + r->src_idx++; + } + + return CalcResize(r); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// GrowBuffer_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static const uint grow_buffer_size_factor = 0x100; + +/////////////////////////////////////////////////////////////////////////////// + +void InitializeGrowBuffer ( GrowBuffer_t *gb, uint max_buf_size ) +{ + DASSERT(gb); + memset(gb,0,sizeof(*gb)); + gb->grow_size = grow_buffer_size_factor; + gb->max_size = ( max_buf_size + grow_buffer_size_factor - 1 ) + / grow_buffer_size_factor * grow_buffer_size_factor; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ResetGrowBuffer ( GrowBuffer_t *gb ) +{ + DASSERT(gb); + FREE(gb->buf); + const uint grow_size = gb->grow_size; + InitializeGrowBuffer(gb,gb->max_size); + gb->grow_size = grow_size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint GetSpaceGrowBuffer +( + // returns the maximal possible space + const GrowBuffer_t *gb // valid grow-buffer object +) +{ + DASSERT(gb); + const uint max = gb->max_size > gb->size ? gb->max_size : gb->size; + return max - gb->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrepareGrowBuffer +( + // returns the number of available space, smaller than or equal 'size' + + GrowBuffer_t *gb, // valid grow-buffer object + uint size, // needed size + bool force // always alloc enough space and return 'size' +) +{ + #if HAVE_DASSERT + DASSERT(gb); + if (gb->buf) + { + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + } + else + { + DASSERT(!gb->ptr); + DASSERT(!gb->size); + DASSERT(!gb->used); + } + #endif + + if (!gb->buf) + { + //--- initial setup of buffer + + if ( gb->grow_size < grow_buffer_size_factor ) + gb->grow_size = grow_buffer_size_factor; + else + gb->grow_size = gb->grow_size + / grow_buffer_size_factor * grow_buffer_size_factor; + gb->size = ( size / gb->grow_size + 1 ) * gb->grow_size; + + if ( gb->max_size < grow_buffer_size_factor ) + gb->max_size = grow_buffer_size_factor; + else + gb->max_size = gb->max_size + / grow_buffer_size_factor * grow_buffer_size_factor; + if ( gb->size > gb->max_size ) + gb->size = gb->max_size; + + if ( gb->size < size && force ) + gb->size = ( size + gb->grow_size ) + / gb->grow_size * gb->grow_size - 1; + + gb->buf = gb->ptr = MALLOC(gb->size+1); // 1 extra byte for NULL-TERM + gb->used = 0; + } + else + { + if (!gb->grow_size) + gb->grow_size = grow_buffer_size_factor; + if (!gb->used) + gb->ptr = gb->buf; + } + DASSERT(gb->grow_size); + + uint avail = gb->size - gb->used; + if ( size > avail && ( force || gb->size < gb->max_size ) ) + { + //--- grow buffer + + gb->size = ( size + gb->used + gb->grow_size ) + / gb->grow_size * gb->grow_size - 1; + DASSERT( gb->used <= gb->size ); + u8 *newbuf = MALLOC(gb->size+1); // 1 extra byte for NULL-TERM + memcpy(newbuf,gb->ptr,gb->used); + FREE(gb->buf); + gb->buf = gb->ptr = newbuf; + } + + uint space = ( gb->buf + gb->size ) - ( gb->ptr + gb->used ); + if ( space < size && gb->buf != gb->ptr ) + { + memmove(gb->buf,gb->ptr,gb->used); + gb->ptr = gb->buf; + space = gb->size - gb->used; + } + + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + + return size < space ? size : space; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + const void *data, // data to insert + uint size // size of 'data' +) +{ + DASSERT(gb); + size = PrepareGrowBuffer(gb,size,false); + DASSERT( gb->ptr + gb->used + size <= gb->buf + gb->size ); + memcpy( gb->ptr + gb->used, data, size ); + gb->used += size; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + + DASSERT( gb->used <= gb->size ); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + + return size; +} + +/////////////////////////////////////////////////////////////////////////////// + +char *tiparm ( ccp str, ... ); + +uint InsertCapNGrowBuffer +( + // insert 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +) +{ + DASSERT(gb); + + if ( n <= 0 ) + return 0; + + if ( n == 1 && cap_1.len ) + return InsertMemGrowBuffer(gb,cap_1); + + #if DCLIB_TERMINAL + if (cap_n.len) + { + ccp str = tiparm(cap_n.ptr,n); + if (str) + return InsertGrowBuffer(gb,str,strlen(str)); + } + #endif + + if (!cap_1.len) + return 0; + + const uint total = cap_1.len * n; + PrepareGrowBuffer(gb,total,true); + u8 *ptr = gb->ptr + gb->used; + while ( n-- > 0 ) + { + memcpy(ptr,cap_1.ptr,cap_1.len); + ptr += cap_1.len; + } + gb->used += total; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint WriteCapN +( + // print 'cap_1' or 'cap_n' depending of 'n' and the existence of 'cap_*' + // returns the number of printed bytes + + FILE *f, // valid FILE + int n, // repeat number; if <1: insert nothing + mem_t cap_1, // default for N=1 + mem_t cap_n // default for N>1 +) +{ + DASSERT(f); + + if ( n <= 0 ) + return 0; + + if ( n == 1 && cap_1.len ) + return fwrite(cap_1.ptr,cap_1.len,1,f); + + #if DCLIB_TERMINAL + if (cap_n.len) + { + ccp str = tiparm(cap_n.ptr,n); + if (str) + return fwrite(str,strlen(str),1,f); + } + #endif + + if (!cap_1.len) + return 0; + + const uint total = cap_1.len * n; + while ( n-- > 0 ) + fwrite(cap_1.ptr,cap_1.len,1,f); + return total; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint InsertCatchedGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + SavedStdFiles_t *ssf // saved output and catched data +) +{ + DASSERT(gb); + DASSERT(ssf); + + TermCatchStdFiles(ssf); + uint stat = 0; + if (ssf->data) + { + PrepareGrowBuffer(gb,ssf->size,true); + stat = InsertGrowBuffer(gb,ssf->data,ssf->size); + XFREE(ssf->data); + ssf->data = 0; + ssf->size = 0; + } + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint PrintArgGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + va_list arg // parameters for 'format' +) +{ + DASSERT(gb); + DASSERT(format); + + char buf[10000]; + + va_list arg2; + va_copy(arg2,arg); + int stat = vsnprintf(buf,sizeof(buf),format,arg2); + va_end(arg2); + + if ( stat < sizeof(buf) ) + return InsertGrowBuffer(gb,buf,stat); + + + //--- buffer too small, use dynamic memory + + noPRINT("PrintArgGrowBuffer() -> MALLOC(%u)\n",stat+1); + + char *dest = MALLOC(stat+1); + stat = vsnprintf(dest,stat+1,format,arg); + + const uint res = InsertGrowBuffer(gb,dest,stat); + FREE(dest); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PrintGrowBuffer +( + // returns the number of inserted bytes + + GrowBuffer_t *gb, // valid grow-buffer object + ccp format, // format string for vfprintf() + ... // arguments for 'vsnprintf(format,...)' +) +{ + DASSERT(gb); + DASSERT(format); + + va_list arg; + va_start(arg,format); + enumError err = PrintArgGrowBuffer(gb,format,arg); + va_end(arg); + return err; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ConvertToCrLfGrowBuffer +( + // returns the number of inserted CR bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint begin // index to begin with conversion +) +{ + DASSERT(gb); + uint count = 0; + + if ( begin < gb->used ) + { + //--- count LF without CR + + u8 *ptr = gb->ptr + begin; + u8 *end = gb->ptr + gb->used; + for ( ; ptr < end; ptr++ ) + { + if ( *ptr == '\r' ) + ptr++; + else if ( *ptr == '\n' ) + count++; + } + + if ( count > 0 ) + { + PrepareGrowBuffer(gb,count,true); + u8 *src = gb->ptr + gb->used; + u8 *dest = src + count; + u8 *beg = gb->ptr + begin; + while ( src > beg ) + { + const char ch = *--src; + *--dest = ch; + if ( ch == '\n' && src[-1] != '\r' ) + *--dest = '\r'; + } + DASSERT( src == dest ); + gb->used += count; + gb->ptr[gb->used] = 0; + if ( gb->max_used < gb->used ) + gb->max_used = gb->used; + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ClearGrowBuffer ( GrowBuffer_t *gb ) +{ + DASSERT(gb); + gb->ptr = gb->buf; + if (gb->ptr) + *gb->ptr = 0; + gb->used = 0; + return gb->size; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint PurgeGrowBuffer ( GrowBuffer_t *gb ) +{ + if (!gb->used) + return ClearGrowBuffer(gb); + + if ( gb->ptr > gb->buf ) + { + memmove(gb->buf,gb->ptr,gb->used); + gb->ptr = gb->buf; + } + return gb->size - gb->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint DropGrowBuffer +( + // returns the number of dropped bytes + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // number of bytes to drop +) +{ + #if HAVE_DASSERT + DASSERT(gb); + if (gb->buf) + { + DASSERT_MSG( gb->used < gb->size, "%d/%d\n",gb->used,gb->size); + DASSERT( gb->ptr >= gb->buf ); + DASSERT( gb->ptr + gb->used <= gb->buf + gb->size ); + } + else + { + DASSERT(!gb->ptr); + DASSERT(!gb->size); + DASSERT(!gb->used); + } + #endif + + if (!gb->buf) + return 0; + + if ( size >= gb->used ) + { + size = gb->used; + gb->used = 0; + gb->ptr = gb->buf; + *gb->ptr = 0; + return size; + } + + gb->ptr += size; + gb->used -= size; + return size; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void LogGrowBuffer +( + FILE *f, // output file + int indent, // indention + const GrowBuffer_t *gb, // valid grow-buffer + ccp format, // format string for vfprintf() + ... // arguments for 'vfprintf(format,...)' +) +{ + DASSERT(f); + DASSERT(gb); + indent = NormalizeIndent(indent); + + fprintf(f,"%*sTB: size=%s/%s/%s idx=%s grow=%s/%s disabled=%d", + indent,"", + PrintSize1024(0,0,gb->used,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->max_used,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->size,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintNumberU6(0,0,gb->ptr-gb->buf,true), + PrintSize1024(0,0,gb->grow_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + PrintSize1024(0,0,gb->max_size,DC_SFORM_TINY|DC_SFORM_ALIGN), + gb->disabled ); + + if (format) + { + fputs(" : ",f); + va_list arg; + va_start(arg,format); + vfprintf(f,format,arg); + va_end(arg); + } + + fputc('\n',f); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __APPLE__ + +FILE * OpenFileGrowBuffer +( + // returns a FILE opened by fmemopen() => call CloseFileGrowBuffer() + + GrowBuffer_t *gb, // valid grow-buffer object + uint size // needed size +) +{ + DASSERT(gb); + uint space = PurgeGrowBuffer(gb); + if ( space < size ) + space = PrepareGrowBuffer(gb,size,true); + return fmemopen( gb->ptr + gb->used, space, "w" ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int CloseFileGrowBuffer +( + // returns the number of wirtten bytes, or -1 on error + GrowBuffer_t *gb, // valid grow-buffer object + FILE *f // FILE returned by OpenFileGrowBuffer() +) +{ + DASSERT(gb); + if (!f) + return -1; + + const int stat = ftell(f); + if ( stat >= 0 && gb->ptr + gb->used + stat <= gb->buf + gb->size ) + gb->used += stat; + + fclose(f); + return stat; +} + +#endif // !__APPLE__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define GROW_BUFFER_CHUNK_SIZE 3*1024 // multiple of 3 because of BASE64 (3->4) + +void SaveCurrentStateGrowBuffer +( + FILE *f, // output file + ccp name_prefix, // NULL or prefix of name + uint tab_pos, // tab pos of '=' + const GrowBuffer_t *gb // valid grow-buffer +) +{ + DASSERT(f); + DASSERT(gb); + + if (!name_prefix) + name_prefix = EmptyString; + + const int base = 8*tab_pos + 7 - strlen(name_prefix); + + fprintf(f, + "%s" "disabled%.*s= %u\n" + "%s" "used%.*s= %u\n" + "%s" "size%.*s= %u\n" + "%s" "grow-size%.*s= %u\n" + "%s" "max-size%.*s= %u\n" + ,name_prefix, (base-8)/8, Tabs20, gb->disabled + ,name_prefix, (base-4)/8, Tabs20, gb->used + ,name_prefix, (base-4)/8, Tabs20, gb->size + ,name_prefix, (base-9)/8, Tabs20, gb->grow_size + ,name_prefix, (base-8)/8, Tabs20, gb->max_size + ); + + char buf[4*GROW_BUFFER_CHUNK_SIZE/3+10], name[20]; + uint start,i; + + for ( start = i = 0; start < gb->used; i++ ) + { + uint size = gb->used - start; + if ( size > GROW_BUFFER_CHUNK_SIZE ) + size = GROW_BUFFER_CHUNK_SIZE; + + const uint len = snprintf(name,sizeof(name),"data-%u",i); + EncodeBase64(buf,sizeof(buf),(ccp)gb->ptr+start,size,0,true,0,0); + fprintf(f, "%s%s%.*s= %s\n", + name_prefix, name, (base-len)/8, Tabs20, buf ); + + start += size; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateGrowBuffer +( + GrowBuffer_t *gb, // valid grow-buffer + ccp name_prefix, // NULL or prefix of name + RestoreState_t *rs // info data, can be modified (cleaned after call) +) +{ + DASSERT(gb); + DASSERT(rs); + + ResetGrowBuffer(gb); + + if (!name_prefix) + name_prefix = EmptyString; + + char name[200]; // name buf + snprintf(name,sizeof(name),"%sused",name_prefix); + int size = GetParamFieldUINT(rs,name,0); + snprintf(name,sizeof(name),"%sdisabled",name_prefix); + gb->disabled = GetParamFieldINT(rs,name,gb->disabled) > 0; + + if ( size > 0 ) + { + char buf[2*GROW_BUFFER_CHUNK_SIZE]; + + PrepareGrowBuffer(gb,size,true); + uint index = 0; + while ( size > 0 ) + { + snprintf(name,sizeof(name),"%sdata-%u",name_prefix,index++); + int res = GetParamFieldBUF(buf,sizeof(buf),rs,name,ENCODE_BASE64URL,0); + if ( res < 0 ) + break; + + InsertGrowBuffer(gb,buf,res); + size -= res; + } + + #ifdef TEST0 + if ( gb->used > 10000 ) + { + fprintf(stderr,"--- ResetGrowBuffer() size = %d\n",gb->used); + fwrite(gb->ptr,1,gb->used,stderr); + fprintf(stderr,"--- ResetGrowBuffer() size = %d\n",gb->used); + } + #endif + } + + + //--- known as 'unused' + + if ( rs->log_mode & RSL_UNUSED_NAMES ) + { + static ccp name_tab[] = + { + "size", + "grow-size", + "max-size", + 0 + }; + + ccp *np; + for ( np = name_tab; *np; np++ ) + { + StringCat2S(name,sizeof(name),name_prefix,*np); + GetParamField(rs,name); + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int opt_new = 0; + +/////////////////////////////////////////////////////////////////////////////// + +uint CreateUniqueId ( int range ) +{ + static uint unique_id = 1; + const uint ret = unique_id; + if ( range > 0 ) + unique_id += range; + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// + +void Sha1Hex2Bin ( sha1_hash_t bin, ccp src, ccp end ) +{ + DASSERT(bin); + DASSERT(src); + + uint i; + for ( i = 0; i < 20; i++ ) + bin[i] = ScanDigits(&src,end,16,2,0); +} + +//----------------------------------------------------------------------------- + +void Sha1Bin2Hex ( sha1_hex_t hex, cvp bin ) +{ + DASSERT(hex); + DASSERT(bin); + + snprintf(hex,sizeof(sha1_hex_t),"%08x%08x%08x%08x%08x", + be32(bin), be32(bin+4), be32(bin+8), be32(bin+12), be32(bin+16) ); +} + +//----------------------------------------------------------------------------- + +ccp GetSha1Hex ( cvp bin ) +{ + DASSERT(bin); + + char *hex = GetCircBuf(sizeof(sha1_hex_t)); + Sha1Bin2Hex(hex,bin); + return hex; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// END /////////////// +/////////////////////////////////////////////////////////////////////////////// + |
