diff options
Diffstat (limited to '')
41 files changed, 11498 insertions, 1100 deletions
diff --git a/project/dclib/Makefile.inc b/project/dclib/Makefile.inc index d386594..45fc2a5 100644 --- a/project/dclib/Makefile.inc +++ b/project/dclib/Makefile.inc @@ -1,18 +1,46 @@ + ########################################################################### + # # + # _____ ____ # + # | __ \ / __ \ _ _ _____ # + # | | \ \ / / \_\ | | | | _ \ # + # | | \ \| | | | | | |_| | # + # | | | || | | | | | ___/ # + # | | / /| | __ | | | | _ \ # + # | |__/ / \ \__/ / | |___| | |_| | # + # |_____/ \____/ |_____|_|_____/ # + # # + # Wiimms source code library # + # # + ########################################################################### + #------------------------------------------------------------------------------ # standard object files -DCLIB_O = dclib-basics.o dclib-cli.o dclib-color.o dclib-numeric.o \ - dclib-file.o dclib-option.o dclib-regex.o \ - dclib-utf8.o dclib-shift-jis.o \ - dclib-vector.o dclib-tables.o dclib-ui.o \ - dclib-xdump.o dclib-debug.o -DCLIB_O1 := lib-dol.o lib-bmg.o lib-mkw.o -DCLIB_O2 := lib-wiimmfi.o lib-gamespy.o lib-natneg.o +DCLIB_O := dclib-basics.o dclib-cli.o dclib-color.o dclib-numeric.o \ + dclib-file.o dclib-option.o dclib-regex.o \ + dclib-utf8.o dclib-shift-jis.o \ + dclib-vector.o dclib-tables.o dclib-ui.o \ + dclib-xdump.o dclib-debug.o + +DCLIB_O_MKW := lib-dol.o lib-bmg.o lib-mkw.o + +DCLIB_O_WIIMMFI := lib-wiimmfi.o lib-gamespy.o lib-natneg.o + +# legacy +DCLIB_O1 := $(DCLIB_O_MKW) +DCLIB_O2 := $(DCLIB_O_WIIMMFI) DCLIB_PRIVATE := dclib-terminal* dclib-command* lib-wiimmfi* lib-gamespy* lib-natneg* #------------------------------------------------------------------------------ +# if DCLIB_THREADS not set: disable it + +ifeq ($(origin DCLIB_THREAD), undefined) + DCLIB_THREAD := 0 +endif + +#------------------------------------------------------------------------------ # if DCLIB_MYSQL not set: enable it if file 'mysql.h' is found ifeq ($(origin DCLIB_MYSQL), undefined) @@ -43,8 +71,13 @@ endif #------------------------------------------------------------------------------ # add libraries and guardian macros +ifeq ($(DCLIB_THREAD),1) + DCLIB_O += dclib-thread.o + DCLIB_FLAGS += -DDCLIB_THREAD=1 +endif + ifeq ($(DCLIB_NETWORK),1) - DCLIB_O += dclib-network.o + DCLIB_O += dclib-network.o dclib-punycode.o DCLIB_FLAGS += -DDCLIB_NETWORK=1 else DCLIB_MYSQL := 0 diff --git a/project/dclib/dclib-basics.c b/project/dclib/dclib-basics.c index fdea8fc..964b5d2 100644 --- a/project/dclib/dclib-basics.c +++ b/project/dclib/dclib-basics.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -39,6 +39,7 @@ #include <time.h> #include <stdio.h> #include <stddef.h> +#include <signal.h> #include <errno.h> #include <sys/time.h> #include <fcntl.h> @@ -118,6 +119,14 @@ const char ThinLine300_3[901] = // 300 * '─' (U+2500) + NULL "──────────────────────────────────────────────────" "──────────────────────────────────────────────────"; +const char DoubleLine300_3[901] = // 300 * '═' (U+2550) + NULL + "══════════════════════════════════════════════════" + "══════════════════════════════════════════════════" + "══════════════════════════════════════════════════" + "══════════════════════════════════════════════════" + "══════════════════════════════════════════════════" + "══════════════════════════════════════════════════"; + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -301,9 +310,9 @@ int GetProgramPath #ifdef __APPLE__ //--- read apple path of executable - + extern int _NSGetExecutablePath( char* buf, uint32_t * bufsize ); - + uint32_t size = sizeof(path); if (!_NSGetExecutablePath(path,&size)) { @@ -312,7 +321,7 @@ int GetProgramPath return StringCopyS(buf,buf_size,path) - buf; } -#else // !__APPLE__ +#else // !__APPLE__ //--- read files of /proc/... @@ -332,7 +341,7 @@ int GetProgramPath return StringCopyS(buf,buf_size,path) - buf; } -#endif // !__APPLE__ +#endif // !__APPLE__ //--- analyze argv0 @@ -433,8 +442,8 @@ 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); + DASSERT(buf); + DASSERT(buf<buf_end); buf_end--; if ( max_copy >= 0 && buf + max_copy < buf_end ) buf_end = buf + max_copy; @@ -536,6 +545,54 @@ char * StringCat3A ( ccp src1, ccp src2, ccp src3 ) /////////////////////////////////////////////////////////////////////////////// +ccp StringCenterE +( + char * buf, // result buffer; if NULL, circ-buf is used + ccp buf_end, // end of 'buf', NULL if buf==NULL + ccp src, // source string + int width // wanted width +) +{ + if (!src) + src = ""; + int n2 = strlen(src); + if ( n2 >= width ) + { + if (!buf) + return CopyCircBuf(src,n2+1); + + StringCopyEM(buf,buf_end,src,n2); + return buf; + } + + n2 = width - n2; + int n1 = n2/2; + n2 -= n1; + + if (!buf) + { + buf = GetCircBuf(width+1); + buf_end = buf + width; + } + else + buf_end--; + + char *dest = buf; + while ( n1-- > 0 && dest < buf_end ) + *dest++ = ' '; + + while ( *src && dest < buf_end ) + *dest++ = *src++; + + while ( n2-- > 0 && dest < buf_end ) + *dest++ = ' '; + + *dest = 0; + return buf; +}; + +/////////////////////////////////////////////////////////////////////////////// + char * StringLowerE ( char * buf, ccp buf_end, ccp src ) { // RESULT: end of copied string pointing to NULL @@ -658,6 +715,166 @@ char * MemUpperS ( char * buf, size_t buf_size, mem_t src ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// Signals & Sleep /////////////// +/////////////////////////////////////////////////////////////////////////////// + +volatile int SIGINT_level = 0; +volatile int SIGHUP_level = 0; +volatile int SIGALRM_level = 0; +volatile int SIGCHLD_level = 0; +volatile int SIGPIPE_level = 0; +volatile int SIGUSR1_level = 0; +volatile int SIGUSR2_level = 0; + +// no timout for SIGPIPE +u_sec_t SIGINT_sec = 0; +u_sec_t SIGHUP_sec = 0; +u_sec_t SIGALRM_sec = 0; +u_sec_t SIGCHLD_sec = 0; +u_sec_t SIGUSR1_sec = 0; +u_sec_t SIGUSR2_sec = 0; + +int SIGINT_level_max = 3; +bool ignore_SIGINT = false; +bool ignore_SIGHUP = false; + +int redirect_signal_pid = 0; +LogFile_t log_signal_file = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +static void inc_sig_handler ( volatile int *level, volatile uint *sec ) +{ + DASSERT(level); + DASSERT(sec); + + const uint now = GetTimeSec(false); + + if ( now - *sec > 30 ) + *level = 0; + *sec = now; + (*level)++; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void sig_handler ( int signum ) +{ + fflush(stdout); + static bool had_sigpipe = false; + + if (redirect_signal_pid) + { + kill(redirect_signal_pid,signum); + return; + } + + switch(signum) + { + case SIGINT: + if (ignore_SIGINT) + break; + // fall through + + case SIGTERM: + inc_sig_handler(&SIGINT_level,&SIGINT_sec); + ccp signame = signum == SIGINT ? "INT" : "TERM"; + if ( SIGINT_level > 1 ) + PrintLogFile(&log_signal_file,"#SIGNAL %s: level set to %d/%d\n", + signame, SIGINT_level, SIGINT_level_max ); + if ( SIGINT_level >= SIGINT_level_max ) + { + PrintLogFile(&log_signal_file,"#SIGNAL %s: TERMINATE IMMEDIATELY!\n",signame); + exit(0); + } + break; + + case SIGHUP: + if (!ignore_SIGHUP) + { + inc_sig_handler(&SIGHUP_level,&SIGHUP_sec); + PrintLogFile(&log_signal_file,"#SIGNAL HUP: level set to %d\n",SIGHUP_level); + } + break; + + case SIGCHLD: + SIGCHLD_level++; + PrintLogFile(&log_signal_file,"#SIGNAL CHLD: level set to %d\n",SIGCHLD_level); + break; + + case SIGALRM: + inc_sig_handler(&SIGALRM_level,&SIGALRM_sec); + PrintLogFile(&log_signal_file,"#SIGNAL ALRM: level set to %d\n",SIGALRM_level); + break; + + case SIGUSR1: + inc_sig_handler(&SIGUSR1_level,&SIGUSR1_sec); + PrintLogFile(&log_signal_file,"#SIGNAL USR1: level set to %d\n",SIGUSR1_level); + break; + + case SIGUSR2: + inc_sig_handler(&SIGUSR2_level,&SIGUSR2_sec); + PrintLogFile(&log_signal_file,"#SIGNAL USR2: level set to %d\n",SIGUSR2_level); + break; + + case SIGPIPE: + SIGPIPE_level++; + if (!had_sigpipe) + { + PrintLogFile(&log_signal_file,"#SIGNAL SIGPIPE: %d\n",SIGPIPE_level); + had_sigpipe = true; + } + if ( SIGPIPE_level >= 5 ) + { + PrintLogFile(&log_signal_file,"#SIGNAL SIGPIPE: TERMINATE IMMEDIATELY!\n"); + exit(0); + } + break; + + default: + PrintLogFile(&log_signal_file,"#SIGNAL: %d\n",signum); + } + fflush(stderr); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupSignalHandler ( int max_sigint_level, FILE *log_file ) +{ + SIGINT_level_max = max_sigint_level; + if (log_file) + { + log_signal_file.log = log_file; + log_signal_file.flush = true; + } + + static const int sigtab[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM, SIGPIPE, -1 }; + int i; + for ( i = 0; sigtab[i] >= 0; i++ ) + { + struct sigaction sa; + memset(&sa,0,sizeof(sa)); + sa.sa_handler = &sig_handler; + sigaction(sigtab[i],&sa,0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int SleepNSec ( s_nsec_t nsec ) +{ + if ( nsec > 0 ) + { + struct timespec ts; + ts.tv_sec = nsec / NSEC_PER_SEC; + ts.tv_nsec = nsec % NSEC_PER_SEC; + return nanosleep(&ts,0); + } + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// PathCat*() /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -1165,6 +1382,67 @@ uint CountEqual ( cvp m1, cvp m2, uint size ) /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +char * SkipEscapes ( ccp str ) +{ + if (str) + { + while ( *str == '\e' ) + { + str++; + if ( *str == '[' ) + str++; + while ( *str > 0 && *str < 0x40 ) + str++; + if (*str) + str++; + } + } + return (char*)str; +} + +//----------------------------------------------------------------------------- + +char * SkipEscapesE ( ccp str, ccp end ) +{ + if (str) + { + if (!end) + return SkipEscapes(str); + + while ( str < end && *str == '\e' ) + { + str++; + if ( str < end && *str == '[' ) + str++; + while ( str < end && *str > 0 && *str < 0x40 ) + str++; + if ( str < end ) + str++; + } + } + return (char*)str; +} + +//----------------------------------------------------------------------------- + +char * SkipEscapesS ( ccp str, int size ) +{ + return size < 0 ? SkipEscapes(str) : SkipEscapesE(str,str+strlen(str)); +} + +//----------------------------------------------------------------------------- + +mem_t SkipEscapesM ( mem_t mem ) +{ + mem_t res; + res.ptr = SkipEscapesE(mem.ptr,mem.ptr+mem.len); + res.len = mem.ptr + mem.len - res.ptr; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + int ReplaceToBuf ( // returns -1 on error (buf to small) or the length of the resulting string. @@ -1576,8 +1854,8 @@ char * PrintID /////////////// circ buf /////////////// /////////////////////////////////////////////////////////////////////////////// -static char circ_buf[CIRC_BUF_SIZE]; -static char *circ_ptr = circ_buf; +static __thread char circ_buf[CIRC_BUF_SIZE]; +static __thread char *circ_ptr = 0; /////////////////////////////////////////////////////////////////////////////// @@ -1597,6 +1875,8 @@ char * GetCircBuf // ==> ERROR0(ERR_OUT_OF_MEMORY) ) { + if (!circ_ptr) + circ_ptr = circ_buf; DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); buf_size = buf_size + 3 & ~3; @@ -1725,6 +2005,8 @@ void ReleaseCircBuf uint release_size // number of bytes to give back from end ) { + if (!circ_ptr) + circ_ptr = circ_buf; DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); if ( end_buf == circ_ptr @@ -1738,6 +2020,94 @@ void ReleaseCircBuf // /////////////////////////////////////////////////////////////////////////////// +/////////////// PrintMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupPrintMode ( PrintMode_t * pm ) +{ + DASSERT(pm); + + //-- stdout + + if (!pm->fout) + { + pm->fout = stdout; + pm->cout = colout; + } + if (!pm->cout) + pm->cout = GetColorSet0(); + + + //-- stderr + + //if (!pm->ferr) pm->ferr = stderr; + if (!pm->cerr) + pm->cerr = GetColorSet0(); + + + //-- stdlog + + if (pm->flog) + { + if (!pm->clog) + pm->clog = GetColorSet0(); + } + else if (pm->ferr) + { + pm->flog = pm->ferr; + pm->clog = pm->cerr; + } + else + { + pm->flog = pm->fout; + pm->clog = pm->cout; + } + + + //-- misc + + if (!pm->prefix) pm->prefix = EmptyString; + if (!pm->eol) pm->eol = "\n"; + + pm->indent = NormalizeIndent(pm->indent); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// get line by list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetLineByListHelper ( const int *list, ccp beg, ccp mid, ccp end, ccp line ) +{ + DASSERT(list); + DASSERT(beg); + DASSERT(mid); + DASSERT(end); + DASSERT(line); + + char buf[CIRC_BUF_MAX_ALLOC+20]; + char *endbuf = buf + CIRC_BUF_MAX_ALLOC; + char *dest = buf; + + for (;;) + { + int wd = *list++; + if ( wd < 0 ) + break; + + dest = StringCopyE(dest,endbuf,beg); + beg = mid; + while ( wd-- > 0 ) + dest = StringCopyE(dest,endbuf,line); + } + + dest = StringCopyE(dest,endbuf,end); + *dest++ = 0; + return dest <= endbuf ? CopyCircBuf(buf,dest-buf) : EmptyString; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct FastBuf_t /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -2343,6 +2713,473 @@ int ScanSplitArg // /////////////////////////////////////////////////////////////////////////////// +/////////////// PointerList_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetPointerMgr ( PointerList_t *pl ) +{ + if (pl) + { + FREE(pl->list); + InitializePointerMgr(pl,pl->grow); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddToPointerMgr ( PointerList_t *pl, const void *info ) +{ + DASSERT(pl); + if (!info) + return; + + // no duplicates! + for ( int i = 0; i < pl->used; i++ ) + if ( pl->list[i] == info ) + return; + + if ( pl->used == pl->size ) + { + if ( pl->grow < 10 ) + pl->grow = 10; + pl->size += pl->size/2 + pl->grow; + pl->list = REALLOC ( pl->list, (pl->size+1) * sizeof(*pl->list) ); + } + DASSERT( pl->used < pl->size ); + void **dest = pl->list + pl->used++; + *dest++ = (void*)info; + *dest = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddListToPointerMgr ( PointerList_t *pl, const void **list, int n_elem ) +{ + if (list) + { + if ( n_elem < 0 ) + while (*list) + AddToPointerMgr(pl,*list++); + else + while( --n_elem >= 0 ) + AddToPointerMgr(pl,*list++); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ** DupPointerMgr ( PointerList_t *pl, bool reset ) +{ + DASSERT(pl); + void **dest = MEMDUP( pl->list, (pl->used+1) * sizeof(*dest) ); + if (reset) + ResetPointerMgr(pl); + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ** ReplacePointerMgr + ( PointerList_t *pl, bool reset, const PointerList_t **old_list ) +{ + FREE(old_list); + return DupPointerMgr(pl,reset); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ArgManager_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetArgManager ( ArgManager_t *am ) +{ + static char *null_list[] = {0}; + + if (am) + { + if (am->size) + { + uint i; + for ( i = 0; i < am->argc; i++ ) + FreeString(am->argv[i]); + if ( am->argv != null_list ) + FREE(am->argv); + } + + const typeof(am->force_case) force_case = am->force_case; + memset(am,0,sizeof(*am)); + am->force_case = force_case; + am->argv = null_list; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupArgManager + ( ArgManager_t *am, LowerUpper_t force_case, int argc, char ** argv, bool clone ) +{ + DASSERT(am); + memset(am,0,sizeof(*am)); + am->force_case = force_case; + if (clone) + CloneArgManager(am,argc,argv); + else + AttachArgManager(am,argc,argv); +} + +/////////////////////////////////////////////////////////////////////////////// + +void AttachArgManager ( ArgManager_t *am, int argc, char ** argv ) +{ + DASSERT(am); + ResetArgManager(am); + am->argc = argc; + am->argv = argv; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloneArgManager ( ArgManager_t *am, int argc, char ** argv ) +{ + DASSERT(am); + AttachArgManager(am,argc,argv); + PrepareEditArgManager(am,0); +} + +/////////////////////////////////////////////////////////////////////////////// + +void CopyArgManager ( ArgManager_t *dest, const ArgManager_t *src ) +{ + DASSERT(dest); + if (!src) + ResetArgManager(dest); + else if( dest != src ) + AttachArgManager(dest,src->argc,src->argv); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MoveArgManager ( ArgManager_t *dest, ArgManager_t *src ) +{ + DASSERT(dest); + ResetArgManager(dest); + if (src) + { + dest->argv = src->argv; + dest->argc = src->argc; + dest->size = src->size; + memset(src,0,sizeof(*src)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void PrepareEditArgManager ( ArgManager_t *am, int needed_space ) +{ + DASSERT(am); + + uint new_size = am->argc; + if ( needed_space > 0 ) + new_size += needed_space; + new_size = GetGoodAllocSize2(new_size+(new_size>>4)+10,sizeof(*am->argv)); + PRINT("PrepareEditArgManager(,%d) n=%d, size: %d -> %d\n", + needed_space, am->argc, am->size, new_size ); + + if (am->size) + am->argv = REALLOC(am->argv,sizeof(*am->argv)*new_size); + else + { + char **old = am->argv; + am->argv = MALLOC(sizeof(*am->argv)*new_size); + + uint i; + for ( i = 0; i < am->argc; i++ ) + am->argv[i] = old[i] ? STRDUP(old[i]) : 0; + } + am->size = new_size - 1; + DASSERT( am->argc <= am->size ); + am->argv[am->argc] = 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint AppendArgManager ( ArgManager_t *am, ccp arg1, ccp arg2, bool move_arg ) +{ + DASSERT(am); + PRINT("AppendArgManager(,%s,%s,%d)\n",arg1,arg2,move_arg); + return InsertArgManager(am,am->argc,arg1,arg2,move_arg); +} + +/////////////////////////////////////////////////////////////////////////////// + +static char ** insert_arg_manager + ( ArgManager_t *am, char **dest, ccp p_arg, bool move_arg ) +{ + DASSERT(am); + if (p_arg) + { + char *arg = (char*)p_arg; + if (!move_arg) + arg = STRDUP(arg); + + if ( am->force_case == LOUP_LOWER || am->force_case == LOUP_UPPER ) + { + ccp end = arg + strlen(arg) + 1; + if (am->force_case == LOUP_LOWER) + StringLowerE(arg,end,arg); + else + StringUpperE(arg,end,arg); + } + *dest++ = arg; + } + return dest; +} + +//----------------------------------------------------------------------------- + +uint InsertArgManager + ( ArgManager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ) +{ + PRINT("InsertArgManager(,%d,%s,%s,%d)\n",pos,arg1,arg2,move_arg); + + pos = CheckIndex1(am->argc,pos); + const int n = (arg1!=0) + (arg2!=0); + if (n) + { + PrepareEditArgManager(am,n); + DASSERT( pos>=0 && pos <= am->argc ); + char **dest = am->argv + pos; + if ( pos < am->argc ) + memmove( dest+n, dest, (u8*)(am->argv+am->argc) - (u8*)dest ); + + dest = insert_arg_manager(am,dest,arg1,move_arg); + dest = insert_arg_manager(am,dest,arg2,move_arg); + + am->argc += n; + am->argv[am->argc] = 0; + } + return pos+n; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ReplaceArgManager + ( ArgManager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ) +{ + PRINT("ReplaceArgManager(,%d,%s,%s,%d)\n",pos,arg1,arg2,move_arg); + + pos = CheckIndex1(am->argc,pos); + const int n = (arg1!=0) + (arg2!=0); + if (n) + { + PrepareEditArgManager(am,n); + DASSERT( pos>=0 && pos <= am->argc ); + char **dest = am->argv + pos; + if (arg1) + { + if ( pos++ < am->argc ) + FreeString(*dest); + *dest++ = move_arg ? (char*)arg1 : STRDUP(arg1); + } + if (arg2) + { + if ( pos++ < am->argc ) + FreeString(*dest); + *dest++ = move_arg ? (char*)arg2 : STRDUP(arg2); + } + if ( pos > am->argc ) + { + am->argc = pos; + am->argv[am->argc] = 0; + } + } + return pos; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint RemoveArgManager ( ArgManager_t *am, int pos, int count ) +{ + DASSERT(am); + + #if HAVE_PRINT + const int oldpos = pos; + #endif + const int n = CheckIndexC(am->argc,&pos,count); + PRINT("RemoveArgManager(,%d,%d) : remove n=%d: %d..%d\n", + oldpos, count, n, pos, pos+n ); + + DASSERT( pos >= 0 && pos <= am->argc ); + DASSERT( n >= 0 && pos+n <= am->argc ); + + if ( n > 0 ) + { + PrepareEditArgManager(am,0); + char **dest = am->argv + pos; + + uint i; + for ( i = 0; i <n; i++ ) + FreeString(dest[i]); + am->argc -= n; + memmove( dest, dest+n, (u8*)(am->argv+am->argc) - (u8*)dest ); + am->argv[am->argc] = 0; + } + return pos; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanSimpleArgManager ( ArgManager_t *am, ccp src ) +{ + uint count = 0; + if (src) + { + for(;;) + { + while ( *src == ' ' || *src == '\t' ) + src++; + if (!*src) + break; + + ccp start = src; + while ( *src && *src != ' ' && *src != '\t' ) + src++; + + ccp arg = MEMDUP(start,src-start); + AppendArgManager(am,arg,0,true); + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint ScanQuotedArgManager ( ArgManager_t *am, ccp src, bool is_utf8 ) +{ + uint count = 0; + if (src) + { + char buf[10000], *bufend = buf + sizeof(buf) - 2; + + for(;;) + { + while ( *src == ' ' || *src == '\t' ) + src++; + if (!*src) + break; + + char *dest = buf; + while ( dest < bufend ) + { + if ( *src == '\'' || *src == '\"' ) + { + uint scanned; + dest += ScanEscapedString(dest,bufend-dest,src,-1,is_utf8,-1,&scanned); + src += scanned; + } + else + { + while ( dest < bufend && *src && *src != ' ' && *src != '\t' + && *src != '\'' && *src != '\"' ) + { + if ( *src == '\\' && src[1] ) + src++; + *dest++ = *src++; + } + } + if ( !*src || *src == ' ' || *src == '\t' ) + break; + } + ccp arg = MEMDUP(buf,dest-buf); + AppendArgManager(am,arg,0,true); + } + } + return count; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CheckFilterArgManager ( const ArgManager_t *filter, ccp name ) +{ + if ( !name || !*name ) + return false; + + if (!filter->argc) + return true; + + char buf[200]; + if ( filter->force_case == LOUP_LOWER ) + { + StringLowerS(buf,sizeof(buf),name); + name = buf; + } + else if ( filter->force_case == LOUP_UPPER ) + { + StringUpperS(buf,sizeof(buf),name); + name = buf; + } + + bool fallback_status = true; + for ( int i = 0; i < filter->argc; i++ ) + { + ccp arg = filter->argv[i]; + const bool status = *arg != '/'; + if (status) + fallback_status = false; + else + arg++; + + const uint len = strlen(arg); + if ( *arg == '=' ) + { + if ( !strcmp(arg+1,name) ) + return status; + } + else if ( *arg == '^' ) + { + if ( len > 1 && !strncmp(arg+1,name,len-1) ) + return status; + } + else + if ( len && strstr(name,arg) ) + return status; + } + + return fallback_status; +} + +/////////////////////////////////////////////////////////////////////////////// + +void DumpArgManager ( FILE *f, int indent, const ArgManager_t *am, ccp title ) +{ + if ( !f || !am ) + return; + + indent = NormalizeIndent(indent); + if (title) + fprintf(f,"%*s" "ArgManager[%s]: N=%d", + indent,"", title, am->argc ); + else + fprintf(f,"%*s" "ArgManager: N=%d", indent,"", am->argc ); + + if (am->size) + fprintf(f,"/%u\n",am->size); + else + fputc('\n',f); + + if (am->argc) + { + char buf[10]; + int fw = snprintf(buf,sizeof(buf),"%u",am->argc-1)+2; + + uint i; + for ( i = 0; i < am->argc; i++ ) + fprintf(f,"%*s%*u: |%s|\n", indent,"", fw,i, am->argv[i] ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct mem_t /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -2487,16 +3324,80 @@ mem_t MemCat3A ( const mem_t m1, const mem_t m2, const mem_t m3 ) return res; } +/////////////////////////////////////////////////////////////////////////////// +// UTF8 support + +mem_t MidMem8 ( const mem_t src, int begin, int count ) +{ + if ( !src.ptr || !src.len ) + return src; + + ccp src_end = src.ptr + src.len; + const int u8len = ScanUTF8LengthE(src.ptr,src_end); + begin = CheckIndex1(u8len,begin); + if ( count < 0 ) + { + count = -count; + begin -= count; + if ( begin < 0 ) + { + count += begin; + begin = 0; + } + } + + mem_t res; + res.ptr = SkipUTF8CharE(src.ptr,src_end,begin); + const int max = u8len - begin; + res.len = SkipUTF8CharE( res.ptr, src_end, count < max ? count : max ) - res.ptr; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t ExtractMem8 ( const mem_t src, int begin, int end ) +{ + if ( !src.ptr || !src.len ) + return src; + + ccp src_end = src.ptr + src.len; + const int u8len = ScanUTF8LengthE(src.ptr,src_end); + const int count = CheckIndex2(u8len,&begin,&end); + + mem_t res; + res.ptr = SkipUTF8CharE( src.ptr, src_end, begin ); + res.len = SkipUTF8CharE( res.ptr, src_end, count ) - res.ptr; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +mem_t ExtractEndMem8 ( const mem_t src, int begin, int end ) +{ + if ( !src.ptr || !src.len ) + return src; + + ccp src_end = src.ptr + src.len; + const int u8len = ScanUTF8LengthE(src.ptr,src_end); + const int count = CheckIndex2End(u8len,&begin,&end); + + mem_t res; + res.ptr = SkipUTF8CharE( src.ptr, src_end, begin ); + res.len = SkipUTF8CharE( res.ptr, src_end, count ) - res.ptr; + return res; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// struct exmem_t /////////////// +/////////////////////////////////////////////////////////////////////////////// exmem_t EmptyExMem = {{EmptyString,0},0,false,false,false,false}; exmem_t NullExMem = {{0,0},0,false,false,false,false}; /////////////////////////////////////////////////////////////////////////////// -void FreeExMem ( const exmem_t *em ) +void ResetExMem ( const exmem_t *em ) { if (em) { @@ -2508,6 +3409,23 @@ void FreeExMem ( const exmem_t *em ) /////////////////////////////////////////////////////////////////////////////// +void FreeExMem ( const exmem_t *em ) +{ + if (em) + { + if (em->is_alloced) + { + exmem_t *em2 = (exmem_t*)em; + FreeString(em2->data.ptr); + em2->data.ptr = 0; + em2->data.len = 0; + em2->is_alloced = false; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + void FreeExMemCM ( const exmem_t *em, CopyMode_t copy_mode ) { if ( em && copy_mode == CPM_MOVE ) @@ -2604,7 +3522,41 @@ void AssignExMem ( exmem_t *dest, const exmem_t *source, CopyMode_t copy_mode ) /////////////////////////////////////////////////////////////////////////////// -exmem_t CopyExMemS ( ccp string, int slen, CopyMode_t copy_mode ) +void AssignExMemS ( exmem_t *dest, cvp source, int slen, CopyMode_t copy_mode ) +{ + if (dest) + { + ccp free_str = dest->is_alloced ? dest->data.ptr : 0; + const bool dest_key_alloced = dest->is_key_alloced; + memset(dest,0,sizeof(*dest)); + dest->is_key_alloced = dest_key_alloced; + + if (source) + { + dest->data.len = slen < 0 ? strlen(source) : slen; + switch (copy_mode) + { + case CPM_COPY: + dest->data.ptr = MEMDUP(source,dest->data.len); + dest->is_alloced = true; + break; + + case CPM_MOVE: + dest->is_alloced = true; + // fall through + case CPM_LINK: + dest->data.ptr = source; + break; + } + } + FreeString(free_str); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 // [[obsolete]] since 2021-11 +exmem_t CopyExMemS ( cvp string, int slen, CopyMode_t copy_mode ) { exmem_t em = {{0}}; @@ -2630,6 +3582,7 @@ exmem_t CopyExMemS ( ccp string, int slen, CopyMode_t copy_mode ) return em; } +#endif /////////////////////////////////////////////////////////////////////////////// @@ -5038,10 +5991,10 @@ int ScanKeywordOffOn const KeywordTab_t KeyTab_OFF_AUTO_ON[] = { { OFFON_OFF, "OFF", "-1", 0 }, - { OFFON_OFF, "DISABLED", 0, 0 }, + { OFFON_OFF, "DISABLED", "NO", 0 }, { OFFON_AUTO, "AUTO", "0", 0 }, { OFFON_ON, "ON", "1", 0 }, - { OFFON_ON, "ENABLED", 0, 0 }, + { OFFON_ON, "ENABLED", "YES", 0 }, { OFFON_FORCE, "FORCE", "2", 0 }, { 0,0,0,0 } }; @@ -6589,16 +7542,42 @@ exmem_key_t * AppendEML ( exmem_list_t * eml, ccp key, CopyMode_t cm_key, /////////////////////////////////////////////////////////////////////////////// -bool RemoveEML ( exmem_list_t * eml, ccp key ) +bool RemoveByIndexEML ( exmem_list_t * eml, int index ) +{ + if ( index < 0 ) + index += eml->used; + if ( index >= 0 && index < eml->used ) + { + DASSERT( eml->used > 0 ); + eml->used--; + exmem_key_t * dest = eml->list + index; + if (dest->data.is_key_alloced) + FreeString(dest->key); + FreeExMem(&dest->data); + if (eml->used) + memmove(dest,dest+1,(eml->used-index)*sizeof(*dest)); + else + eml->is_unsorted = false; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool RemoveByKeyEML ( exmem_list_t * eml, ccp key ) { bool found; - uint idx = FindHelperEML(eml,key,&found); + const uint index = FindHelperEML(eml,key,&found); + #if 1 + return found && RemoveByIndexEML(eml,index); + #else // [[obsolete]] 2021-11 if (found) { DASSERT(eml->used); eml->used--; - DASSERT( idx <= eml->used ); - exmem_key_t * dest = eml->list + idx; + DASSERT( index <= eml->used ); + exmem_key_t * dest = eml->list + index; if (dest->data.is_key_alloced) FreeString(dest->key); FreeExMem(&dest->data); @@ -6608,6 +7587,7 @@ bool RemoveEML ( exmem_list_t * eml, ccp key ) eml->is_unsorted = false; } return found; + #endif } /////////////////////////////////////////////////////////////////////////////// @@ -6849,6 +7829,560 @@ void AddStandardSymbolsEML ( exmem_list_t * eml, bool overwrite ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// MultiColumn /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMultiColumn ( MultiColumn_t *mc ) +{ + if (mc) + { + ResetEML(&mc->list); + memset(mc,0,sizeof(*mc)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +exmem_key_t * PutLineMC + ( MultiColumn_t *mc, uint opt, ccp key, ccp value, CopyMode_t cm_value ) +{ + DASSERT(mc); + DASSERT(key); + DASSERT(value); + + exmem_t data = ExMemByString(value); + exmem_key_t *res = AppendEML( &mc->list, key, CPM_LINK, &data, cm_value ); + res->data.attrib = opt; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +exmem_key_t * PrintLineMC + ( MultiColumn_t *mc, uint opt, ccp key, ccp format, ... ) +{ + char buf[1000]; + va_list arg; + va_start(arg,format); + vsnprintf(buf,sizeof(buf),format,arg); + va_end(arg); + return PutLineMC(mc,opt,key,buf,CPM_COPY); +} + +/////////////////////////////////////////////////////////////////////////////// +// [[mc_len_t]] + +typedef struct mc_len_t +{ + int key; // key length + int val; // val length +} +mc_len_t; + +//----------------------------------------------------------------------------- +// [[mc_param_t]] + +#define MULTI_COLUMN_MAX_COLS 100 + +typedef struct mc_param_t +{ + FILE *f; // output file + const ColorSet_t + *colset; // color set for the frame + int debug; // debug level: 0..2 + + int frame_mode; // 0:ASCII, 1:single line, 2:double line + ccp prefix; // line prefix + int prefix_fw; // field width of 'prefix' + int indent; // indention excluding prefix + ccp sep; // separator for frame_mode 0 and 1 + int sep_fw; // field width of 'sep' + ccp eol; // end-of-line string + int fw; // total field width + + ccp head_key; // header string for key column + int head_key_fw; // field width of 'head_key' + ccp head_val; // header string for value column + int head_val_fw; // field width of 'head_val' + + int row_size; // additional size for a row + int col_size; // additional size for a column + + int n_rows; // number of rows + int n_cols; // number of columns + + mc_len_t *len_list; // list of estimatest lengths + mc_len_t fw_col[MULTI_COLUMN_MAX_COLS]; + // field width for each column +} +mc_param_t; + +//----------------------------------------------------------------------------- + +static void mc_print_sep ( const mc_param_t *p ) +{ + fprintf(p->f,"%s%*s",p->prefix,p->indent,""); + + const mc_len_t *fw = p->fw_col; + for ( int col = 0; col < p->n_cols; col++, fw++ ) + { + if (col) + fputs("-+-",p->f); + fprintf(p->f,"%.*s",fw->key+fw->val+p->sep_fw,Minus300); + } + fputs(p->eol,p->f); +} + +//----------------------------------------------------------------------------- + +static void mc_print_line1 ( const mc_param_t *p, ccp template ) +{ + fprintf(p->f,"%s%*s%s%.3s", + p->prefix, p->indent,"", + p->colset->heading, template ); + + const mc_len_t *fw = p->fw_col; + for ( int col = 0; col < p->n_cols; col++, fw++ ) + { + if (col) + fprintf(p->f,"%.3s",template+3); + fprintf(p->f,"%.*s", 3*(fw->key+fw->val+p->sep_fw+2), ThinLine300_3 ); + } + + fprintf(p->f,"%.3s%s%s",template+6,p->colset->reset,p->eol); +} + +//----------------------------------------------------------------------------- + +static void mc_print_line2 ( const mc_param_t *p, ccp template ) +{ + fprintf(p->f,"%s%*s%s%.3s", + p->prefix, p->indent,"", + p->colset->heading, template ); + + const mc_len_t *fw = p->fw_col; + for ( int col = 0; col < p->n_cols; col++, fw++ ) + { + if (col) + fprintf(p->f,"%.3s",template+6); + fprintf(p->f,"%.*s%.3s%.*s", + 3*(fw->key+2), DoubleLine300_3, + template+3, + 3*(fw->val+2), DoubleLine300_3 ); + } + + fprintf(p->f,"%.3s%s%s",template+9,p->colset->reset,p->eol); +} + +//----------------------------------------------------------------------------- + +int PrintMultiColumn ( const MultiColumn_t *mc ) +{ + const u_usec_t start_time = GetTimerUSec(); + if ( !mc || !mc->list.used ) + return -1; + + + //--- setup + + mc_param_t p = {0}; + p.f = mc->f ? mc->f : stdout; + p.colset = mc->colset ? mc->colset : GetColorSet0(); + p.debug = mc->debug; + p.indent = NormalizeIndent(mc->indent); + + p.prefix = mc->prefix; + if (p.prefix) + p.prefix_fw = ScanEUTF8Length(p.prefix); + else + { + p.prefix = EmptyString; + p.prefix_fw = 0; + } + + p.sep = mc->sep ? mc->sep : " "; + p.sep_fw = ScanEUTF8Length(p.sep); + p.eol = mc->eol ? mc->eol : "\n"; + p.fw = !mc->fw ? 79 : mc->fw > 10 ? mc->fw : 10; + + if ( mc->print_header ) + { + p.head_key = mc->head_key ? mc->head_key : "Name"; + p.head_key_fw = strlene8(p.head_key); + + p.head_val = mc->head_val ? mc->head_val : "Value"; + p.head_val_fw = strlene8(p.head_val); + } + else + { + p.head_key = p.head_val = EmptyString; + // p.head_key_fw = p.head_val_fw = 0; // initial value + } + + switch (mc->frame_mode) + { + case 0: + p.frame_mode = 0; + p.row_size = -3; + p.col_size = 3 + p.sep_fw; + break; + + case 1: + p.frame_mode = 1; + p.row_size = 1; + p.col_size = 3 + p.sep_fw; + break; + + default: + p.frame_mode = 2; + p.row_size = 1; + p.col_size = 6; + break; + } + + if ( p.debug > 1 ) + fprintf(p.f, + "#> debug=%d, pre=%s, indent=%d, eol=%s, fw=%d," + " print[Fhf]=%d%d%d, min_rows=%d, max_cols=%d%s", + p.debug, QuoteStringS(p.prefix,0,CHMD__MODERN), + p.indent, QuoteStringS(p.eol,0,CHMD__MODERN), p.fw, + p.frame_mode, mc->print_header, mc->print_footer, + mc->min_rows, mc->max_cols, p.eol ); + + + //--- find min width + + p.len_list = MALLOC( mc->list.used * sizeof(*p.len_list) ); + mc_len_t *len_ptr = p.len_list; + + int min_fw = INT_MAX; + const exmem_key_t *list_end = mc->list.list + mc->list.used; + for ( const exmem_key_t *ptr = mc->list.list; ptr < list_end; ptr++ ) + { + len_ptr->key = strlene8(ptr->key); + len_ptr->val = strlene8(ptr->data.data.ptr); + const int len = len_ptr->key + len_ptr->val; + if ( min_fw > len ) + min_fw = len; + len_ptr++; + } + min_fw += p.col_size; + + const int avail_chars = p.fw - p.prefix_fw - p.indent - p.row_size; + int max_cols = ( avail_chars + p.col_size - 1 ) / min_fw; + if ( max_cols > MULTI_COLUMN_MAX_COLS ) + max_cols = MULTI_COLUMN_MAX_COLS; + + const int min_rows = mc->min_rows > 1 ? mc->min_rows : 1; + const int temp = ( mc->list.used + min_rows - 1 ) / min_rows + 1; + if ( temp > 0 && max_cols > temp ) + max_cols = temp; + + if ( p.debug > 1 ) + fprintf(p.f, + "#> src-lines=%d, min-fw=%d, min-rows=%d, max-cols=%d/%d%s", + mc->list.used, min_fw, + min_rows, max_cols, MULTI_COLUMN_MAX_COLS, p.eol ); + + + //--- evaluate number of columns + + for ( p.n_cols = max_cols; p.n_cols > 0; p.n_cols-- ) + { + memset(p.fw_col,0,sizeof(p.fw_col)); + mc_len_t len = { p.head_key_fw, p.head_val_fw }; + + p.n_rows = ( mc->list.used - 1 ) / p.n_cols + 1; + p.n_cols = ( mc->list.used - 1 ) / p.n_rows + 1; + if ( p.debug > 1 ) + fprintf(p.f, "#> … try %2u columns and %3u rows, %3u cells total%s", + p.n_cols, p.n_rows, p.n_cols*p.n_rows, p.eol ); + + int idx, row, col, total_fw = 0; + len_ptr = p.len_list; + for ( idx = row = col = 0; idx < mc->list.used; idx++, len_ptr++ ) + { + if ( len.key < len_ptr->key ) + len.key = len_ptr->key; + if ( len.val < len_ptr->val ) + len.val = len_ptr->val; + + if ( ++row >= p.n_rows ) + { + total_fw += len.key + len.val + p.col_size; + if ( total_fw > avail_chars ) + break; + p.fw_col[col] = len; + + len.key = p.head_key_fw; + len.val = p.head_val_fw; + row = 0; + col++; + } + } + if ( col < p.n_cols ) + { + p.fw_col[col] = len; + total_fw += len.key + len.val + p.col_size; + } + if ( total_fw <= avail_chars ) + break; + } + + if ( p.n_cols < 1 ) + { + p.n_rows = mc->list.used; + p.n_cols = 1; + } + + if ( p.debug > 1 ) + { + fprintf(p.f,"#> cols=%d, rows=%d, fw", p.n_cols, p.n_rows ); + int total = 0; + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, fw++ ) + { + fprintf( p.f, "%c%u+%u", col ? ',' : '=', fw->key, fw->val ); + total += fw->key + fw->val + p.col_size; + } + fprintf(p.f," = %d/%d%s",total,avail_chars,p.eol); + } + + //--- print columns + + const u_usec_t output_time = GetTimerUSec(); + + uint out_lines = p.n_rows; + //---------------------- + if ( p.frame_mode >= 2 ) + //---------------------- + { + mc_print_line2(&p,"╔╤╦╗"); + out_lines++; + + if (mc->print_header) + { + fprintf(p.f,"%s%*s%s║%s",p.prefix,p.indent,"",p.colset->heading,p.colset->reset); + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, fw++ ) + fprintf(p.f," %s %s│%s %s %s║%s", + AlignEUTF8ToCircBuf(p.head_key,-fw->key,fw->key), + p.colset->heading, p.colset->reset, + AlignEUTF8ToCircBuf(p.head_val,-fw->val,fw->val), + p.colset->heading, p.colset->reset ); + fputs(p.eol,p.f); + + mc_print_line2(&p,"╠╪╬╣"); + out_lines += 2; + } + + for ( int row = 0; row < p.n_rows; row++ ) + { + fprintf(p.f,"%s%*s%s║%s",p.prefix,p.indent,"",p.colset->heading,p.colset->reset); + const exmem_key_t *ptr = mc->list.list + row; + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, ptr += p.n_rows, fw++ ) + { + if ( ptr < list_end ) + { + fprintf(p.f," %s %s│%s ", + AlignEUTF8ToCircBuf(ptr->key,-fw->key,fw->key), + p.colset->heading, p.colset->reset ); + + if ( ptr->data.attrib > 1 ) + { + const int fw_attrib = ptr->data.attrib < fw->val ? ptr->data.attrib : fw->val; + const int fw_val = p.len_list[ptr-mc->list.list].val; + if ( fw_attrib <= fw_val ) + goto std2; + fprintf(p.f,"%s%*s %s║%s", + AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_attrib,fw_attrib), + fw_attrib - fw->val, "", + p.colset->heading, p.colset->reset ); + } + else + { + std2:; + const int fw_val = ptr->data.attrib ? -fw->val : fw->val; + fprintf(p.f,"%s %s║%s", + AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_val,fw->val), + p.colset->heading, p.colset->reset ); + } + } + else + fprintf(p.f," %*s %s│%s %*s %s║%s", + fw->key, "", p.colset->heading, p.colset->reset, + fw->val, "", p.colset->heading, p.colset->reset ); + } + fputs(p.eol,p.f); + } + + mc_print_line2(&p,"╚╧╩╝"); + out_lines++; + } + //--------------------------- + else if ( p.frame_mode >= 1 ) + //--------------------------- + { + mc_print_line1(&p,"┌┬┐"); + out_lines++; + + if (mc->print_header) + { + fprintf(p.f,"%s%*s%s│%s",p.prefix,p.indent,"",p.colset->heading,p.colset->reset); + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, fw++ ) + fprintf(p.f," %s%s%s %s│%s", + AlignEUTF8ToCircBuf(p.head_key,-fw->key,fw->key), + p.sep, + AlignEUTF8ToCircBuf(p.head_val,-fw->val,fw->val), + p.colset->heading, p.colset->reset ); + + fputs(p.eol,p.f); + + mc_print_line1(&p,"├┼┤"); + out_lines += 2; + } + + for ( int row = 0; row < p.n_rows; row++ ) + { + fprintf(p.f,"%s%*s%s│%s",p.prefix,p.indent,"",p.colset->heading,p.colset->reset); + const exmem_key_t *ptr = mc->list.list + row; + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, ptr += p.n_rows, fw++ ) + { + if ( ptr < list_end ) + { + fprintf(p.f," %s%s", + AlignEUTF8ToCircBuf(ptr->key,-fw->key,fw->key), + p.sep ); + + if ( ptr->data.attrib > 1 ) + { + const int fw_attrib = ptr->data.attrib < fw->val ? ptr->data.attrib : fw->val; + const int fw_val = p.len_list[ptr-mc->list.list].val; + if ( fw_attrib <= fw_val ) + goto std1; + fprintf(p.f,"%s%*s %s│%s", + AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_attrib,fw_attrib), + fw_attrib - fw->val, "", + p.colset->heading, p.colset->reset ); + } + else + { + std1:; + const int fw_val = ptr->data.attrib ? -fw->val : fw->val; + fprintf(p.f,"%s %s│%s", + AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_val,fw->val), + p.colset->heading, p.colset->reset ); + } + } + else + fprintf(p.f," %*s %s│%s", + fw->key + fw->val + p.sep_fw, "", + p.colset->heading, p.colset->reset ); + } + fputs(p.eol,p.f); + } + + mc_print_line1(&p,"└┴┘"); + out_lines++; + } + //------------------- + else // !p.frame_mode + //------------------- + { + if (mc->print_header) + { + mc_print_sep(&p); + + fprintf(p.f,"%s%*s",p.prefix,p.indent,""); + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, fw++ ) + { + if (col) + fputs(" | ",p.f); + fprintf(p.f,"%s%s%s", + AlignEUTF8ToCircBuf(p.head_key,-fw->key,fw->key), + p.sep, + AlignEUTF8ToCircBuf(p.head_val,-fw->val,fw->val) ); + } + fputs(p.eol,p.f); + out_lines += 2; + } + + if ( mc->print_header || mc->print_footer ) + { + mc_print_sep(&p); + out_lines++; + } + + for ( int row = 0; row < p.n_rows; row++ ) + { + fprintf(p.f,"%s%*s",p.prefix,p.indent,""); + const exmem_key_t *ptr = mc->list.list + row; + const mc_len_t *fw = p.fw_col; + for ( int col = 0; col < p.n_cols; col++, ptr += p.n_rows, fw++ ) + { + if (col) + fputs(" | ",p.f); + if ( ptr < list_end ) + { + fprintf(p.f,"%s%s", + AlignEUTF8ToCircBuf(ptr->key,-fw->key,fw->key), + p.sep ); + + + if ( ptr->data.attrib > 1 ) + { + const int fw_attrib = ptr->data.attrib < fw->val ? ptr->data.attrib : fw->val; + const int fw_val = p.len_list[ptr-mc->list.list].val; + if ( fw_attrib <= fw_val ) + goto std0; + fprintf(p.f,"%s%*s", + AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_attrib,fw_attrib), + fw_attrib - fw->val, "" ); + } + else + { + std0:; + const int fw_val = ptr->data.attrib ? -fw->val : fw->val; + fputs(AlignEUTF8ToCircBuf(ptr->data.data.ptr,fw_val,fw->val),p.f); + } + } + } + fputs(p.eol,p.f); + } + + if (mc->print_footer) + { + mc_print_sep(&p); + out_lines++; + } + } + //--------------------- + // end-if !p.frame_mode + //--------------------- + + + //--- terminate + + if ( p.debug > 0 ) + { + const u_usec_t now = GetTimerUSec(); + fprintf(p.f,"#> setup in %llu us, %u ouput lines in %llu us, total %llu us%s", + output_time - start_time, + out_lines, now - output_time, + now - start_time, + p.eol ); + } + + FREE(p.len_list); + return out_lines; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// Memory Maps /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -8638,6 +10172,84 @@ int opt_new = 0; /////////////////////////////////////////////////////////////////////////////// +ccp GetIpModeName ( IpMode_t ipm ) +{ + switch (ipm) + { + case IPV4_ONLY: return "IPv4 only"; + case IPV6_ONLY: return "IPv6 only"; + case IPV4_FIRST: return "IPv4 first"; + case IPV6_FIRST: return "IPv6 first"; + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ipv4_t GetIP4Mask ( int bits ) +{ + return bits > 0 && bits < 32 + ? htonl( 0xffffffffu << (32-bits) ) + : bits ? 0xffffffffu : 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ipv6_t GetIP6Mask ( int bits ) +{ + ipv6_t ip6; + memset(&ip6,~0,sizeof(ip6)); + if ( bits >= 0 && bits < 128 ) + { + int idx = bits/32; + bits -= idx*32; + const u32 mask = htonl( bits ? 0xffffffffu << (32-bits) : 0 ); + ip6.v32[idx++] &= mask; + while ( idx < 4 ) + ip6.v32[idx++] = 0; + } + return ip6; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetBitsByMask4 ( ipv4_t ip4_nbo ) +{ + const int res = FindHighest0BitBE(&ip4_nbo,4); + return 31-res; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetBitsByMask6 ( ipv6_t ip6_nbo ) +{ + const int res = FindHighest0BitBE(&ip6_nbo,16); + return 127-res; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetBitsBySA ( sockaddr_t *sa, int return_on_error ) +{ + if (sa) + { + if ( sa->sa_family == AF_INET ) + { + sockaddr_in4_t *sa4 = (sockaddr_in4_t*)sa; + return GetBitsByMask4(sa4->sin_addr.s_addr); + } + + if ( sa->sa_family == AF_INET6 ) + { + sockaddr_in6_t *sa6 = (sockaddr_in6_t*)sa; + return GetBitsByMask6(*(ipv6_t*)&sa6->sin6_addr); + } + } + return return_on_error; +} + +/////////////////////////////////////////////////////////////////////////////// + uint CreateUniqueIdN ( int range ) { static uint unique_id = 1; diff --git a/project/dclib/dclib-basics.h b/project/dclib/dclib-basics.h index 95e47bb..47827c7 100644 --- a/project/dclib/dclib-basics.h +++ b/project/dclib/dclib-basics.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -39,6 +39,7 @@ #include <sys/types.h> #include <arpa/inet.h> +#include <sys/un.h> #include <limits.h> #include <stdlib.h> #include <stdio.h> @@ -65,8 +66,13 @@ #define SIZEOFRANGE(type,a,b) \ ( offsetof(type,b) + sizeof(((type*)0)->b) - offsetof(type,a) ) -#define PTR_DISTANCE(a,b) ( (u8*)(a) - (u8*)(b) ) -#define PTR_DISTANCE_INT(a,b) ( (int)( (u8*)(a) - (u8*)(b) )) +#define END_OF_VAR(v) ((void*)( (char*)(v) + sizeof(v) )) + +#define PTR_DISTANCE(a,b) ( (char*)(a) - (char*)(b) ) +#define PTR_DISTANCE_INT(a,b) ( (int)( (char*)(a) - (char*)(b) )) + +#define GET_INT_SIGN(type) ({ typeof(type) a=-1; a>0 ? 1 : -1; }) +#define GET_INT_TYPE(type) ((int)sizeof(type)*GET_INT_SIGN(type)) // /////////////////////////////////////////////////////////////////////////////// @@ -79,9 +85,16 @@ #else #define HAVE_CLOCK_GETTIME 0 #endif - + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// external types /////////////// /////////////////////////////////////////////////////////////////////////////// +typedef struct ColorSet_t ColorSet_t; +typedef struct RestoreState_t RestoreState_t; +typedef struct LogFile_t LogFile_t; + // /////////////////////////////////////////////////////////////////////////////// /////////////// bits /////////////// @@ -141,6 +154,7 @@ static inline uint Count1Bits64 ( u64 data ) } /////////////////////////////////////////////////////////////////////////////// +// return -1 if bit not found int FindLowest0BitBE ( cvp addr, uint size ); int FindLowest1BitBE ( cvp addr, uint size ); @@ -788,6 +802,20 @@ mem_t BehindMem ( const mem_t src, ccp ref ); //----------------------------------------------------------------------------- +// UTF8 support + +mem_t MidMem8 ( const mem_t src, int begin, int count ); +mem_t ExtractMem8 ( const mem_t src, int begin, int end ); +mem_t ExtractEndMem8 ( const mem_t src, int begin, int end ); + +static mem_t LeftMem8 ( const mem_t src, int count ) + { return ExtractMem8(src,0,count); } + +static mem_t RightMem8 ( const mem_t src, int count ) + { return ExtractEndMem8(src,-count,0); } + +//----------------------------------------------------------------------------- + // Alloc dest buffer and terminate with 0. // If m*.len < 0, then use strlen(). @@ -800,7 +828,7 @@ int CmpMem ( const mem_t s1, const mem_t s2 ); int StrCmpMem ( const mem_t mem, ccp str ); bool LeftStrCmpMemEQ ( const mem_t mem, ccp str ); -// free a string if str is not NULL|EmptyString|MinusString +// free a string if str is !NULL | !EmptyString | !MinusString | !IsCircBuf() void FreeString ( ccp str ); static inline void FreeMem ( mem_t *mem ) @@ -886,24 +914,39 @@ static inline exmem_t ExMemByStringE ( ccp str, ccp end ) return em; } -static inline exmem_t ExMemByS ( ccp str, int len ) +static inline exmem_t ExMemByS ( cvp str, int len ) { exmem_t em = { .data={ str, len }, .is_original=true }; return em; } -static inline exmem_t ExMemByE ( ccp str, ccp end ) +static inline exmem_t ExMemByE ( cvp str, cvp end ) { - exmem_t em = { .data={ str, end-str }, .is_original=true }; + exmem_t em = { .data={ str, (u8*)end-(u8*)str }, .is_original=true }; + return em; +} + +static inline exmem_t ExMemCalloc ( int size ) +{ + char *data = CALLOC(size,1); + exmem_t em = { .data={ data, size }, .is_original=true, .is_alloced=true }; + return em; +} + +static inline exmem_t ExMemDup ( cvp src, int size ) +{ + char *data = MEMDUP(src,size); + exmem_t em = { .data={ data, size+1 }, .is_original=true, .is_alloced=true }; return em; } //----------------------------------------------------------------------------- // interface -// 'em' maybe modified ignoring 'const' -void FreeExMem ( const exmem_t *em ); -void FreeExMemCM ( const exmem_t *em, CopyMode_t copy_mode ); // free if CPM_MOVE +// 'em' can be modified ignoring 'const' +void ResetExMem ( const exmem_t *em ); +void FreeExMem ( const exmem_t *em ); +void FreeExMemCM ( const exmem_t *em, CopyMode_t copy_mode ); // free on CPM_MOVE exmem_t AllocExMemS ( @@ -917,13 +960,17 @@ exmem_t AllocExMemS static inline exmem_t AllocExMemM ( mem_t src, bool try_circ, mem_t orig ) { return AllocExMemS(src.ptr,src.len,try_circ,orig.ptr,orig.len); } -void AssignExMem ( exmem_t *dest, const exmem_t *source, CopyMode_t copy_mode ); +void AssignExMem ( exmem_t *dest, const exmem_t *source, CopyMode_t copy_mode ); +void AssignExMemS ( exmem_t *dest, cvp source, int slen, CopyMode_t copy_mode ); + static inline exmem_t CopyExMem ( const exmem_t *source, CopyMode_t copy_mode ) { exmem_t em = {{0}}; AssignExMem(&em,source,copy_mode); return em; } -exmem_t CopyExMemS ( ccp string, int slen, CopyMode_t copy_mode ); +static inline exmem_t CopyExMemS ( cvp source, int slen, CopyMode_t copy_mode ) + { exmem_t em = {{0}}; AssignExMemS(&em,source,slen,copy_mode); return em; } + static inline exmem_t CopyExMemM ( const mem_t source, CopyMode_t copy_mode ) - { return CopyExMemS(source.ptr,source.len,copy_mode); } + { exmem_t em = {{0}}; AssignExMemS(&em,source.ptr,source.len,copy_mode); return em; } ccp PrintExMem ( const exmem_t * em ); // print to circ-buf @@ -1176,6 +1223,39 @@ uint PurgeCircBuf ( CircBuf_t *cb ); // /////////////////////////////////////////////////////////////////////////////// +/////////////// PrintMode_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PrintMode_t]] + +typedef struct PrintMode_t +{ + FILE *fout; // NULL (stdout) or output file + const ColorSet_t + *cout; // NULL (no colors) or color set for 'fout' + + FILE *ferr; // NULL or error file + const ColorSet_t + *cerr; // NULL (no colors) or color set for 'ferr' + + FILE *flog; // if NULL: use 'ferr' or 'fout' as fallback + const ColorSet_t + *clog; // NULL (no colors) or color set for 'flog' + // if 'flog' is copied, use related 'col*' + + ccp prefix; // NULL (no prefix) or line prefix + ccp eol; // NULL (LF) or end-of-line string + int indent; // indention after prefix + int debug; // debug level (ignored if <=0) + + int mode; // user defined print mode + bool compact; // TRUE: print compact (e.g. no empty lines for readability) +} +PrintMode_t; + +void SetupPrintMode ( PrintMode_t * pm ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// split strings /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -1280,6 +1360,7 @@ extern const char Slash200[201]; // 200 * '/' + NULL // UTF-8 => 3 bytes per char extern const char ThinLine300_3[901]; // 300 * '─' (U+2500) + NULL +extern const char DoubleLine300_3[901]; // 300 * '═' (U+2550) + NULL //----------------------------------------------------------------------------- @@ -1336,9 +1417,14 @@ char * StringCat3E ( char * buf, ccp buf_end, ccp src1, ccp src2, ccp src3 ); char * StringCat2A ( ccp src1, ccp src2 ); char * StringCat3A ( ccp src1, ccp src2, ccp src3 ); -static inline char * StringCopySMem ( char * buf, size_t bufsize, mem_t mem ) +// center string +ccp StringCenterE ( char * buf, ccp buf_end, ccp src, int width ); +static inline ccp StringCenterS ( char *buf, size_t bufsize, ccp src, int width ) + { return StringCenterE(buf,buf+bufsize,src,width); } + +static inline char * StringCopySMem ( char *buf, size_t bufsize, mem_t mem ) { return StringCopySM(buf,bufsize,mem.ptr,mem.len); } -static inline char * StringCopyEMem ( char * buf, ccp buf_end, mem_t mem ) +static inline char * StringCopyEMem ( char *buf, ccp buf_end, mem_t mem ) { return StringCopyEM(buf,buf_end,mem.ptr,mem.len); } char * StringLowerS ( char * buf, size_t bufsize, ccp src ); @@ -1357,6 +1443,12 @@ int StrNumCmp ( ccp a, ccp b ); // count the number of equal bytes and return a value of 0 to SIZE uint CountEqual ( cvp m1, cvp m2, uint size ); +// skip ESC & well known escape sequences (until noc-escape) +char * SkipEscapes ( ccp str ); +char * SkipEscapesE ( ccp str, ccp end ); +char * SkipEscapesS ( ccp str, int size ); +mem_t SkipEscapesM ( mem_t mem ); + // Concatenate path + path, return pointer to buf // Return pointer to buf or path1|path2|buf or alloced string char *PathCatBufPP ( char *buf, size_t bufsize, ccp path1, ccp path2 ); @@ -1495,12 +1587,14 @@ char * PrintID /////////////// circ buf /////////////// /////////////////////////////////////////////////////////////////////////////// -#define CIRC_BUF_MAX_ALLOC 0x0400 // request limit -#define CIRC_BUF_SIZE 0x4000 // internal buffer size +#define CIRC_BUF_MAX_ALLOC 0x0400 // request limit +#define CIRC_BUF_SIZE 0x4000 // internal buffer size // returns true if 'ptr' points into circ buffer => ignored by FreeString() bool IsCircBuf ( cvp ptr ); +//----------------------------------------------------------------------------- + char * GetCircBuf ( // Never returns NULL, but always ALIGN(4) @@ -1510,6 +1604,21 @@ char * GetCircBuf // ==> ERROR0(ERR_OUT_OF_MEMORY) ); +static inline wchar_t * GetCircBufW +( + // Never returns NULL, but always ALIGN(4) + + u32 buf_size // wanted buffer size (wchar elements), + // caller must add 1 for NULL-term + // if final buf_size > CIRC_BUF_MAX_ALLOC: + // ==> ERROR0(ERR_OUT_OF_MEMORY) +) +{ + return (wchar_t*)GetCircBuf(buf_size*sizeof(wchar_t)); +} + +//----------------------------------------------------------------------------- + char * CopyCircBuf ( // Never returns NULL, but always ALIGN(4) @@ -1563,6 +1672,28 @@ void ReleaseCircBuf // /////////////////////////////////////////////////////////////////////////////// +/////////////// get line by list /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetLineByListHelper ( const int *list, ccp beg, ccp mid, ccp end, ccp line ); + +static inline ccp GetTopLineByList ( const int *list ) +{ + return GetLineByListHelper(list,"┌","┬","┐","─"); +} + +static inline ccp GetMidLineByList ( int *list ) +{ + return GetLineByListHelper(list,"├","┼","┤","─"); +} + +static inline ccp GetBottomLineByList ( int *list ) +{ + return GetLineByListHelper(list,"└","┴","┘","─"); +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct IntMode_t /////////////// /////////////////////////////////////////////////////////////////////////////// // [[IntMode_t]] @@ -1631,6 +1762,75 @@ ccp GetIntModeName ( IntMode_t mode ); // /////////////////////////////////////////////////////////////////////////////// +/////////////// lt/eq/gt /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[compare_operator_t]] + +typedef enum compare_operator_t +{ + COP_LT = 1, + COP_EQ = 2, + COP_GT = 4, + + COP_NE = COP_LT | COP_GT, + COP_LE = COP_LT | COP_EQ, + COP_GE = COP_GT | COP_EQ, + + COP_FALSE = 0, + COP_TRUE = COP_LT | COP_EQ | COP_GT, +} +__attribute__ ((packed)) compare_operator_t; + +//----------------------------------------------------------------------------- + +char * ScanCompareOp ( compare_operator_t *dest_op, ccp arg ); +ccp GetCompareOpName ( compare_operator_t op ); + +static inline compare_operator_t NegateCompareOp ( compare_operator_t op ) + { return op ^ COP_TRUE; } + +bool CompareByOpStat ( int stat, compare_operator_t op ); +bool CompareByOpINT ( int a, compare_operator_t op, int b ); +bool CompareByOpUINT ( uint a, compare_operator_t op, uint b ); +bool CompareByOpI64 ( s64 a, compare_operator_t op, s64 b ); +bool CompareByOpU64 ( u64 a, compare_operator_t op, u64 b ); +bool CompareByOpFLT ( float a, compare_operator_t op, float b ); +bool CompareByOpDBL ( double a, compare_operator_t op, double b ); + +static inline bool CompareByOpS ( ccp a, compare_operator_t op, ccp b ) + { return CompareByOpStat(strcmp(a,b),op); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// LOWER/AUTO/UPPER /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[LowerUpper_t]] + +typedef enum LowerUpper_t +{ + LOUP_ERROR = -99, + LOUP_LOWER = -1, + LOUP_AUTO = 0, + LOUP_UPPER = 1, +} +LowerUpper_t; + +extern const KeywordTab_t KeyTab_LOWER_AUTO_UPPER[]; + +int ScanKeywordLowerAutoUpper +( + // returns one of LOUP_* + + 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 +); + +ccp GetKeywordLowerAutoUpper ( LowerUpper_t value ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct FastBuf_t /////////////// /////////////////////////////////////////////////////////////////////////////// // [[FastBuf_t]] @@ -1682,6 +1882,7 @@ ccp GetFastBufStatus ( const FastBuf_t *fb ); int ReserveSpaceFastBuf ( FastBuf_t *fb, uint size ); char * GetSpaceFastBuf ( FastBuf_t *fb, uint size ); +// if size<0: use strlen(source) instead uint InsertFastBuf ( FastBuf_t *fb, int index, cvp source, int size ); char * AppendFastBuf ( FastBuf_t *fb, cvp source, int size ); char * WriteFastBuf ( FastBuf_t *fb, uint offset, cvp source, int size ); @@ -1833,39 +2034,152 @@ int ScanSplitArg // /////////////////////////////////////////////////////////////////////////////// -/////////////// arg_manager_t /////////////// +/////////////// PointerList_t /////////////// /////////////////////////////////////////////////////////////////////////////// -// [[arg_manager_t]] +// [[PointerList_t]] -typedef struct arg_manager_t +typedef struct PointerList_t { - char **argv; // list of strings - int argc; // number of used elements in 'argv' - uint size; // >0: 'argv' is alloced to store # elements - // last NULL element is not counted here + void **list; // 0 terminated list, 1 longer than size + uint used; // num of used elements of 'list' + uint size; // num of alloced elements of 'list' + int grow; // list grows by 1.5*size + grow (at least by 10) } -arg_manager_t; +PointerList_t; //----------------------------------------------------------------------------- -void ResetArgManager ( arg_manager_t *am ); -void SetupArgManager ( arg_manager_t *am, int argc, char ** argv, bool clone ); -void AttachArgManager ( arg_manager_t *am, int argc, char ** argv ); -void CloneArgManager ( arg_manager_t *am, int argc, char ** argv ); +static inline void InitializePointerMgr ( PointerList_t *pl, int grow ) + { DASSERT(pl); memset(pl,0,sizeof(*pl)); pl->grow = grow; } + +void ResetPointerMgr ( PointerList_t *pl ); + +// if n_elem<0: add until NULL pointer +void AddToPointerMgr ( PointerList_t *pl, const void *info ); +void AddListToPointerMgr ( PointerList_t *pl, const void **list, int n_elem ); + +// reset==true: call ResetPointerMgr() after duplicating +void ** DupPointerMgr ( PointerList_t *pl, bool reset ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ArgManager_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ArgManager_t]] + +typedef struct ArgManager_t +{ + LowerUpper_t force_case; // change case? + char **argv; // list of strings + int argc; // number of used elements in 'argv' + uint size; // >0: 'argv' is alloced to store # elements + // last NULL element is not counted here +} +ArgManager_t; + +//----------------------------------------------------------------------------- -void AddSpaceArgManager ( arg_manager_t *am, int needed_space ); +void ResetArgManager ( ArgManager_t *am ); +void SetupArgManager ( ArgManager_t *am, LowerUpper_t force_case, + int argc, char ** argv, bool clone ); +void AttachArgManager ( ArgManager_t *am, int argc, char ** argv ); +void CloneArgManager ( ArgManager_t *am, int argc, char ** argv ); -void CopyArgManager ( arg_manager_t *dest, const arg_manager_t *src ); -void MoveArgManager ( arg_manager_t *dest, arg_manager_t *src ); +void AddSpaceArgManager ( ArgManager_t *am, int needed_space ); -void PrepareEditArgManager ( arg_manager_t *am, int needed_space ); +void CopyArgManager ( ArgManager_t *dest, const ArgManager_t *src ); +void MoveArgManager ( ArgManager_t *dest, ArgManager_t *src ); -uint AppendArgManager ( arg_manager_t *am, ccp arg1, ccp arg2, bool move_arg ); -uint InsertArgManager ( arg_manager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ); -uint ReplaceArgManager( arg_manager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ); -uint RemoveArgManager ( arg_manager_t *am, int pos, int count ); +void PrepareEditArgManager ( ArgManager_t *am, int needed_space ); -void DumpArgManager ( FILE *f, int indent, const arg_manager_t *am, ccp title ); +// return the pos behind last insert arguments +uint AppendArgManager ( ArgManager_t *am, ccp arg1, ccp arg2, bool move_arg ); +uint InsertArgManager ( ArgManager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ); +uint ReplaceArgManager( ArgManager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ); + +// return the pos at removed argument +uint RemoveArgManager ( ArgManager_t *am, int pos, int count ); + +uint ScanSimpleArgManager ( ArgManager_t *am, ccp src ); +uint ScanQuotedArgManager ( ArgManager_t *am, ccp src, bool is_utf8 ); + +// NULL if for both params accepted +bool CheckFilterArgManager ( const ArgManager_t *filter, ccp name ); + +void DumpArgManager ( FILE *f, int indent, const ArgManager_t *am, ccp title ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sizeof_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sizeof_info_t]] + +#define SIZEOF_INFO_TITLE(t) { -1, t }, +#define SIZEOF_INFO_ENTRY(e) { sizeof(e), #e }, +#define SIZEOF_INFO_SEP() { -2, 0 }, +#define SIZEOF_INFO_TERM() { -9, 0 }, + +typedef struct sizeof_info_t +{ + int size; + ccp name; +} +__attribute__ ((packed)) sizeof_info_t; + +//----------------------------------------------------------------------------- + +// basic lists +extern const sizeof_info_t sizeof_info_linux[]; // C and Linux types +extern const sizeof_info_t sizeof_info_dclib[]; // dcLib types + +// default list of lists for GCMD_Sizeof(), terminated by 0 +extern const sizeof_info_t *sizeof_info_default[]; +extern PointerList_t SizeofInfoMgr; + +static inline void AddDefaultToSizeofInfoMgr(void) + { AddListToPointerMgr(&SizeofInfoMgr,(cvpp)sizeof_info_default,-1); } + +static inline void AddInfoToSizeofInfoMgr ( const sizeof_info_t *info ) + { AddToPointerMgr(&SizeofInfoMgr,info); } + +static inline void AddListToSizeofInfoMgr ( const sizeof_info_t **list ) + { AddListToPointerMgr(&SizeofInfoMgr,(cvpp)list,-1); } + +static inline const sizeof_info_t ** GetSizeofInfoMgrList(void) + { return SizeofInfoMgr.used ? (const sizeof_info_t**)SizeofInfoMgr.list : 0; } + +/////////////////////////////////////////////////////////////////////////////// +// [[sizeof_info_order_t]] + +typedef enum sizeof_info_order_t +{ + SIZEOF_ORDER_NONE, // don't sort output, but print categories + SIZEOF_ORDER_NAME, // Sort the output by name (case-insensitive) + // and suppress output of categories. + SIZEOF_ORDER_SIZE, // Sort the output by size and suppress output of categories. +} +sizeof_info_order_t; + +//----------------------------------------------------------------------------- + +void ListSizeofInfo +( + const PrintMode_t *p_pm, // NULL or print mode + const sizeof_info_t **si_list, // list of list of entries + ArgManager_t *p_filter, // NULL or filter arguments, LOUP_LOWER recommended + sizeof_info_order_t order // kind of order +); + +static inline void ListSizeofInfoMgr +( + const PrintMode_t *p_pm, // NULL or print mode + PointerList_t *mgr, // valid PointerList_t + ArgManager_t *p_filter, // NULL or filter arguments, LOUP_LOWER recommended + sizeof_info_order_t order // kind of order +) +{ + ListSizeofInfo(p_pm,(const sizeof_info_t**)mgr->list,p_filter,order); +} // /////////////////////////////////////////////////////////////////////////////// @@ -2369,7 +2683,7 @@ extern ccp TableDecode64default, TableEncode64default; uint GetEscapedSize ( - // returns the needed buffer size for PrintEscapedString() + // returns the needed buffer size for PrintEscapedString() ccp source, // NULL or string to print int src_len, // length of string. if -1, str is null terminated @@ -2465,7 +2779,7 @@ uint EncodeBase64 // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -2496,7 +2810,7 @@ uint EncodeBase64ml // ml: multi line // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -2644,7 +2958,7 @@ uint EncodeByMode // returns the number of valid bytes in 'buf'. Result is NULL-terminated. char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 ccp source, // string to encode int slen, // length of string. if -1, str is null terminated EncodeMode_t emode // encoding mode @@ -2751,6 +3065,45 @@ static char * QuoteBashCircS ( ccp src, cvp if_null, CharMode_t cm ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// struct Escape_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[EscapeStat_t]] + +typedef enum EscapeStat_t +{ + ESCST_NONE, // nothing scanned (data incomplete) + ESCST_CHAR, // single char scanned (char behind '\e') + ESCST_FE, // FE scanned (single byte) + ESCST_SS2, // SS2 scanned ('N' + single byte), code := second char + ESCST_SS3, // SS3 scanned ('O' + single byte) , code := second char + ESCST_CSI, // 'Control Sequence Introduce' based string scanned, code := final char + ESCST_OSC, // 'Operating System Command' based string scanned + + ESCST__N +} +EscapeStat_t; + +extern const char ecape_stat_name[ESCST__N+1][5]; + +/////////////////////////////////////////////////////////////////////////////// +// [[Escape_t]] + +typedef struct Escape_t +{ + EscapeStat_t status; // one of EscapeStat_t + int scanned_len; // number of scanned chars + u32 code; // depends on 'status', see enum EscapeStat_t + mem_t esc; // if status==2: pointer+len to escape string; else (0,0) +} +Escape_t; + +//----------------------------------------------------------------------------- + +// returns esc->status +EscapeStat_t CheckEscape ( Escape_t *esc, cvp source, cvp end, bool check_esc ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// scan keywords /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -2977,35 +3330,6 @@ ccp GetKeywordOffAutoOn ( OffOn_t value ); // /////////////////////////////////////////////////////////////////////////////// -/////////////// LOWER/AUTO/UPPER /////////////// -/////////////////////////////////////////////////////////////////////////////// -// [[LowerUpper_t]] - -typedef enum LowerUpper_t -{ - LOUP_ERROR = -99, - LOUP_LOWER = -1, - LOUP_AUTO = 0, - LOUP_UPPER = 1, -} -LowerUpper_t; - -extern const KeywordTab_t KeyTab_LOWER_AUTO_UPPER[]; - -int ScanKeywordLowerAutoUpper -( - // returns one of LOUP_* - - 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 -); - -ccp GetKeywordLowerAutoUpper ( LowerUpper_t value ); - -// -/////////////////////////////////////////////////////////////////////////////// /////////////// scan command lists /////////////// /////////////////////////////////////////////////////////////////////////////// // @@ -3280,14 +3604,15 @@ unsigned long long int str2ull ( const char *nptr, char **endptr, int base ); typedef enum sizeform_mode_t { DC_SFORM_ALIGN = 0x01, // aligned output - DC_SFORM_NARROW = 0x02, // suppress space between number and unit - DC_SFORM_UNIT1 = 0x04, // limit unit to 1 character (space for bytes) - DC_SFORM_INC = 0x08, // increment to next factor if no fraction is lost - DC_SFORM_PLUS = 0x10, // on signed output: print PLUS sign for values >0 - DC_SFORM_DASH = 0x20, // on NULL: print only a dash (minus sign) + DC_SFORM_CENTER = 0x02, // aligned and centered output + DC_SFORM_NARROW = 0x04, // suppress space between number and unit + DC_SFORM_UNIT1 = 0x08, // limit unit to 1 character (space for bytes) + DC_SFORM_INC = 0x10, // increment to next factor if no fraction is lost + DC_SFORM_PLUS = 0x20, // on signed output: print PLUS sign for values >0 + DC_SFORM_DASH = 0x40, // on NULL: print only a dash (minus sign) - DC_SFORM__MASK = 0x3f, // mask of above - DC_SFORM__AUTO = 0x40, // hint for some functions to decide by themself + DC_SFORM__MASK = 0x7f, // mask of above + DC_SFORM__AUTO = 0x80, // hint for some functions to decide by themself DC_SFORM_TINY = DC_SFORM_NARROW | DC_SFORM_UNIT1, } @@ -3302,6 +3627,13 @@ double RoundD7bytes ( double d ); /////////////////////////////////////////////////////////////////////////////// +// Parameters: +// char *buf, // result buffer +// // NULL: use a local circulary static buffer +// size_t buf_size, // size of 'buf', ignored if buf==NULL +// * num, // number to print +// sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH[not *D*()] + char * PrintNumberU4 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); char * PrintNumberU5 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); char * PrintNumberU6 ( char *buf, size_t buf_size, u64 num, sizeform_mode_t mode ); @@ -3315,13 +3647,6 @@ char * PrintNumberD5 ( char *buf, size_t buf_size, double num, sizeform_mode_t m char * PrintNumberD6 ( char *buf, size_t buf_size, double num, sizeform_mode_t mode ); char * PrintNumberD7 ( char *buf, size_t buf_size, double num, sizeform_mode_t mode ); -// Parameters: -// char *buf, // result buffer -// // NULL: use a local circulary static buffer -// size_t buf_size, // size of 'buf', ignored if buf==NULL -// * num, // number to print -// sizeform_mode_t mode // any of DC_SFORM_ALIGN, DC_SFORM_DASH[not *D*()] - /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // [[size_mode_t]] @@ -3475,6 +3800,24 @@ u64 GetSizeU64 ( ccp src, char **end, int force_base ); double GetSizeD ( ccp src, char **end, int force_base ); //----------------------------------------------------------------------------- +// [[range_opt_t]] + +typedef enum range_opt_t +{ + RAOPT_COLON = 0x01, // allow ':' as from/to separator + RAOPT_MINUS = 0x02, // allow '-' as from/to separator + RAOPT_HASH = 0x04, // allow '#' as from/size separator + RAOPT_COMMA = 0x08, // allow ',' as from/size separator + RAOPT_NEG = 0x10, // accept negative numbers as index relative to max_value + RAOPT_ASSUME_0 = 0x20, // allow ':-#,' as first char and assume 0 as first value + RAOPT_F_FORCE = 0x40, // flag to avoid value 0 as default alias + RAOPT__ALL = 0x7f, // all possible bits + + RAOPT__DEFAULT = RAOPT_COLON | RAOPT_HASH, +} +range_opt_t; + +//----------------------------------------------------------------------------- char * ScanSizeRange ( @@ -3487,7 +3830,8 @@ char * ScanSizeRange u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); char * ScanSizeRangeU32 @@ -3501,7 +3845,23 @@ char * ScanSizeRangeU32 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +); + +char * ScanSizeRangeS32 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s32 *num1, // not NULL: store 'from' result + s32 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); char * ScanSizeRangeU64 @@ -3515,7 +3875,23 @@ char * ScanSizeRangeU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +); + +char * ScanSizeRangeS64 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s64 *num1, // not NULL: store 'from' result + s64 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); //----------------------------------------------------------------------------- @@ -3531,7 +3907,8 @@ char * ScanSizeRangeList u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); char * ScanSizeRangeListU32 @@ -3545,7 +3922,8 @@ char * ScanSizeRangeListU32 u32 default_factor, // use this factor if number hasn't one u32 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); char * ScanSizeRangeListU64 @@ -3559,7 +3937,8 @@ char * ScanSizeRangeListU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ); //----------------------------------------------------------------------------- @@ -3610,78 +3989,49 @@ enumError ScanSizeOptU32 // /////////////////////////////////////////////////////////////////////////////// -/////////////// Scan Duration /////////////// -/////////////////////////////////////////////////////////////////////////////// -// [[ScanDuration_t]] - -typedef enum ScanDuration_t -{ - SDUMD_SKIP_BLANKS = 0x01, // skip leading blanks - SDUMD_ALLOW_COLON = 0x02, // allow 'H:M' and 'H:M:S' - SDUMD_ALLOW_DIV = 0x04, // allow fraction 'n/d' as number - - SDUMD_ALLOW_LIST = 0x10, // allow lists like '9h12m' - SDUMD_ALLOW_SPACE_SEP = 0x20, // lists: allow spaces as list separator - SDUMD_ALLOW_PLUS_SEP = 0x40, // lists: allow '+' as list separator - SDUMD_ALLOW_MINUS_SEP = 0x80, // lists: allow '-' as list separator - - SDUMD_M_ALL = 0xf7, -} -ScanDuration_t; - -//----------------------------------------------------------------------------- - -char * ScanDuration -( - double *num, // not NULL: store result - ccp source, // source text - double default_factor, // default factor if no SI unit found - ScanDuration_t mode -); - -double GetDurationFactor ( char ch ); - -char * ScanSIFactor -( - // returns end of scanned string - - double *num, // not NULL: store result - ccp source, // source text - double default_factor // return this if no factor found -); - -// -/////////////////////////////////////////////////////////////////////////////// /////////////// time & timer /////////////// /////////////////////////////////////////////////////////////////////////////// //--- conversion factors -#define SEC_PER_MIN 60 -#define SEC_PER_HOUR 3600 -#define SEC_PER_DAY 86400 -#define SEC_PER_WEEK 604800 +#define SEC_PER_MIN 60 +#define SEC_PER_HOUR 3600 +#define SEC_PER_DAY 86400 +#define SEC_PER_WEEK 604800 +#define SEC_PER_MONTH 2629746 // 365.2425 * SEC_PER_DAY / 12 +#define SEC_PER_QUARTER 7889238 // 365.2425 * SEC_PER_DAY / 4 +#define SEC_PER_YEAR 31556952 // 365.2425 * SEC_PER_DAY -#define MSEC_PER_SEC 1000ull -#define MSEC_PER_MIN (60*MSEC_PER_SEC) -#define MSEC_PER_HOUR (3600*MSEC_PER_SEC) -#define MSEC_PER_DAY (SEC_PER_DAY*MSEC_PER_SEC) -#define MSEC_PER_WEEK (SEC_PER_WEEK*MSEC_PER_SEC) +#define MSEC_PER_SEC 1000ll +#define MSEC_PER_MIN (MSEC_PER_SEC*SEC_PER_MIN) +#define MSEC_PER_HOUR (MSEC_PER_SEC*SEC_PER_HOUR) +#define MSEC_PER_DAY (MSEC_PER_SEC*SEC_PER_DAY) +#define MSEC_PER_WEEK (MSEC_PER_SEC*SEC_PER_WEEK) +#define MSEC_PER_MONTH (MSEC_PER_SEC*SEC_PER_MONTH) +#define MSEC_PER_QUARTER (MSEC_PER_SEC*SEC_PER_QUARTER) +#define MSEC_PER_YEAR (MSEC_PER_SEC*SEC_PER_YEAR) -#define USEC_PER_MSEC 1000ull -#define USEC_PER_SEC 1000000ull -#define USEC_PER_MIN (60*USEC_PER_SEC) -#define USEC_PER_HOUR (3600*USEC_PER_SEC) -#define USEC_PER_DAY (SEC_PER_DAY*USEC_PER_SEC) -#define USEC_PER_WEEK (SEC_PER_WEEK*USEC_PER_SEC) +#define USEC_PER_MSEC 1000ll +#define USEC_PER_SEC 1000000ll +#define USEC_PER_MIN (USEC_PER_SEC*SEC_PER_MIN) +#define USEC_PER_HOUR (USEC_PER_SEC*SEC_PER_HOUR) +#define USEC_PER_DAY (USEC_PER_SEC*SEC_PER_DAY) +#define USEC_PER_WEEK (USEC_PER_SEC*SEC_PER_WEEK) +#define USEC_PER_MONTH (USEC_PER_SEC*SEC_PER_MONTH) +#define USEC_PER_QUARTER (USEC_PER_SEC*SEC_PER_QUARTER) +#define USEC_PER_YEAR (USEC_PER_SEC*SEC_PER_YEAR) + +#define NSEC_PER_USEC 1000ll +#define NSEC_PER_MSEC 1000000ll +#define NSEC_PER_SEC 1000000000ll +#define NSEC_PER_MIN (NSEC_PER_SEC*SEC_PER_MIN) +#define NSEC_PER_HOUR (NSEC_PER_SEC*SEC_PER_HOUR) +#define NSEC_PER_DAY (NSEC_PER_SEC*SEC_PER_DAY) +#define NSEC_PER_WEEK (NSEC_PER_SEC*SEC_PER_WEEK) +#define NSEC_PER_MONTH (NSEC_PER_SEC*SEC_PER_MONTH) +#define NSEC_PER_QUARTER (NSEC_PER_SEC*SEC_PER_QUARTER) +#define NSEC_PER_YEAR (NSEC_PER_SEC*SEC_PER_YEAR) -#define NSEC_PER_USEC 1000ull -#define NSEC_PER_MSEC 1000000ull -#define NSEC_PER_SEC 1000000000ull -#define NSEC_PER_MIN (60*NSEC_PER_SEC) -#define NSEC_PER_HOUR (3600*NSEC_PER_SEC) -#define NSEC_PER_DAY (SEC_PER_DAY*NSEC_PER_SEC) -#define NSEC_PER_WEEK (SEC_PER_WEEK*NSEC_PER_SEC) //--- time types @@ -3694,6 +4044,19 @@ typedef s64 s_usec_t; // signed type to store time as microseconds typedef u64 u_nsec_t; // unsigned type to store time as nanoseconds typedef s64 s_nsec_t; // signed type to store time as nanoseconds + +//--- max values time types + +#define S_SEC_MAX S32_MAX +#define U_SEC_MAX U32_MAX +#define S_MSEC_MAX S64_MAX +#define U_MSEC_MAX U64_MAX +#define S_USEC_MAX S64_MAX +#define U_USEC_MAX U64_MAX +#define S_NSEC_MAX S64_MAX +#define U_NSEC_MAX U64_MAX + +//----------------------------------------------------------------------------- // [[DayTime_t]] typedef struct DayTime_t { @@ -3716,6 +4079,14 @@ extern int timezone_adjust_isdst; /////////////////////////////////////////////////////////////////////////////// +extern ccp micro_second_char; // default = "u" + +static inline void SetMicroSecondChar ( bool use_micro ) + { micro_second_char = use_micro ? "µ" : "u"; } + + +/////////////////////////////////////////////////////////////////////////////// + void SetupTimezone ( bool force ); int GetTimezoneAdjust ( time_t tim ); @@ -3727,17 +4098,55 @@ u_msec_t GetTimeMSec ( bool localtime ); u_usec_t GetTimeUSec ( bool localtime ); u_nsec_t GetTimeNSec ( bool localtime ); +struct timespec GetClockTimer(void); +u_sec_t GetTimerSec(void); u_msec_t GetTimerMSec(void); u_usec_t GetTimerUSec(void); u_nsec_t GetTimerNSec(void); -static inline u64 double2msec ( double d ) { return d>0.0 ? (u64)trunc( 1e3*d+0.5 ) : 0; } -static inline u64 double2usec ( double d ) { return d>0.0 ? (u64)trunc( 1e6*d+0.5 ) : 0; } -static inline u64 double2nsec ( double d ) { return d>0.0 ? (u64)trunc( 1e9*d+0.5 ) : 0; } +//----------------------------------------------------------------------------- + +static inline u_sec_t Time2TimerSec ( u_sec_t tim, bool localtime ) + { return tim - GetTimeSec(localtime) + GetTimerSec(); } + +static inline u_msec_t Time2TimerMSec ( u_msec_t tim, bool localtime ) + { return tim - GetTimeMSec(localtime) + GetTimerMSec(); } + +static inline u_usec_t Time2TimerUSec ( u_usec_t tim, bool localtime ) + { return tim - GetTimeUSec(localtime) + GetTimerUSec(); } -static inline double msec2double ( u64 num ) { return num * 1e-3; } -static inline double usec2double ( u64 num ) { return num * 1e-6; } -static inline double nsec2double ( u64 num ) { return num * 1e-9; } +static inline u_nsec_t Time2TimerNSec ( u_nsec_t tim, bool localtime ) + { return tim - GetTimeNSec(localtime) + GetTimerNSec(); } + + +static inline u_sec_t Timer2TimeSec ( u_sec_t tim, bool localtime ) + { return tim - GetTimerSec() + GetTimeSec(localtime); } + +static inline u_msec_t Timer2TimeMSec ( u_msec_t tim, bool localtime ) + { return tim - GetTimerMSec() + GetTimeMSec(localtime); } + +static inline u_usec_t Timer2TimeUSec ( u_usec_t tim, bool localtime ) + { return tim - GetTimerUSec() + GetTimeUSec(localtime); } + +static inline u_nsec_t Timer2TimeNSec ( u_nsec_t tim, bool localtime ) + { return tim - GetTimerNSec() + GetTimeNSec(localtime); } + +//----------------------------------------------------------------------------- + +static inline u_sec_t double2sec ( double d ) { return d>0.0 ? (u_msec_t)trunc( d+0.5 ) : 0; } +static inline u_msec_t double2msec ( double d ) { return d>0.0 ? (u_msec_t)trunc( 1e3*d+0.5 ) : 0; } +static inline u_usec_t double2usec ( double d ) { return d>0.0 ? (u_usec_t)trunc( 1e6*d+0.5 ) : 0; } +static inline u_nsec_t double2nsec ( double d ) { return d>0.0 ? (u_nsec_t)trunc( 1e9*d+0.5 ) : 0; } + +static inline s_sec_t double2ssec ( double d ) { return (s_sec_t) floor( d+0.5 ); } +static inline s_msec_t double2smsec ( double d ) { return (s_msec_t) floor( 1e3*d+0.5 ); } +static inline s_usec_t double2susec ( double d ) { return (s_usec_t) floor( 1e6*d+0.5 ); } +static inline s_nsec_t double2snsec ( double d ) { return (s_nsec_t) floor( 1e9*d+0.5 ); } + +static inline double sec2double ( s_sec_t num ) { return num; } +static inline double msec2double ( s_msec_t num ) { return num * 1e-3; } +static inline double usec2double ( s_usec_t num ) { return num * 1e-6; } +static inline double nsec2double ( s_nsec_t num ) { return num * 1e-9; } //--- another epoch: Monday, 2001-01-01 @@ -3747,13 +4156,57 @@ static inline double nsec2double ( u64 num ) { return num * 1e-9; } #define EPOCH_2001_NSEC (EPOCH_2001_SEC*NSEC_PER_SEC) /////////////////////////////////////////////////////////////////////////////// +// [[CurrentTime_t]] + +typedef struct CurrentTime_t +{ + u_sec_t sec; + u_msec_t msec; + u_usec_t usec; +} +CurrentTime_t; + +//----------------------------------------------------------------------------- + +CurrentTime_t GetCurrentTime ( bool localtime ); + +extern CurrentTime_t current_time; +static inline void UpdateCurrentTime(void) + { current_time = GetCurrentTime(false); } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// PrintRefTime*() return temporary buffer by GetCircBuf(), field width 10 +// Formats: +// ±10h: " %k:%M:%S" +// ±12d: " %e. %k:%M" +// *: "%F" + +ccp PrintRefTime +( + + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +); + +ccp PrintRefTimeUTC +( + + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +); + +//----------------------------------------------------------------------------- ccp PrintTimeByFormat ( // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ); ccp PrintTimeByFormatUTC @@ -3761,7 +4214,7 @@ ccp PrintTimeByFormatUTC // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ); //----------------------------------------------------------------------------- @@ -3786,6 +4239,24 @@ ccp PrintNSecByFormatUTC uint nsec // nanosecond of second ); +ccp PrintNTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +); + +ccp PrintNTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +); + //----------------------------------------------------------------------------- static inline ccp PrintUSecByFormat @@ -3814,6 +4285,70 @@ static inline ccp PrintUSecByFormatUTC return PrintNSecByFormatUTC(format,tim,usec*NSEC_PER_USEC); } +ccp PrintUTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +); + +ccp PrintUTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +); + +//----------------------------------------------------------------------------- + +static inline ccp PrintMSecByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + time_t tim, // seconds since epoch + uint msec // millisecond of second +) +{ + return PrintNSecByFormat(format,tim,msec*NSEC_PER_MSEC); +} + +static inline ccp PrintMSecByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + time_t tim, // seconds since epoch + uint msec // millisecond of second +) +{ + return PrintNSecByFormatUTC(format,tim,msec*NSEC_PER_MSEC); +} + +ccp PrintMTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +); + +ccp PrintMTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +); + //----------------------------------------------------------------------------- ccp PrintTimevalByFormat @@ -3926,7 +4461,7 @@ ccp PrintTimer3 // helper function u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ); static inline ccp PrintTimerSec3 @@ -3935,10 +4470,10 @@ static inline ccp PrintTimerSec3 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_sec_t sec, // seconds to print - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer3(buf,buf_size,sec,-1,aligned); + return PrintTimer3(buf,buf_size,sec,-1,mode); } static inline ccp PrintTimerMSec3 @@ -3947,10 +4482,10 @@ static inline ccp PrintTimerMSec3 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_msec_t msec, // microseconds to print - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer3(buf,buf_size,msec/1000,msec%1000*1000,aligned); + return PrintTimer3(buf,buf_size,msec/1000,msec%1000*1000,mode); } static inline ccp PrintTimerUSec3 @@ -3959,10 +4494,10 @@ static inline ccp PrintTimerUSec3 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_usec_t usec, // microseconds to print - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer3(buf,buf_size,usec/1000000,usec%1000000,aligned); + return PrintTimer3(buf,buf_size,usec/1000000,usec%1000000,mode); } //----------------------------------------------------------------------------- @@ -3975,7 +4510,7 @@ ccp PrintTimer4 // helper function u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ); static inline ccp PrintTimerSec4 @@ -3984,10 +4519,10 @@ static inline ccp PrintTimerSec4 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_sec_t sec, // seconds to print - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer4(buf,buf_size,sec,-1,aligned); + return PrintTimer4(buf,buf_size,sec,-1,mode); } static inline ccp PrintTimerMSec4 @@ -3996,10 +4531,10 @@ static inline ccp PrintTimerMSec4 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_msec_t msec, // microseconds to print - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer4(buf,buf_size,msec/1000,msec%1000*1000,aligned); + return PrintTimer4(buf,buf_size,msec/MSEC_PER_SEC,msec%MSEC_PER_SEC*USEC_PER_SEC,mode); } static inline ccp PrintTimerUSec4 @@ -4008,23 +4543,36 @@ static inline ccp PrintTimerUSec4 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_usec_t usec, // microseconds to print - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH +) +{ + return PrintTimer4(buf,buf_size,usec/USEC_PER_SEC,usec%USEC_PER_SEC,mode); +} + +static inline ccp PrintTimerNSec4 +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_nsec_t nsec, // nanoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { - return PrintTimer4(buf,buf_size,usec/1000000,usec%1000000,aligned); + const u_usec_t usec = nsec / NSEC_PER_USEC; + return PrintTimer4(buf,buf_size,usec/USEC_PER_SEC,usec%USEC_PER_SEC,mode); } //----------------------------------------------------------------------------- -ccp PrintTimer6 // helper function +ccp PrintTimer6N // helper function ( char * buf, // result buffer (>6 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u64 sec, // seconds to print - int usec, // 0...999999: usec fraction, - // otherwise suppress ms/us output - bool aligned // true: print aligned 6 character output + int nsec, // 0...999999999: nsec fraction, + // otherwise suppress ms/us/ns output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ); static inline ccp PrintTimerSec6 @@ -4033,10 +4581,10 @@ static inline ccp PrintTimerSec6 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_sec_t sec, // seconds to print - bool aligned // true: print aligned 6 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ) { - return PrintTimer6(buf,buf_size,sec,-1,aligned); + return PrintTimer6N(buf,buf_size,sec,-1,mode); } static inline ccp PrintTimerMSec6 @@ -4045,10 +4593,11 @@ static inline ccp PrintTimerMSec6 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_msec_t msec, // microseconds to print - bool aligned // true: print aligned 6 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ) { - return PrintTimer6(buf,buf_size,msec/1000,msec%1000*1000,aligned); + return PrintTimer6N( buf, buf_size, + msec/MSEC_PER_SEC, msec%MSEC_PER_SEC*NSEC_PER_MSEC, mode ); } static inline ccp PrintTimerUSec6 @@ -4057,10 +4606,24 @@ static inline ccp PrintTimerUSec6 // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u_usec_t usec, // microseconds to print - bool aligned // true: print aligned 6 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH +) +{ + return PrintTimer6N( buf, buf_size, + usec/USEC_PER_SEC, usec%USEC_PER_SEC*NSEC_PER_USEC, mode ); +} + +static inline ccp PrintTimerNSec6 +( + char * buf, // result buffer (>6 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + u_nsec_t nsec, // nanoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ) { - return PrintTimer6(buf,buf_size,usec/1000000,usec%1000000,aligned); + return PrintTimer6N( buf, buf_size, + nsec/NSEC_PER_SEC, nsec%NSEC_PER_SEC, mode ); } //----------------------------------------------------------------------------- @@ -4071,19 +4634,31 @@ ccp PrintTimerUSec4s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ); +static inline ccp PrintTimerNSec4s +( + char * buf, // result buffer (>4 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_nsec_t nsec, // nanoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH +) +{ + return PrintTimerUSec4s(buf,buf_size,nsec/NSEC_PER_USEC,mode); +} + static inline ccp PrintTimerMSec4s ( char * buf, // result buffer (>4 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_msec_t msec, // milliseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { - return PrintTimerUSec4s(buf,buf_size,1000*msec,mode); + return PrintTimerUSec4s(buf,buf_size,msec*USEC_PER_MSEC,mode); } static inline ccp PrintTimerSec4s @@ -4092,7 +4667,7 @@ static inline ccp PrintTimerSec4s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL int sec, // seconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { return PrintTimerUSec4s(buf,buf_size,1000000ll*sec,mode); @@ -4100,13 +4675,22 @@ static inline ccp PrintTimerSec4s //----------------------------------------------------------------------------- +ccp PrintTimerNSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_nsec_t nsec, // nanoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH +); + ccp PrintTimerUSec7s ( char * buf, // result buffer (>7 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ); static inline ccp PrintTimerMSec7s @@ -4115,7 +4699,7 @@ static inline ccp PrintTimerMSec7s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_msec_t msec, // milliseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { return PrintTimerUSec7s(buf,buf_size,1000*msec,mode); @@ -4127,12 +4711,169 @@ static inline ccp PrintTimerSec7s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL int sec, // seconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { return PrintTimerUSec7s(buf,buf_size,1000000ll*sec,mode); } +//----------------------------------------------------------------------------- + +ccp PrintAge3Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +); + +ccp PrintAge4Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +); + +ccp PrintAge6Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +); + +ccp PrintAge7Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Scan Duration /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ScanDuration_t]] + +typedef enum ScanDuration_t +{ + SDUMD_SKIP_BLANKS = 0x01, // skip leading blanks + SDUMD_ALLOW_COLON = 0x02, // allow 'H:M' and 'H:M:S' + SDUMD_ALLOW_DIV = 0x04, // allow fraction 'n/d' as number + SDUMD_ALLOW_CAPITALS = 0x08, // allow upper case letters + + SDUMD_ALLOW_LIST = 0x10, // allow lists like '9h12m' + SDUMD_ALLOW_SPACE_SEP = 0x20, // lists: allow spaces as list separator + SDUMD_ALLOW_PLUS_SEP = 0x40, // lists: allow '+' as list separator + SDUMD_ALLOW_MINUS_SEP = 0x80, // lists: allow '-' as list separator + + SDUMD_M_DEFAULT = 0xf7, + SDUMD_M_ALL = 0xff, +} +ScanDuration_t; + +//----------------------------------------------------------------------------- + +char * ScanDuration +( + // Returns a pointer to the the first not used character. + // On error, it is 'source' and '*seconds' is set to 0.0. + + double *seconds, // not NULL: store result (number of seconds) + ccp source, // source text + double default_factor, // default factor if no SI unit found + ScanDuration_t mode // flags +); + +double GetDurationFactor ( char ch ); + +char * ScanSIFactor +( + // returns end of scanned string + + double *num, // not NULL: store result + ccp source, // source text + double default_factor // return this if no factor found +); + +//----------------------------------------------------------------------------- + +// simple interfaces to ScanDuration(), SDUMD_M_DEFAULT is used as mode +double str2duration ( ccp src, char **endptr, double default_factor ); + + +static inline u_sec_t str2sec ( ccp src, char **endptr ) + { return double2sec(str2duration(src,endptr,1.0)); } + +static inline u_msec_t str2msec ( ccp src, char **endptr ) + { return double2msec(str2duration(src,endptr,1.0)); } + +static inline u_usec_t str2usec ( ccp src, char **endptr ) + { return double2usec(str2duration(src,endptr,1.0)); } + +static inline u_nsec_t str2nsec ( ccp src, char **endptr ) + { return double2nsec(str2duration(src,endptr,1.0)); } + + +static inline s_sec_t str2ssec ( ccp src, char **endptr ) + { return double2ssec(str2duration(src,endptr,1.0)); } + +static inline s_msec_t str2smsec ( ccp src, char **endptr ) + { return double2smsec(str2duration(src,endptr,1.0)); } + +static inline s_usec_t str2susec ( ccp src, char **endptr ) + { return double2susec(str2duration(src,endptr,1.0)); } + +static inline s_nsec_t str2snsec ( ccp src, char **endptr ) + { return double2snsec(str2duration(src,endptr,1.0)); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// Signals & Sleep /////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern volatile int SIGINT_level; +extern volatile int SIGHUP_level; +extern volatile int SIGALRM_level; +extern volatile int SIGCHLD_level; +extern volatile int SIGPIPE_level; +extern volatile int SIGUSR1_level; +extern volatile int SIGUSR2_level; + +// no timout for SIGPIPE +extern u_sec_t SIGINT_sec; +extern u_sec_t SIGHUP_sec; +extern u_sec_t SIGALRM_sec; +extern u_sec_t SIGCHLD_sec; +extern u_sec_t SIGUSR1_sec; +extern u_sec_t SIGUSR2_sec; + +extern int SIGINT_level_max; +extern bool ignore_SIGINT; +extern bool ignore_SIGHUP; + +extern int redirect_signal_pid; +extern LogFile_t log_signal_file; + + +extern volatile int SIGINT_level; +extern volatile int SIGHUP_level; +extern volatile int SIGALRM_level; +extern volatile int SIGPIPE_level; + +extern int SIGINT_level_max; +extern bool ignore_SIGINT; +extern bool ignore_SIGHUP; + +void SetupSignalHandler ( int max_sigint_level, FILE *log_file ); + +//----------------------------------------------------------------------------- + +int SleepNSec ( s_nsec_t nsec ); + +static inline int SleepUSec ( s_usec_t usec ) + { return SleepNSec( usec * NSEC_PER_USEC ); } + +static inline int SleepMSec ( s_msec_t msec ) + { return SleepNSec( msec * NSEC_PER_MSEC ); } + +static inline int SleepSec ( s_sec_t sec ) + { return SleepNSec( sec * NSEC_PER_SEC ); } + // /////////////////////////////////////////////////////////////////////////////// /////////////// scan date & time /////////////// @@ -4362,13 +5103,383 @@ static inline ccp year2text // /////////////////////////////////////////////////////////////////////////////// +/////////////// usage counter /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if IS_DEVELOP + #define TEST_USAGE_COUNT 0 + #define USAGE_COUNT_ENABLED 0 +#else + #define TEST_USAGE_COUNT 0 + #define USAGE_COUNT_ENABLED 0 +#endif + +#if TEST_USAGE_COUNT + #define USAGE_COUNT_ENTRIES 11 // number of entries +#else + #define USAGE_COUNT_ENTRIES 30 // number of entries +#endif + +#define USAGE_COUNT_FIRST_ADD 8 // first entry where values wil be added +#define USAGE_COUNT_NEXT_MIN(v) (15*(v)/8) + +//----------------------------------------------------------------------------- +// [[UsageParam_t]] + +typedef struct UsageParam_t +{ + uint used; // number of used entries + bool enabled; // TRUE: statistics are enabled + bool is_active; // TRUE: counter is active (set by framework) + + //--- not saved/restored + + bool dirty; // TRUE: record needs an update @ 'update_nsec' + u_nsec_t update_nsec; // next update, based on GetTimerNSec() + u_nsec_t force_update_nsec; // force next update, based on GetTimerNSec() +} +UsageParam_t; + +//----------------------------------------------------------------------------- +// [[UsageCountEntry_t]] + +typedef struct UsageCountEntry_t +{ + u64 count; // counter + u_nsec_t elapsed_nsec; // elapsed time, by GetTimerNSec() + u_sec_t time_sec; // time of record, by GetTimeSec(false) + float top1s; // = 100.0*(utime+stime)/elapsed for 1s + float top5s; // = 100.0*(utime+stime)/elapsed for 5s +} +__attribute__ ((packed)) UsageCountEntry_t; + +//----------------------------------------------------------------------------- +// [[UsageCount_t]] + +typedef struct UsageCount_t +{ + UsageParam_t par; // base parameters + UsageCountEntry_t ref; // reference with absolute values + #if TEST_USAGE_COUNT + UsageCountEntry_t entry[USAGE_COUNT_ENTRIES+1]; // list of entries with delta values + #else + UsageCountEntry_t entry[USAGE_COUNT_ENTRIES]; // list of entries with delta values + #endif +} +UsageCount_t; + +//----------------------------------------------------------------------------- + +extern int usage_count_enabled; + +void EnableUsageCount(void); +static inline void DisableUsageCount(void) { usage_count_enabled--; } + +//----------------------------------------------------------------------------- + +// n_rec<0: clear from end, n_rec>=0: clear from index +void ClearUsageCount ( UsageCount_t *uc, int count ); + +// NOW_NSEC: 0 or GetTimerNSec(), but not GetTime*() +void UpdateUsageCount ( UsageCount_t *uc, u_nsec_t now_nsec ); +void UpdateUsageCountIncrement ( UsageCount_t *uc ); +void UpdateUsageCountAdd ( UsageCount_t *uc, int add ); +static inline void UsageCountIncrement ( UsageCount_t *uc ) + { UpdateUsageCountAdd(uc,1); } + +// stat about last 'nsec' nanoseconds +UsageCountEntry_t GetUsageCount ( const UsageCount_t *uc, s_nsec_t nsec ); + +void AddUsageCountEntry ( UsageCountEntry_t *dest, const UsageCountEntry_t *add ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// wait counter /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[UsageDurationEntry_t]] + +typedef struct UsageDurationEntry_t +{ + u_nsec_t elapsed_nsec; // elapsed time, by GetTimerNSec() + u_nsec_t total_nsec; // total waiting time + u_nsec_t top_nsec; // top waiting time + u64 count; // number of events + u_sec_t time_sec; // time of record, by GetTimeSec(false) + float top_cnt_1s; // top number of counts in 1 sec + float top_cnt_5s; // top number of counts in 5 sec +} +__attribute__ ((packed)) UsageDurationEntry_t; + +//----------------------------------------------------------------------------- +// [[UsageDuration_t]] + +typedef struct UsageDuration_t +{ + UsageParam_t par; // base parameters + UsageDurationEntry_t ref; // reference with absolute values + #if TEST_USAGE_COUNT + UsageDurationEntry_t entry[USAGE_COUNT_ENTRIES+1]; // list of entries with delta values + #else + UsageDurationEntry_t entry[USAGE_COUNT_ENTRIES]; // list of entries with delta values + #endif +} +UsageDuration_t; + +//----------------------------------------------------------------------------- + +// n_rec<0: clear from end, n_rec>=0: clear from index +void ClearUsageDuration ( UsageDuration_t *ud, int count ); + +// NOW_NSEC: 0 or GetTimerNSec(), but not GetTime*() +void UpdateUsageDuration ( UsageDuration_t *ud, u_nsec_t now_nsec ); +void UpdateUsageDurationAdd ( UsageDuration_t *ud, int add, u_nsec_t wait_nsec, u_nsec_t top_nsec ); +void UpdateUsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ); +void UsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ); + +// stat about last 'nsec' nanoseconds +UsageDurationEntry_t GetUsageDuration ( const UsageDuration_t *ud, s_nsec_t nsec ); + +void AddUsageDurationEntry ( UsageDurationEntry_t *dest, const UsageDurationEntry_t *add ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// usage counter mgr /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ListUsageFunc]] + +typedef struct UsageCtrl_t UsageCtrl_t; + +enum +{ + LUC_PM_STANDARD, // standard view + LUC_PM_RAW, // raw values, not supported by ListUsageCount() + LUC_PM_SUMMARY, // summary + LUC_PM__USER // base value for user defined modes +}; + +typedef void (*ListUsageFunc) +( + PrintMode_t *pm, // valid print mode + const UsageCtrl_t *uci, // valid object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +); + +//----------------------------------------------------------------------------- + +typedef void (*UpdateUsageFunc) +( + const UsageCtrl_t *uci, // valid object to update + u_nsec_t now_usec // NULL or current time by GetTimerNSec() +); + +//----------------------------------------------------------------------------- +// [[UsageCtrl_t]] + +typedef struct UsageCtrl_t +{ + UsageParam_t *par; // higher prioritiy than uc->par and ud->par + UsageCount_t *uc; // not NULL for usage counters + UsageDuration_t *ud; // not NULL for wait counters + + UpdateUsageFunc update_func; // not NULL: Call this function to update + ListUsageFunc list_func; // not NULL: Call this function to list + + ccp title; // NULL or title + ccp key1; // first key, upper case + ccp key2; // NULL or second key, upper case + + ccp srt_load; // NULL or prefix vor SRT reading to allow prefix changes + ccp srt_prefix; // NULL prefix or SRT reading + writing + // If NULL: don't save/restore + // 'srt_load' is scanned before 'srt_prefix' + + float *color_val; // 6 values for mark,info,gint,warn,err,bad + // if NULL then use system list + u_nsec_t *color_dur; // 6 values for mark,info,gint,warn,err,bad + // if NULL then use system list +} +UsageCtrl_t; + +//----------------------------------------------------------------------------- + +extern PointerList_t usage_count_mgr; + +// NOW_NSEC: 0 or GetTimerNSec(), but not GetTime*() +u_nsec_t MaintainUsageByMgr ( u_nsec_t now_nsec ); +void UpdateByUCI ( const UsageCtrl_t *uci, u_nsec_t now_nsec ); +void UpdateUsageByMgr ( u_nsec_t now_nsec ); +void AddToUsageMgr ( const UsageCtrl_t *uci ); + +void ListUsageCount +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +); + +void ListUsageDuration +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +); + +static inline UsageParam_t * GetUsageParam ( const UsageCtrl_t *uci ) + { DASSERT(uci); return uci->par ? uci->par : uci->uc ? &uci->uc->par : uci->ud ? &uci->ud->par : 0; } + +static inline char GetUsageTypeChar ( const UsageCtrl_t *uci ) + { return !uci ? '-' : uci->uc ? 'C' : uci->ud ? 'D' : uci->par ? 'x' : '?'; } + +void SaveCurrentStateByUsageCountMgr ( FILE *f, uint fw_name ); +void RestoreStateByUsageCountMgr ( RestoreState_t *rs ); + +/////////////////////////////////////////////////////////////////////////////// +// [[IntervalInfo_t]] + +typedef struct IntervalInfo_t +{ + u_nsec_t nsec; + u_usec_t usec; + ccp name; +} +IntervalInfo_t; + +extern const IntervalInfo_t interval_info[]; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cpu usage /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if TEST_USAGE_COUNT + #define CPU_USAGE_ENTRIES 11 // number of entries +#else + #define CPU_USAGE_ENTRIES 30 // number of entries +#endif + +#if IS_DEVELOP + #define CPU_USAGE_ENABLED 0 // initial value for cpu_usage.par.enabled +#else + #define CPU_USAGE_ENABLED 0 // initial value for cpu_usage.par.enabled +#endif + +#define CPU_USAGE_FIRST_ADD 8 // first entry where values wil be added +#define CPU_USAGE_NEXT_MIN(v) (15*(v)/8) + +//----------------------------------------------------------------------------- +// [[CpuUsageEntry_t]] + +typedef struct CpuUsageEntry_t +{ + u_usec_t elapsed_usec; // elapsed time, by GetTimerUSec() + u_usec_t utime_usec; // user CPU time used + u_usec_t stime_usec; // system CPU time used + u_sec_t time_sec; // time of record, by GetTimeUSec(false) + float top1s; // = 100.0*(utime+stime)/elapsed for 1s + float top5s; // = 100.0*(utime+stime)/elapsed for 5s + u32 n_wait; // number of waits (poll() or select()) + float topw1s; // top number of waits in 1 sec + float topw5s; // top number of waits in 5 sec +} +__attribute__ ((packed)) CpuUsageEntry_t; + +//----------------------------------------------------------------------------- +// [[CpuUsage_t]] + +typedef struct CpuUsage_t +{ + UsageParam_t par; // base parameters + CpuUsageEntry_t ref; // reference with absolute values + #if TEST_USAGE_COUNT + CpuUsageEntry_t entry[CPU_USAGE_ENTRIES+1]; // list of entries with delta values + #else + CpuUsageEntry_t entry[CPU_USAGE_ENTRIES]; // list of entries with delta values + #endif +} +CpuUsage_t; + +//----------------------------------------------------------------------------- + +extern CpuUsage_t cpu_usage; +extern const UsageCtrl_t cpu_usage_ctrl; + +void UpdateCpuUsage(void); +void UpdateCpuUsageByUCI ( const UsageCtrl_t *, u_nsec_t ); // both params ignored +void UpdateCpuUsageIncrement(void); + +void ListCpuUsage +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // ignored! + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +); + +// stat about last 'usec' microseconds +CpuUsageEntry_t GetCpuUsage ( s_usec_t usec ); +uint GetCpuUsagePercent ( s_usec_t usec ); + +void AddCpuEntry ( CpuUsageEntry_t *dest, const CpuUsageEntry_t *add ); + +static inline uint GetCpuEntryPercent ( const CpuUsageEntry_t *entry ) +{ + DASSERT(entry); + return entry->elapsed_usec + ? 100 * ( entry->utime_usec + entry->stime_usec + entry->elapsed_usec/200 ) + / entry->elapsed_usec + : 0; +} + +static inline double GetCpuEntryRatio ( const CpuUsageEntry_t *entry ) +{ + DASSERT(entry); + return entry->elapsed_usec + ? (double)(entry->utime_usec + entry->stime_usec ) / entry->elapsed_usec + : 0.0; +} + +//----------------------------------------------------------------------------- + +// get percent, returns previuis valus if delay is <10ms +double get_rusage_percent (void); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// sockaddr support /////////////// /////////////////////////////////////////////////////////////////////////////// -// [[sockaddr_t]] [[sockaddr_in_t]] [[sockaddr_un_t]] [[sockaddr_info_t]] +// [[sockaddr_in46_t]] + +typedef struct sockaddr_in46_t +{ + union + { + sa_family_t family; // is first member of each sockaddr_* structure + sockaddr_in4_t in4; + sockaddr_in6_t in6; + }; +} +sockaddr_in46_t; + +//----------------------------------------------------------------------------- +// [[sockaddr_dclib_t]] + +typedef struct sockaddr_dclib_t +{ + union + { + sa_family_t family; // is first member of each sockaddr_* structure + sockaddr_in4_t in4; + sockaddr_in6_t in6; + sockaddr_un_t un; + }; +} +sockaddr_dclib_t; -typedef struct sockaddr sockaddr_t; -typedef struct sockaddr_in sockaddr_in_t; -typedef struct sockaddr_un sockaddr_un_t; +//----------------------------------------------------------------------------- +// [[sockaddr_info_t]] typedef struct sockaddr_info_t { @@ -4377,7 +5488,7 @@ typedef struct sockaddr_info_t } sockaddr_info_t; -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// // [[doxygen]] static inline sockaddr_info_t CreateSockaddrInfo ( cvp sa, uint size ) @@ -4402,24 +5513,95 @@ static inline void SetupSockaddrInfo ( sockaddr_info_t *sai, cvp sa, uint size ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// ScanIP4 /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ipv4_class_t]] + +typedef enum ipv4_class_t +{ + CIP4_ERROR = -1, // error + CIP4_INVALID = 0, // not an IPv4 + CIP4_NUMERIC, // 32 bit number only + CIP4_ABBREV, // abbreviated IPv4 (trailing point) + CIP4_CLASS_A, // IPv4 in class-A notation + CIP4_CLASS_B, // IPv4 in class-B notation + CIP4_CLASS_C, // IPv4 in class-C notation +} +ipv4_class_t; + +//----------------------------------------------------------------------------- + +ipv4_class_t ScanIP4 +( + ipv4_t *ret_ip4, // not NULL: return numeric IPv4 here + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +); + +//----------------------------------------------------------------------------- + +char * NormalizeIP4 +( + // Return a pointer to dest if valid IPv4, + // Otherwise return 0 and buf is not modifieid. + + char *buf, // result buffer, can be 'addr' + // if NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + bool c_notation, // TRUE: force lass-C notation (a.b.c.d) + + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +); + +//----------------------------------------------------------------------------- + +static inline ipv4_class_t CheckIP4 +( + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +) +{ + return ScanIP4(0,addr,addr_len); +} + +//----------------------------------------------------------------------------- + +int dclib_aton ( ccp addr, struct in_addr *inp ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// PrintIP4*() /////////////// /////////////////////////////////////////////////////////////////////////////// +#define FW_IPV4_N 10 // 4294967295 #define FW_IPV4_A 12 // 255.16777215 #define FW_IPV4_B 13 // 255.255.65535 #define FW_IPV4_C 15 // 255.255.255.255 #define FW_IPV4_PORT 5 // 65535 +#define FW_IPV4_N_PORT (FW_IPV4_N+1+FW_IPV4_PORT) #define FW_IPV4_A_PORT (FW_IPV4_A+1+FW_IPV4_PORT) #define FW_IPV4_B_PORT (FW_IPV4_B+1+FW_IPV4_PORT) #define FW_IPV4_C_PORT (FW_IPV4_C+1+FW_IPV4_PORT) //----------------------------------------------------------------------------- +char * PrintIP4N +( + // print as unsigned number (CIP4_NUMERIC) + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +); + char * PrintIP4A ( - // print in A-notation + // print in A-notation (CIP4_CLASS_A) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -4430,7 +5612,7 @@ char * PrintIP4A char * PrintIP4B ( - // print in A-notation + // print in B-notation (CIP4_CLASS_B) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -4480,6 +5662,20 @@ char * PrintAlignedIP4 ); //----------------------------------------------------------------------------- +// print by enum ipv4_class_t + +char * PrintIP4ByMode +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + ipv4_class_t mode, // CIP4_NUMERIC | CIP4_CLASS_A | CIP4_CLASS_B: + // force numeric or class-A/B output; otherwise class-C output + s32 port // 0..0xffff: add ':port' +); + +//----------------------------------------------------------------------------- struct sockaddr_in; static inline char * PrintIP4sa @@ -4491,9 +5687,10 @@ static inline char * PrintIP4sa const struct sockaddr_in *sa // socket address ) { - DASSERT(sa); - return PrintIP4(buf,buf_size, - ntohl(sa->sin_addr.s_addr), ntohs(sa->sin_port) ); + return sa + ? PrintIP4(buf,buf_size, + ntohl(sa->sin_addr.s_addr), ntohs(sa->sin_port) ) + : "-"; } static inline char * PrintRightIP4sa @@ -4790,7 +5987,8 @@ exmem_key_t * AppendEML ( exmem_list_t * eml, ccp key, CopyMode_t cm_key, const exmem_t *data, CopyMode_t cm_data ); // Return true if item found and removed -bool RemoveEML ( exmem_list_t * eml, ccp key ); +bool RemoveByIndexEML ( exmem_list_t * eml, int index ); +bool RemoveByKeyEML ( exmem_list_t * eml, ccp key ); // find first pattern that matches @@ -4808,6 +6006,55 @@ void AddStandardSymbolsEML ( exmem_list_t * eml, bool overwrite ); // /////////////////////////////////////////////////////////////////////////////// +/////////////// MultiColumn /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MultiColumn_t]] + +typedef struct MultiColumn_t +{ + FILE *f; // NULL or output file + const ColorSet_t + *colset; // NULL or color set for the frame + ccp prefix; // NULL or line prefix + int indent; // indention after prefix + ccp sep; // NULL or separator for frame_mode 0 and 1 + ccp eol; // NULL or end-of-line string + + int fw; // max field width + int min_rows; // minimal number of rows before MC + int max_cols; // >0: maximal number columns + + int frame_mode; // 0:ASCII, 1:single line, 2:double line + bool print_header; // TRUE: print column header + bool print_footer; // TRUE: print footer line + + ccp head_key; // NULL or name of key column, default="Name" + ccp head_val; // NULL or name of value column, default="Value" + + int debug; // >0: print basic debug info + // >1: be verbose + + exmem_list_t list; // list of parameters to print +} +MultiColumn_t; + +//----------------------------------------------------------------------------- + +void ResetMultiColumn ( MultiColumn_t *mc ); + +// return the number of printed lines including header and footer, or -1 on error +int PrintMultiColumn ( const MultiColumn_t *mc ); + +// Wrapper for short definition lines +// opt == 0 align right value +// opt >= 1 align left and try field width of # for value + +exmem_key_t * PutLineMC ( MultiColumn_t *mc, uint opt, ccp key, ccp value, CopyMode_t cm_value ); +exmem_key_t * PrintLineMC ( MultiColumn_t *mc, uint opt, ccp key, ccp format, ... ) + __attribute__ ((__format__(__printf__,4,5))); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// Memory Map /////////////// /////////////////////////////////////////////////////////////////////////////// // [[MemMapItem_t]] @@ -5122,6 +6369,7 @@ uint DropDataBuf /////////////////////////////////////////////////////////////////////////////// /////////////// ResizeElement_t, ResizeHelper_t /////////////// /////////////////////////////////////////////////////////////////////////////// +// [[ResizeElement_t]] typedef struct ResizeElement_t { @@ -5131,6 +6379,7 @@ typedef struct ResizeElement_t ResizeElement_t; //----------------------------------------------------------------------------- +// [[ResizeHelper_t]] typedef struct ResizeHelper_t { @@ -5393,6 +6642,11 @@ typedef enum SaveRestoreType_t SRT_DEF_ARRAY, // define/end an array of structs + SRT_MODE__BEGIN, //--- begin of modes + SRT_MODE_ASSIGN, // load option: assign numeric values (default) + SRT_MODE_ADD, // load option: add numeric values + SRT_MODE__END, //--- end of modes + SRT__IS_LIST, //----- from here: print lists; also used as separator SRT_STRING_FIELD, // var is StringField_t @@ -5411,10 +6665,18 @@ typedef struct SaveRestoreTab_t uint offset; // offset of variable uint size; // sizeof( var or string ) ccp name; // name in configuration file - s16 n_elem; // >0: is array with N elements - // <0: is array with -N elements, use last SRT_COUNT - u8 type; // SaveRestoreType_t - u8 emode; // EncodeMode_t + + union + { + struct + { + s16 n_elem; // >0: is array with N elements + // <0: is array with -N elements, use last SRT_COUNT + u8 type; // SaveRestoreType_t + u8 emode; // EncodeMode_t + }; + //struct SaveRestoreTab_t *ref; + }; } __attribute__ ((packed)) SaveRestoreTab_t; @@ -5424,7 +6686,8 @@ __attribute__ ((packed)) SaveRestoreTab_t; // #undef SRT_NAME // #define SRT_NAME name_of_the_struct -#define DEF_SRT_VAR(v,ne,n,t,e) {offsetof(SRT_NAME,v),sizeof(((SRT_NAME*)0)->v),n,ne,t,e} +#define DEF_SRT_VAR(v,ne,n,t,e) {offsetof(SRT_NAME,v),sizeof(((SRT_NAME*)0)->v),n, \ + .n_elem=ne,.type=t,.emode=e} #define DEF_SRT_BOOL(v,n) DEF_SRT_VAR(v,1,n,SRT_BOOL,0) #define DEF_SRT_UINT(v,n) DEF_SRT_VAR(v,1,n,SRT_UINT,0) @@ -5458,7 +6721,7 @@ __attribute__ ((packed)) SaveRestoreTab_t; #define DEF_SRT_ARRAY(v,n,t,e) \ {offsetof(SRT_NAME,v), sizeof(((SRT_NAME*)0)->v[0]), n, \ - sizeof(((SRT_NAME*)0)->v)/sizeof(*((SRT_NAME*)0)->v), t, e } + .n_elem=sizeof(((SRT_NAME*)0)->v)/sizeof(*((SRT_NAME*)0)->v), .type=t, .emode=e } #define DEF_SRT_BOOL_A(v,n) DEF_SRT_ARRAY(v,n,SRT_BOOL,0) #define DEF_SRT_UINT_A(v,n) DEF_SRT_ARRAY(v,n,SRT_UINT,0) @@ -5475,7 +6738,7 @@ __attribute__ ((packed)) SaveRestoreTab_t; #define DEF_SRT_ARRAY_C(v,n,t,e) \ {offsetof(SRT_NAME,v), sizeof(((SRT_NAME*)0)->v[0]), n, \ - -(s16)(sizeof(((SRT_NAME*)0)->v)/sizeof(*((SRT_NAME*)0)->v)), t, e } + .n_elem=-(s16)(sizeof(((SRT_NAME*)0)->v)/sizeof(*((SRT_NAME*)0)->v)), .type=t, .emode=e } #define DEF_SRT_BOOL_AC(v,n) DEF_SRT_ARRAY_C(v,n,SRT_BOOL,0) #define DEF_SRT_UINT_AC(v,n) DEF_SRT_ARRAY_C(v,n,SRT_UINT,0) @@ -5492,29 +6755,34 @@ __attribute__ ((packed)) SaveRestoreTab_t; #define DEF_SRT_ARRAY_FL(f,l) \ {offsetof(SRT_NAME,f), sizeof(((SRT_NAME*)0)->f), 0, \ - (offsetof(SRT_NAME,l)-offsetof(SRT_NAME,f))/sizeof(((SRT_NAME*)0)->f)+1, \ - SRT_DEF_ARRAY, 0 } + .n_elem=(offsetof(SRT_NAME,l)-offsetof(SRT_NAME,f))/sizeof(((SRT_NAME*)0)->f)+1, \ + .type=SRT_DEF_ARRAY } #define DEF_SRT_ARRAY_FLC(f,l) \ {offsetof(SRT_NAME,f), sizeof(((SRT_NAME*)0)->f), 0, \ - -(s16)((offsetof(SRT_NAME,l)-offsetof(SRT_NAME,f)) \ - /sizeof(((SRT_NAME*)0)->f)+1), SRT_DEF_ARRAY, 0 } + .n_elem=-(s16)((offsetof(SRT_NAME,l)-offsetof(SRT_NAME,f)) \ + /sizeof(((SRT_NAME*)0)->f)+1), .type=SRT_DEF_ARRAY } #define DEF_SRT_ARRAY_N(v,ne) DEF_SRT_VAR(v[0],ne,0,SRT_DEF_ARRAY,0) #define DEF_SRT_ARRAY_A(v) DEF_SRT_ARRAY(v,0,SRT_DEF_ARRAY,0) #define DEF_SRT_ARRAY_AC(v) DEF_SRT_ARRAY_C(v,0,SRT_DEF_ARRAY,0) -#define DEF_SRT_ARRAY_END() {0,0,0,0,SRT_DEF_ARRAY,0} +#define DEF_SRT_ARRAY_END() {0,0,0,.type=SRT_DEF_ARRAY} //--- global array of structs -#define DEF_SRT_ARRAY_GN(ne) {0,sizeof(SRT_NAME),0,ne,SRT_DEF_ARRAY,0} -#define DEF_SRT_ARRAY_GA(v) {0,sizeof(*v),0,sizeof(v)/sizeof(*v),SRT_DEF_ARRAY,0} +#define DEF_SRT_ARRAY_GN(ne) {0,sizeof(SRT_NAME),0,.n_elem=ne,.type=SRT_DEF_ARRAY} +#define DEF_SRT_ARRAY_GA(v) {0,sizeof(*v),0,.n_elem=sizeof(v)/sizeof(*v),.type=SRT_DEF_ARRAY} + +//--- modes + +#define DEF_SRT_MODE_ASSIGN() {0,0,0,.type=SRT_MODE_ASSIGN} +#define DEF_SRT_MODE_ADD() {0,0,0,.type=SRT_MODE_ADD} //--- special -#define DEF_SRT_SEPARATOR() {0,0,0,0,SRT__IS_LIST,0} -#define DEF_SRT_COMMENT(c) {0,0,c,0,SRT__IS_LIST,0} -#define DEF_SRT_TERM() {0,0,0,0,SRT__TERM,0} +#define DEF_SRT_SEPARATOR() {0,0,0,.type=SRT__IS_LIST} +#define DEF_SRT_COMMENT(c) {0,0,c,.type=SRT__IS_LIST} +#define DEF_SRT_TERM() {0,0,0,.type=SRT__TERM} /////////////////////////////////////////////////////////////////////////////// @@ -5549,6 +6817,13 @@ void RestoreStateByTable ccp prefix // NULL or prefix for names ); +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +extern const SaveRestoreTab_t SRT_UsageCount[]; +extern const SaveRestoreTab_t SRT_UsageDuration[]; +extern const SaveRestoreTab_t SRT_CpuUsage[]; + // /////////////////////////////////////////////////////////////////////////////// /////////////// GrowBuffer_t /////////////// @@ -5811,11 +7086,13 @@ extern int opt_new; // default 0 float double2float ( double d ); // reduce precision uint CreateUniqueIdN ( int range ); -static inline uint CreateUniqueId() { return CreateUniqueIdN(1); } -static inline uint CreateUniqueIdNBO() { return htonl(CreateUniqueIdN(1)); } +static inline uint CreateUniqueId(void) { return CreateUniqueIdN(1); } +static inline uint CreateUniqueIdNBO(void) { return htonl(CreateUniqueIdN(1)); } void Sha1Hex2Bin ( sha1_hash_t bin, ccp src, ccp end ); void Sha1Bin2Hex ( sha1_hex_t hex, cvp bin ); + +// return CircBuf() ccp GetSha1Hex ( cvp bin ); /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-cli.c b/project/dclib/dclib-cli.c index c4a3015..c4a5664 100644 --- a/project/dclib/dclib-cli.c +++ b/project/dclib/dclib-cli.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -41,248 +41,6 @@ // /////////////////////////////////////////////////////////////////////////////// -/////////////// arg_manager_t /////////////// -/////////////////////////////////////////////////////////////////////////////// - -void ResetArgManager ( arg_manager_t *am ) -{ - static char *null_list[] = {0}; - - if (am) - { - if (am->size) - { - uint i; - for ( i = 0; i < am->argc; i++ ) - FreeString(am->argv[i]); - if ( am->argv != null_list ) - FREE(am->argv); - } - memset(am,0,sizeof(*am)); - - am->argv = null_list; - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SetupArgManager ( arg_manager_t *am, int argc, char ** argv, bool clone ) -{ - DASSERT(am); - memset(am,0,sizeof(*am)); - if (clone) - CloneArgManager(am,argc,argv); - else - AttachArgManager(am,argc,argv); -} - -/////////////////////////////////////////////////////////////////////////////// - -void AttachArgManager ( arg_manager_t *am, int argc, char ** argv ) -{ - DASSERT(am); - ResetArgManager(am); - am->argc = argc; - am->argv = argv; -} - -/////////////////////////////////////////////////////////////////////////////// - -void CloneArgManager ( arg_manager_t *am, int argc, char ** argv ) -{ - DASSERT(am); - AttachArgManager(am,argc,argv); - PrepareEditArgManager(am,0); -} - -/////////////////////////////////////////////////////////////////////////////// - -void CopyArgManager ( arg_manager_t *dest, const arg_manager_t *src ) -{ - DASSERT(dest); - if (!src) - ResetArgManager(dest); - else if( dest != src ) - AttachArgManager(dest,src->argc,src->argv); -} - -/////////////////////////////////////////////////////////////////////////////// - -void MoveArgManager ( arg_manager_t *dest, arg_manager_t *src ) -{ - DASSERT(dest); - ResetArgManager(dest); - if (src) - { - dest->argv = src->argv; - dest->argc = src->argc; - dest->size = src->size; - memset(src,0,sizeof(*src)); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void PrepareEditArgManager ( arg_manager_t *am, int needed_space ) -{ - DASSERT(am); - - uint new_size = am->argc; - if ( needed_space > 0 ) - new_size += needed_space; - new_size = GetGoodAllocSize2(new_size+(new_size>>4)+10,sizeof(*am->argv)); - PRINT("PrepareEditArgManager(,%d) n=%d, size: %d -> %d\n", - needed_space, am->argc, am->size, new_size ); - - if (am->size) - am->argv = REALLOC(am->argv,sizeof(*am->argv)*new_size); - else - { - char **old = am->argv; - am->argv = MALLOC(sizeof(*am->argv)*new_size); - - uint i; - for ( i = 0; i < am->argc; i++ ) - am->argv[i] = old[i] ? STRDUP(old[i]) : 0; - } - am->size = new_size - 1; - DASSERT( am->argc <= am->size ); - am->argv[am->argc] = 0; -} - -/////////////////////////////////////////////////////////////////////////////// - -uint AppendArgManager ( arg_manager_t *am, ccp arg1, ccp arg2, bool move_arg ) -{ - DASSERT(am); - PRINT("AppendArgManager(,%s,%s,%d)\n",arg1,arg2,move_arg); - return InsertArgManager(am,am->argc,arg1,arg2,move_arg); -} - -/////////////////////////////////////////////////////////////////////////////// - -uint InsertArgManager - ( arg_manager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ) -{ - PRINT("InsertArgManager(,%d,%s,%s,%d)\n",pos,arg1,arg2,move_arg); - - pos = CheckIndex1(am->argc,pos); - const int n = (arg1!=0) + (arg2!=0); - if (n) - { - PrepareEditArgManager(am,n); - DASSERT( pos>0 && pos <= am->argc ); - char **dest = am->argv + pos; - if ( pos < am->argc ) - memmove( dest+n, dest, (u8*)(am->argv+am->argc) - (u8*)dest ); - if (arg1) - *dest++ = move_arg ? (char*)arg1 : STRDUP(arg1); - if (arg2) - *dest++ = move_arg ? (char*)arg2 : STRDUP(arg2); - am->argc += n; - am->argv[am->argc] = 0; - } - return pos+n; -} - -/////////////////////////////////////////////////////////////////////////////// - -uint ReplaceArgManager - ( arg_manager_t *am, int pos, ccp arg1, ccp arg2, bool move_arg ) -{ - PRINT("ReplaceArgManager(,%d,%s,%s,%d)\n",pos,arg1,arg2,move_arg); - - pos = CheckIndex1(am->argc,pos); - const int n = (arg1!=0) + (arg2!=0); - if (n) - { - PrepareEditArgManager(am,n); - DASSERT( pos>0 && pos <= am->argc ); - char **dest = am->argv + pos; - if (arg1) - { - if ( pos++ < am->argc ) - FreeString(*dest); - *dest++ = move_arg ? (char*)arg1 : STRDUP(arg1); - } - if (arg2) - { - if ( pos++ < am->argc ) - FreeString(*dest); - *dest++ = move_arg ? (char*)arg2 : STRDUP(arg2); - } - if ( pos > am->argc ) - { - am->argc = pos; - am->argv[am->argc] = 0; - } - } - return pos; -} - -/////////////////////////////////////////////////////////////////////////////// - -uint RemoveArgManager ( arg_manager_t *am, int pos, int count ) -{ - DASSERT(am); - - #if HAVE_PRINT - const int oldpos = pos; - #endif - const int n = CheckIndexC(am->argc,&pos,count); - PRINT("RemoveArgManager(,%d,%d) : remove n=%d: %d..%d\n", - oldpos, count, n, pos, pos+n ); - - DASSERT( pos >= 0 && pos <= am->argc ); - DASSERT( n >= 0 && pos+n <= am->argc ); - - if ( n > 0 ) - { - PrepareEditArgManager(am,0); - char **dest = am->argv + pos; - - uint i; - for ( i = 0; i <n; i++ ) - FreeString(dest[i]); - am->argc -= n; - memmove( dest, dest+n, (u8*)(am->argv+am->argc) - (u8*)dest ); - am->argv[am->argc] = 0; - } - return pos; -} - -/////////////////////////////////////////////////////////////////////////////// - -void DumpArgManager ( FILE *f, int indent, const arg_manager_t *am, ccp title ) -{ - if ( !f || !am ) - return; - - indent = NormalizeIndent(indent); - if (title) - fprintf(f,"%*s" "ArgManager[%s]: N=%d", - indent,"", title, am->argc ); - else - fprintf(f,"%*s" "ArgManager: N=%d", indent,"", am->argc ); - - if (am->size) - fprintf(f,"/%u\n",am->size); - else - fputc('\n',f); - - if (am->argc) - { - char buf[10]; - int fw = snprintf(buf,sizeof(buf),"%u",am->argc-1)+2; - - uint i; - for ( i = 0; i < am->argc; i++ ) - fprintf(f,"%*s%*u: |%s|\n", indent,"", fw,i, am->argv[i] ); - } -} - -// -/////////////////////////////////////////////////////////////////////////////// /////////////// global commands /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-color.c b/project/dclib/dclib-color.c index ef0ca01..eaff47a 100644 --- a/project/dclib/dclib-color.c +++ b/project/dclib/dclib-color.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -380,7 +380,7 @@ uint PutColoredLines int cont_indent = -1; uint n_lines = 0, n_space = 0; uint column = 0; - //ccp col_begin = ""; + ccp next_col = 0; enum { GB_NONE, GB_SIGN, GB_COMMA, GB_SPACE } gb_mode = GB_NONE; @@ -476,7 +476,8 @@ uint PutColoredLines if (*col) { - cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),col); + //cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),col); + next_col = col; if ( *text == '}' ) cur.col_active = col; else @@ -602,6 +603,12 @@ uint PutColoredLines while ( n_space > 0 ) *cur.dest++ = ' ', n_space--; + if (next_col) + { + cur.dest = StringCopyE(cur.dest,buf+sizeof(buf),next_col); + next_col = 0; + } + cur.dest = PrintUTF8Char(cur.dest,code); if ( cur.dest > buf_end ) goto new_line; // should never happen! @@ -821,7 +828,8 @@ static void PrintColorInfo ( FILE *f, color_info_t *ci ) enumError Command_TESTCOLORS ( int argc, char ** argv ) { #if HAVE_PRINT - GetColorByName(colout,"blue"); + // to force an order-check + GetColorOffsetByName("blue"); #endif printf("TEST COLORS: %u arguments:\n",argc); @@ -1093,7 +1101,6 @@ term_size_t GetTermSize ( int default_width, int default_height ) else { ts = GetTermSizeFD(STDOUT_FILENO,default_width,default_height); - if ( opt_width > 0 ) ts.width = opt_width; else if ( opt_max_width > 0 && ts.width > opt_max_width ) @@ -1253,6 +1260,45 @@ void EnableAutoTermSize() /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +good_term_width_t good_term_width = +{ + .min_width = 40, + .def_width = 100, + .max_width = 120, + .good_width = 120 +}; + +/////////////////////////////////////////////////////////////////////////////// + +int GetGoodTermWidth ( good_term_width_t *gtw, bool force ) +{ + if (!gtw) + gtw = &good_term_width; + + if ( force || !gtw->setup_done ) + { + gtw->setup_done = true; + int good_width = opt_width; + if (!good_width) + { + good_width = GetTermWidth(gtw->def_width,gtw->min_width); + const int max = opt_max_width ? opt_max_width : gtw->max_width; + if ( good_width > max ) + good_width = max; + } + + if ( good_width < gtw->min_width ) + good_width = gtw->min_width; + + gtw->good_width = good_width; + } + return gtw->good_width; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + ccp TermCursorUp = TERM_CURSOR_UP; // move cursor up ccp TermCursorDown = TERM_CURSOR_DOWN; // move cursor down ccp TermCursorRight = TERM_CURSOR_RIGHT; // move cursor right @@ -1275,6 +1321,7 @@ ccp TermSetWrap = TERM_SET_WRAP; // enabled line wrapping ccp TermResetWrap = TERM_RESET_WRAP; // disable line wrapping ccp TermTextModeBeg = TERM_TEXT_MODE_BEG; // begin of text mode sequence +ccp TermTextModeBeg0 = TERM_TEXT_MODE_BEG; // begin of text mode sequence with reset ccp TermTextModeEnd = TERM_TEXT_MODE_END; // end of text mode sequence ccp TermTextModeReset = TERM_TEXT_MODE_RESET; // reset text mode @@ -1382,7 +1429,7 @@ const int Color18Index[18+1] = // 18 elements + -1 as terminator static char * GetColorCircBuf ( uint len ) { - static char circ_buf[1024]; + static char circ_buf[2048]; static char *circ_ptr = circ_buf; DASSERT( circ_ptr >= circ_buf && circ_ptr <= circ_buf + sizeof(circ_buf) ); @@ -2622,7 +2669,7 @@ ccp GetTextMode //--- output char buf[100], *end = buf + sizeof(buf); - char *dest = StringCopyE(buf,end,TermTextModeBeg); + char *dest = StringCopyE(buf,end,TermTextModeBeg0); const uint bg = (uint)bg_index < TCI__IGNORE ? color_index[bg_index] : ~0; @@ -3028,7 +3075,7 @@ ccp GetTextMode //--- output char buf[100], *end = buf + sizeof(buf); - char *dest = StringCopyE(buf,end,TermTextModeBeg); + char *dest = StringCopyE(buf,end,TermTextModeBeg0); ccp sep = ""; if ( (uint)font_index < TCI__IGNORE ) @@ -3063,6 +3110,129 @@ ccp GetTextMode // /////////////////////////////////////////////////////////////////////////////// +/////////////// color ids /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[color_helper_tab_t]] + +const TermColorId_t color8id = { .col = +{ + 0x00, // TCE_GREY0, + 0x00, // TCE_GREY1, + 0x10, // TCE_GREY2, + 0x10, // TCE_GREY3, + 0x10, // TCE_GREY4, + 0x07, // TCE_GREY5, + 0x07, // TCE_GREY6, + 0x07, // TCE_GREY7, + 0x17, // TCE_GREY8, + 0x17, // TCE_GREY9, + + 0x01, // TCE_RED_MAGENTA, + 0x01, // TCE_RED, + 0x01, // TCE_RED_ORANGE, + 0x01, // TCE_ORANGE_RED, + 0x01, // TCE_ORANGE, + 0x03, // TCE_ORANGE_YELLOW, + 0x03, // TCE_YELLOW_ORANGE, + 0x03, // TCE_YELLOW, + 0x03, // TCE_YELLOW_GREEN, + 0x02, // TCE_GREEN_YELLOW, + 0x02, // TCE_GREEN, + 0x02, // TCE_GREEN_CYAN, + 0x06, // TCE_CYAN_GREEN, + 0x06, // TCE_CYAN, + 0x06, // TCE_CYAN_BLUE, + 0x04, // TCE_BLUE_CYAN, + 0x04, // TCE_BLUE, + 0x04, // TCE_BLUE_MAGENTA, + 0x05, // TCE_MAGENTA_BLUE, + 0x05, // TCE_MAGENTA, + 0x05, // TCE_MAGENTA_RED, + + 0x11, // TCE_B_RED_MAGENTA, + 0x11, // TCE_B_RED, + 0x11, // TCE_B_RED_ORANGE, + 0x11, // TCE_B_ORANGE_RED, + 0x11, // TCE_B_ORANGE, + 0x13, // TCE_B_ORANGE_YELLOW, + 0x13, // TCE_B_YELLOW_ORANGE, + 0x13, // TCE_B_YELLOW, + 0x13, // TCE_B_YELLOW_GREEN, + 0x12, // TCE_B_GREEN_YELLOW, + 0x12, // TCE_B_GREEN, + 0x12, // TCE_B_GREEN_CYAN, + 0x16, // TCE_B_CYAN_GREEN, + 0x16, // TCE_B_CYAN, + 0x16, // TCE_B_CYAN_BLUE, + 0x14, // TCE_B_BLUE_CYAN, + 0x14, // TCE_B_BLUE, + 0x14, // TCE_B_BLUE_MAGENTA, + 0x15, // TCE_B_MAGENTA_BLUE, + 0x15, // TCE_B_MAGENTA, + 0x15, // TCE_B_MAGENTA_RED, +}}; + +// values for 38;5;x or 48;5;x +const TermColorId_t color256id = { .col = +{ + 232, // TCE_GREY0, + 235, // TCE_GREY1, + 237, // TCE_GREY2, + 240, // TCE_GREY3, + 242, // TCE_GREY4, + 245, // TCE_GREY5, + 247, // TCE_GREY6, + 250, // TCE_GREY7, + 252, // TCE_GREY8, + 255, // TCE_GREY9, + + 161, // TCE_RED_MAGENTA, + 160, // TCE_RED, + 166, // TCE_RED_ORANGE, + 166, // TCE_ORANGE_RED, + 172, // TCE_ORANGE, + 136, // TCE_ORANGE_YELLOW, + 136, // TCE_YELLOW_ORANGE, + 142, // TCE_YELLOW, + 106, // TCE_YELLOW_GREEN, + 70, // TCE_GREEN_YELLOW, + 34, // TCE_GREEN, + 35, // TCE_GREEN_CYAN, + 36, // TCE_CYAN_GREEN, + 37, // TCE_CYAN, + 32, // TCE_CYAN_BLUE, + 27, // TCE_BLUE_CYAN, + 21, // TCE_BLUE, + 92, // TCE_BLUE_MAGENTA, + 128, // TCE_MAGENTA_BLUE, + 164, // TCE_MAGENTA, + 162, // TCE_MAGENTA_RED, + + 198, // TCE_B_RED_MAGENTA, + 196, // TCE_B_RED, + 202, // TCE_B_RED_ORANGE, + 202, // TCE_B_ORANGE_RED, + 208, // TCE_B_ORANGE, + 214, // TCE_B_ORANGE_YELLOW, + 220, // TCE_B_YELLOW_ORANGE, + 226, // TCE_B_YELLOW, + 154, // TCE_B_YELLOW_GREEN, + 118, // TCE_B_GREEN_YELLOW, + 46, // TCE_B_GREEN, + 49, // TCE_B_GREEN_CYAN, + 50, // TCE_B_CYAN_GREEN, + 51, // TCE_B_CYAN, + 75, // TCE_B_CYAN_BLUE, + 111, // TCE_B_BLUE_CYAN, + 105, // TCE_B_BLUE, + 141, // TCE_B_BLUE_MAGENTA, + 177, // TCE_B_MAGENTA_BLUE, + 207, // TCE_B_MAGENTA, + 205, // TCE_B_MAGENTA_RED, +}}; + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// color tables /////////////// /////////////////////////////////////////////////////////////////////////////// // [[color_helper_tab_t]] @@ -3120,6 +3290,8 @@ static const color_helper_tab_t ch_tab_color[] = static const color_helper_tab_t ch_tab_bcolor[] = { + // [[new-color]] + SEP1 JOB1(b_red_magenta) JOB1(b_red) @@ -3150,6 +3322,8 @@ _Static_assert( N_COLORSET_GREY == 10, "wrong N_COLORSET_GREY" ); static const color_helper_tab_t ch_tab_grey[] = { + // [[new-color]] + SEP1 JOB1(black) JOB1(b_black) @@ -3173,8 +3347,10 @@ static const color_helper_tab_t ch_tab_grey[] = //----------------------------------------------------------------------------- -static const color_helper_tab_t ch_tab_name[] = +static const color_helper_tab_t ch_tab_name1[] = { + // [[new-color]] + SEP1 JOB1(setup) @@ -3186,12 +3362,15 @@ static const color_helper_tab_t ch_tab_name[] = JOB1(close) JOB1(file) JOB1(job) + JOB1(debug) + JOB1(log) + JOB1(mark) JOB1(info) JOB1(hint) JOB1(warn) - JOB1(debug) - JOB1(log) + JOB1(err) + JOB1(bad) JOB1(name) JOB1(op) @@ -3202,14 +3381,119 @@ static const color_helper_tab_t ch_tab_name[] = JOB1(fail) JOB1(fail2) JOB1(fatal) + + JOB1(select) + JOB1(differ) + JOB1(stat_line) + JOB1(warn_line) + JOB1(proc_line) + + JOB0 +}; + +//----------------------------------------------------------------------------- +_Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); +_Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); + +static const color_helper_tab_t ch_tab_name2[] = +{ + // [[new-color]] + + SEP1 + + JOB2(hl_line0,hl_line[0],0) + JOB2(hl_line1,hl_line[1],0) + JOB2(hl_line2,hl_line[2],0) + JOB2(hl_line3,hl_line[3],0) + JOB2(order0,order[0],0) + JOB2(order1,order[1],0) + JOB2(order2,order[2],0) + JOB2(order3,order[3],0) + JOB2(order4,order[4],0) + JOB2(order5,order[5],0) + JOB2(order6,order[6],0) + JOB2(order7,order[7],0) + JOB2(order8,order[8],0) + JOB1(cite) + JOB1(status) + JOB1(highlight) + JOB2(hl,highlight,1) + JOB1(hide) + + JOB1(heading) + JOB2(head,heading,1) + JOB1(caption) + JOB1(section) + JOB1(syntax) + JOB1(cmd) + JOB1(option) + JOB2(opt,option,1) + JOB1(param) + JOB2(par,param,1) + + JOB1(off) + JOB1(on) + + JOB0 +}; + +//----------------------------------------------------------------------------- +_Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); +_Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); + +static const color_helper_tab_t ch_tab_name[] = +{ + // [[new-color]] + + SEP1 + + JOB1(setup) + JOB1(run) + JOB1(abort) + JOB1(finish) + JOB1(script) + JOB1(open) + JOB1(close) + JOB1(file) + JOB1(job) + JOB1(debug) + JOB1(log) + JOB1(mark) + JOB1(info) + JOB1(hint) + JOB1(warn) + JOB1(err) JOB1(bad) + JOB1(name) + JOB1(op) + JOB1(value) + JOB2(val,value,1) + JOB1(success) + JOB1(error) + JOB1(fail) + JOB1(fail2) + JOB1(fatal) + JOB1(select) JOB1(differ) JOB1(stat_line) JOB1(warn_line) JOB1(proc_line) + JOB2(hl_line0,hl_line[0],0) + JOB2(hl_line1,hl_line[1],0) + JOB2(hl_line2,hl_line[2],0) + JOB2(hl_line3,hl_line[3],0) + JOB2(order0,order[0],0) + JOB2(order1,order[1],0) + JOB2(order2,order[2],0) + JOB2(order3,order[3],0) + JOB2(order4,order[4],0) + JOB2(order5,order[5],0) + JOB2(order6,order[6],0) + JOB2(order7,order[7],0) + JOB2(order8,order[8],0) JOB1(cite) JOB1(status) JOB1(highlight) @@ -3293,11 +3577,14 @@ const ColorSet_t * GetColorSet8() col.close = STRDUP(GetTextMode(1,TTM_COL_CLOSE)); col.file = STRDUP(GetTextMode(1,TTM_COL_FILE)); col.job = STRDUP(GetTextMode(1,TTM_COL_JOB)); + col.debug = STRDUP(GetTextMode(1,TTM_COL_DEBUG)); + col.log = STRDUP(GetTextMode(1,TTM_COL_LOG)); + col.mark = STRDUP(GetTextMode(1,TTM_COL_MARK)); col.info = STRDUP(GetTextMode(1,TTM_COL_INFO)); col.hint = STRDUP(GetTextMode(1,TTM_COL_HINT)); col.warn = STRDUP(GetTextMode(1,TTM_COL_WARN)); - col.debug = STRDUP(GetTextMode(1,TTM_COL_DEBUG)); - col.log = STRDUP(GetTextMode(1,TTM_COL_LOG)); + col.err = STRDUP(GetTextMode(1,TTM_COL_ERR)); + col.bad = STRDUP(GetTextMode(1,TTM_COL_BAD)); col.name = STRDUP(GetTextMode(1,TTM_COL_NAME)); col.op = STRDUP(GetTextMode(1,TTM_COL_OP)); col.value = STRDUP(GetTextMode(1,TTM_COL_VALUE)); @@ -3306,8 +3593,6 @@ const ColorSet_t * GetColorSet8() col.fail = STRDUP(GetTextMode(1,TTM_COL_FAIL)); col.fail2 = STRDUP(GetTextMode(1,TTM_COL_FAIL2)); col.fatal = STRDUP(GetTextMode(1,TTM_COL_FATAL)); - col.mark = STRDUP(GetTextMode(1,TTM_COL_MARK)); - col.bad = STRDUP(GetTextMode(1,TTM_COL_BAD)); col.select = STRDUP(GetTextMode(1,TTM_COL_SELECT)); col.differ = STRDUP(GetTextMode(1,TTM_COL_DIFFER)); col.stat_line = STRDUP(GetTextMode(1,TTM_COL_STAT_LINE)); @@ -3327,6 +3612,25 @@ const ColorSet_t * GetColorSet8() col.on = STRDUP(GetTextMode(1,TTM_COL_ON)); col.off = STRDUP(GetTextMode(1,TTM_COL_OFF)); + _Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); + + col.hl_line[0] = STRDUP(GetTextMode(1,TTM_COL_HL_LINE0)); + col.hl_line[1] = STRDUP(GetTextMode(1,TTM_COL_HL_LINE1)); + col.hl_line[2] = STRDUP(GetTextMode(1,TTM_COL_HL_LINE2)); + col.hl_line[3] = STRDUP(GetTextMode(1,TTM_COL_HL_LINE3)); + + _Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); + + col.order[0] = STRDUP(GetTextMode(1,TTM_COL_ORDER0)); + col.order[1] = STRDUP(GetTextMode(1,TTM_COL_ORDER1)); + col.order[2] = STRDUP(GetTextMode(1,TTM_COL_ORDER2)); + col.order[3] = STRDUP(GetTextMode(1,TTM_COL_ORDER3)); + col.order[4] = STRDUP(GetTextMode(1,TTM_COL_ORDER4)); + col.order[5] = STRDUP(GetTextMode(1,TTM_COL_ORDER5)); + col.order[6] = STRDUP(GetTextMode(1,TTM_COL_ORDER6)); + col.order[7] = STRDUP(GetTextMode(1,TTM_COL_ORDER7)); + col.order[8] = STRDUP(GetTextMode(1,TTM_COL_ORDER8)); + _Static_assert( N_COLORSET_GREY == 10, "wrong N_COLORSET_GREY" ); col.grey[0] = @@ -3348,8 +3652,8 @@ const ColorSet_t * GetColorSet8() col.red = col.red_orange = col.orange_red = - col.orange = - col.orange_yellow = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_RED)); + col.orange = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_RED)); + col.orange_yellow = col.yellow_orange = col.yellow = col.yellow_green = STRDUP(GetTextMode(1,TTM_NO_BOLD|TTM_YELLOW)); @@ -3370,8 +3674,8 @@ const ColorSet_t * GetColorSet8() col.b_red = col.b_red_orange = col.b_orange_red = - col.b_orange = - col.b_orange_yellow = STRDUP(GetTextMode(1,TTM_BOLD|TTM_RED)); + col.b_orange = STRDUP(GetTextMode(1,TTM_BOLD|TTM_RED)); + col.b_orange_yellow = col.b_yellow_orange = col.b_yellow = col.b_yellow_green = STRDUP(GetTextMode(1,TTM_BOLD|TTM_YELLOW)); @@ -3416,33 +3720,55 @@ const ColorSet_t * GetColorSet256() //--- special settings - col.setup = "\e[38;5;86;40m"; - col.run = "\e[38;5;229;40m"; - col.abort = "\e[38;5;198;40m"; - col.finish = "\e[38;5;154;40m"; - col.script = "\e[0;30;48;5;219m"; - col.open = "\e[38;5;225;40m"; - col.close = "\e[38;5;219;40m"; - col.heading = "\e[38;5;111;40m"; - col.info = "\e[38;5;49;40m"; - col.hint = "\e[38;5;220;40m"; - col.warn = "\e[38;5;208;40m"; - col.debug = "\e[38;5;210;40m"; - col.error = "\e[38;5;255;48;5;130m"; - col.fail = "\e[38;5;251;48;5;88m"; - col.fail2 = "\e[38;5;255;48;5;88m"; - col.fatal = "\e[38;5;255;48;5;129m"; - col.differ = "\e[38;5;226;48;5;19m"; - col.stat_line = "\e[38;5;231;48;5;19m"; - col.warn_line = "\e[38;5;215;48;5;19m"; - col.hide = "\e[38;5;245;40m"; + col.setup = "\e[0;38;5;86m"; + col.run = "\e[0;38;5;229m"; + col.abort = "\e[0;38;5;198m"; + col.finish = "\e[0;38;5;154m"; + col.script = "\e[0;0;30;48;5;219m"; + col.open = "\e[0;38;5;225m"; + col.close = "\e[0;38;5;219m"; + col.file = "\e[0;38;5;223m"; + col.heading = "\e[0;38;5;111m"; + col.cmd = "\e[0;38;5;123m"; + col.info = "\e[0;38;5;49m"; + col.hint = "\e[0;38;5;220m"; + col.warn = "\e[0;38;5;208m"; + col.err = "\e[0;38;5;198m"; + col.debug = "\e[0;38;5;210m"; + col.error = "\e[0;38;5;255;48;5;130m"; + col.fail = "\e[0;38;5;251;48;5;88m"; + col.fail2 = "\e[0;38;5;255;48;5;88m"; + col.fatal = "\e[0;38;5;255;48;5;129m"; + col.differ = "\e[0;38;5;226;48;5;18m"; + col.stat_line = "\e[0;38;5;231;48;5;18m"; + col.warn_line = "\e[0;38;5;214;48;5;18m"; + col.hide = "\e[0;38;5;245m"; + + _Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); + + col.hl_line[0] = "\e[0;38;5;145m"; + col.hl_line[1] = "\e[0;38;5;159m"; + col.hl_line[2] = "\e[0;38;5;157m"; + col.hl_line[3] = "\e[0;38;5;217m"; + + _Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); + + //col.order[0] = + col.order[1] = "\e[0;38;5;39m"; + //col.order[2] = + //col.order[3] = + //col.order[4] = + col.order[5] = "\e[0;38;5;214m"; + col.order[6] = "\e[0;38;5;197m"; + col.order[7] = "\e[0;38;5;213m"; + col.order[8] = "\e[0;38;5;141m"; //--- special settings: color names // dark: // ./mkw-ana testcol 400-420 420-330 330-030 030-033 033-005 005-404 404-400 - // ./mkw-ana testcol 400 410 420 320 330 230 030 032 033 024 005 204 404 402 400 + // ./mkw-ana testcol 401 400 410 420 320 330 230 130 030 031 032 033 024 015 005 204 304 404 402 401 col.red_magenta = "\e[38;5;161m"; col.red = "\e[38;5;160m"; @@ -3468,7 +3794,7 @@ const ColorSet_t * GetColorSet256() // bold: // ./mkw-ana testcol 500-520 520-550 550-050 050-055 055-225 225-515 515-500 - // ./mkw-ana testcol 500 510 520 530 550 350 050 053 055 135 225 425 515 512 500 + // ./mkw-ana testcol 502 500 510 520 530 540 550 350 250 050 053 054 055 135 235 225 325 425 515 513 502 col.b_red_magenta = "\e[38;5;198m"; col.b_red = "\e[38;5;196m"; @@ -3494,15 +3820,15 @@ const ColorSet_t * GetColorSet256() _Static_assert( N_COLORSET_GREY == 10, "wrong N_COLORSET_GREY" ); col.grey[0] = "\e[38;5;232m"; - col.grey[1] = "\e[38;5;235m"; //x + col.grey[1] = "\e[38;5;235m"; col.grey[2] = "\e[38;5;237m"; - col.grey[3] = "\e[38;5;240m"; //x + col.grey[3] = "\e[38;5;240m"; col.grey[4] = "\e[38;5;242m"; - col.grey[5] = "\e[38;5;245m"; //x + col.grey[5] = "\e[38;5;245m"; col.grey[6] = "\e[38;5;247m"; - col.grey[7] = "\e[38;5;250m"; //x + col.grey[7] = "\e[38;5;250m"; col.grey[8] = "\e[38;5;252m"; - col.grey[9] = "\e[38;5;255m"; //x + col.grey[9] = "\e[38;5;255m"; TermColorIndex_t font, bg; for ( font = 0; font < TCI__N_FONT; font++ ) @@ -3512,22 +3838,6 @@ const ColorSet_t * GetColorSet256() } return &col; - -//---------- old definitions ---------- -// -// col.differ = STRDUP(GetColorMode(COLMD_256_COLORS, -// TCI_YELLOW, TCI_BLUE, GCM_ALT|GCM_SHORT )); -// col.stat_line = STRDUP(GetColorMode(COLMD_256_COLORS, -// TCI_WHITE, TCI_BLUE, GCM_ALT|GCM_SHORT )); -// col.warn_line = STRDUP(GetColorMode(COLMD_256_COLORS, -// TCI_ORANGE, TCI_BLUE, GCM_ALT|GCM_SHORT )); -// -// col.orange = STRDUP(GetColorMode(COLMD_256_COLORS, -// TCI_ORANGE, TCI__IGNORE, GCM_ALT|GCM_SHORT )); -// col.b_orange = STRDUP(GetColorMode(COLMD_256_COLORS, -// TCI_B_ORANGE, TCI__IGNORE, GCM_ALT|GCM_SHORT )); -// -//------------------------------------- } /////////////////////////////////////////////////////////////////////////////// @@ -3629,7 +3939,7 @@ bool SetupColorSet ( ColorSet_t *cs, FILE *f ) /////////////////////////////////////////////////////////////////////////////// -ccp GetColorByName ( const ColorSet_t *colset, ccp name ) +int GetColorOffsetByName ( ccp name ) { struct cdef_t { @@ -3642,9 +3952,14 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) #define DEF1(n) { offsetof(ColorSet_t,n), #n }, #define DEF2(t,n) { offsetof(ColorSet_t,n), #t }, + _Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); + _Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); + _Static_assert( N_COLORSET_GREY == 10, "wrong N_COLORSET_GREY" ); + static const struct cdef_t tab[] = { - // [[new-color]] + // [[new-color]] order by name + DEF1(abort) DEF1(b_black) DEF1(b_blue) @@ -3683,6 +3998,7 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) DEF1(cyan_green) DEF1(debug) DEF1(differ) + DEF1(err) DEF1(error) DEF1(fail) DEF1(fail2) @@ -3718,6 +4034,10 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) DEF1(highlight) DEF1(hint) DEF2(hl,highlight) + DEF2(hl_line0,hl_line[0]) + DEF2(hl_line1,hl_line[1]) + DEF2(hl_line2,hl_line[2]) + DEF2(hl_line3,hl_line[3]) DEF1(info) DEF1(job) DEF1(log) @@ -3735,6 +4055,15 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) DEF1(orange) DEF1(orange_red) DEF1(orange_yellow) + DEF2(order0,order[0]) + DEF2(order1,order[1]) + DEF2(order2,order[2]) + DEF2(order3,order[3]) + DEF2(order4,order[4]) + DEF2(order5,order[5]) + DEF2(order6,order[6]) + DEF2(order7,order[7]) + DEF2(order8,order[8]) DEF2(par,param) DEF1(param) DEF1(proc_line) @@ -3776,7 +4105,7 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) if ( i > 0 && strcmp(cd[-1].name,cd->name) >= 0 ) { err_count++; - PRINT("WRONG COL ORDER: %s > %s\n", cd[-1].name, cd->name); + PRINT("\e[1;35mWRONG COL ORDER: %s > %s \e[0m\n", cd[-1].name, cd->name); } } @@ -3791,7 +4120,7 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) //--- search the name - if ( colset && name ) + if (name) { int beg = 0; int end = sizeof(tab)/sizeof(*tab) - 1; @@ -3804,13 +4133,73 @@ ccp GetColorByName ( const ColorSet_t *colset, ccp name ) else if ( stat > 0 ) beg = idx + 1; else - return *(ccp*)((u8*)colset+tab[idx].delta); + return tab[idx].delta; } } + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetColorByName ( const ColorSet_t *colset, ccp name ) +{ + if ( colset && name ) + { + const int offset = GetColorOffsetByName(name); + if ( offset > 0 ) + return GetColorByValidOffset(colset,offset); + } return EmptyString; } /////////////////////////////////////////////////////////////////////////////// + +ccp GetColorByOffsetOrName ( const ColorSet_t *colset, int * offset, ccp name ) +{ + if (!offset) + return GetColorByName(colset,name); + + if (!*offset) + *offset = GetColorOffsetByName(name); + return *offset > 0 ? GetColorByValidOffset(colset,*offset) : EmptyString; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetLineColor ( const ColorSet_t *colset ) +{ + ColorSet_t *cs = (ColorSet_t*)colset; + cs->line_begin = + cs->line_end = EmptyString; + cs->line_reset = cs->reset; +} + +//----------------------------------------------------------------------------- + +void SetLineColor ( const ColorSet_t *colset, int level ) +{ + _Static_assert( N_COLORSET_HL_LINE == 4, "wrong N_COLORSET_HL_LINE" ); + + ColorSet_t *cs = (ColorSet_t*)colset; + if ( level <= 0 ) + { + cs->line_begin = EmptyString; + cs->line_reset = cs->reset; + cs->line_end = EmptyString; + } + else + { + cs->line_begin = + cs->line_reset = cs->hl_line[ level < N_COLORSET_HL_LINE ? level : N_COLORSET_HL_LINE-1 ]; + cs->line_end = cs->reset; + } +} + +_Static_assert( N_COLORSET_ORDER == 9, "wrong N_COLORSET_ORDER" ); +// [[col-order]] + +/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // [[color_helper_t]] @@ -3942,8 +4331,12 @@ void PrintColorSetHelper2 if ( ch->select & COLSEL_GREY ) print_by_helper_tab(ch,ch_tab_grey); - if ( ch->select & COLSEL_NAME ) + if ( (ch->select & COLSEL_NAME) == COLSEL_NAME ) print_by_helper_tab(ch,ch_tab_name); + else if ( ch->select & COLSEL_NAME1 ) + print_by_helper_tab(ch,ch_tab_name1); + else if ( ch->select & COLSEL_NAME2 ) + print_by_helper_tab(ch,ch_tab_name2); if ( ch->multi_assign > 1 ) { @@ -4041,9 +4434,9 @@ static void PrintColorSetSH PrintEscapedString(buf,sizeof(buf),col_string,-1,CHMD_ESC,0,0); if (memcmp(buf,"\\x1B",4)) - fprintf(f,"COL_%s=$'%s'\n",name,buf); + fprintf(f," COL_%s=$'%s'\n",name,buf); else - fprintf(f,"COL_%s='\\033%s'\n",name,buf+4); + fprintf(f," COL_%s='\\033%s'\n",name,buf+4); } } @@ -4100,12 +4493,12 @@ void PrintColorSet case 1: if (!cs) cs = GetColorSet(COLMD_ON); - PrintColorSetHelper(f,indent,cs,PrintColorSetSH,select,0); PrintEscapedString(buf,sizeof(buf),cs->reset,-1,CHMD_ESC,0,0); if (memcmp(buf,"\\x1B",4)) - fprintf(f,"COL0=$'%s'\n",buf); + fprintf(f," COL0=$'%s'\n",buf); else - fprintf(f,"COL0=$'\\033%s'\n",buf+4); + fprintf(f," COL0=$'\\033%s'\n",buf+4); + PrintColorSetHelper(f,indent,cs,PrintColorSetSH,select,0); break; case 2: @@ -4134,16 +4527,16 @@ void SetupColorView ( ColorView_t *cv ) cv->f = stdout; cv->indent = NormalizeIndent(cv->indent); -#ifdef DCLIB_TERMINAL + #ifdef DCLIB_TERMINAL cv->col_mode = cv->colset ? cv->colset->col_mode : NormalizeColorMode(cv->col_mode, cv->term ? cv->term->colors : 8 ); -#else + #else cv->col_mode = cv->colset ? cv->colset->col_mode : NormalizeColorMode(cv->col_mode,8); -#endif + #endif if (!cv->colset) cv->colset = GetColorSet(cv->col_mode); @@ -4429,11 +4822,17 @@ static void ViewColors256_helper #endif else { - const int mode = cv->mode & 1 ? 48 : 38; + int mode; + if ( cv->mode & 1 ) + { + mode = 48; + fputs("\e[38;5;0m",cv->f); + } + else + mode = 38; - uint i; - for ( i = beg; i < end; i += step ) - fprintf(cv->f,"\e[%u;5;%um %03u ",mode,i,i); + for ( uint i = beg; i < end; i += step ) + fprintf(cv->f,"\e[%u;5;%um %03u ",mode,i,i); fputs("\e[0m",cv->f); } } @@ -4527,6 +4926,7 @@ static void ViewColorsPredef_helper DASSERT(tab); const ColorSet_t *col = cv->colset; + const int indent = cv->indent + 15; // 15 is max name width for ( ; tab->name; tab++ ) { @@ -4538,10 +4938,10 @@ static void ViewColorsPredef_helper ccp val = *(ccp*)((u8*)col+tab->offset); if (*val) fprintf(cv->f,"%*s %s %-15s %s \\e%s\n", - cv->indent, tab->name, val, tab->name, col->reset, val+1 ); + indent, tab->name, val, tab->name, col->reset, val+1 ); else fprintf(cv->f,"%*s %s %-15s %s\n", - cv->indent, tab->name, val, tab->name, col->reset ); + indent, tab->name, val, tab->name, col->reset ); } else if (cv->allow_sep) { @@ -4566,16 +4966,18 @@ void ViewColorsPredef ( ColorView_t *cv, ColorSelect_t colsel ) colsel = COLSEL_M_MODE; cv->print_alt = ( colsel & COLSEL_F_ALT ) != 0; - cv->indent += 15; // 15 is max name width - fprintf(cv->f, "%s\n%*sPredefined%s color names for mode %s:\n\n", cv->colset->reset, cv->indent, "", - ( colsel & COLSEL_M_MODE ) == COLSEL_NAME ? " semantic" : "", + colsel & COLSEL_NAME ? " semantic" : "", GetColorModeName(cv->col_mode,0) ); - if ( colsel & COLSEL_NAME ) + if ( (colsel & COLSEL_NAME) == COLSEL_NAME ) ViewColorsPredef_helper(cv,ch_tab_name); + else if ( colsel & COLSEL_NAME1 ) + ViewColorsPredef_helper(cv,ch_tab_name1); + else if ( colsel & COLSEL_NAME2 ) + ViewColorsPredef_helper(cv,ch_tab_name2); if ( colsel & COLSEL_COLOR ) ViewColorsPredef_helper(cv,ch_tab_color); @@ -4585,8 +4987,27 @@ void ViewColorsPredef ( ColorView_t *cv, ColorSelect_t colsel ) if ( colsel & COLSEL_GREY ) ViewColorsPredef_helper(cv,ch_tab_grey); +} - cv->indent -= 15; +//----------------------------------------------------------------------------- + +void ViewColorsPage ( ColorView_t *cv, uint page ) +{ + const typeof(cv->mode) saved_mode = cv->mode; + switch(page) + { + case 0: ViewColorsAttrib8(cv); ViewColorsCombi8(cv); break; + case 1: ViewColors18(cv); break; + case 2: ViewColorsDC(cv,false); break; + case 3: ViewColorsDC(cv,true); break; + case 4: cv->mode=0; ViewColors256(cv); break; + case 5: cv->mode=1; ViewColors256(cv); break; + case 6: ViewColorsPredef(cv,COLSEL_GREY); break; + case 7: ViewColorsPredef(cv,COLSEL_M_COLOR); break; + case 8: ViewColorsPredef(cv,COLSEL_NAME1|COLSEL_F_ALT); break; + case 9: ViewColorsPredef(cv,COLSEL_NAME2|COLSEL_F_ALT); break; + } + cv->mode = saved_mode; } // diff --git a/project/dclib/dclib-color.h b/project/dclib/dclib-color.h index 9b19bbf..e36a9fe 100644 --- a/project/dclib/dclib-color.h +++ b/project/dclib/dclib-color.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -47,11 +47,7 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef SUPPORT_36_COLORS - #if NEW_FEATURES - #define SUPPORT_36_COLORS 1 - #else - #define SUPPORT_36_COLORS 0 - #endif + #define SUPPORT_36_COLORS 1 #endif //----------------------------------------------------------------------------- @@ -106,6 +102,10 @@ ColorMode_t GetPrevColorMode ( ColorMode_t col_mode ); /////////////// terminal & colors /////////////// /////////////////////////////////////////////////////////////////////////////// +#define N_COLORSET_GREY 10 +#define N_COLORSET_HL_LINE 4 +#define N_COLORSET_ORDER 9 + extern int termios_valid; // valid if >0 void ResetTermios(void); @@ -153,7 +153,25 @@ extern uint auto_term_size_dirty; // >0: SIGWINCH received, auto_term_size is di bool GetAutoTermSize(void); // return true if changed void EnableAutoTermSize(void); -//----------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////// +// [[good_term_width_t]] + +typedef struct good_term_width_t +{ + int def_width; + int min_width; + int max_width; + int good_width; + bool setup_done; +} +good_term_width_t; + +extern good_term_width_t good_term_width; + +// If !gtw: Use good_term_width +int GetGoodTermWidth ( good_term_width_t *gtw, bool recalc ); + +/////////////////////////////////////////////////////////////////////////////// //----- some basic terminal codes @@ -179,6 +197,7 @@ void EnableAutoTermSize(void); #define TERM_RESET_WRAP "\e[?7l" // disable line wrapping #define TERM_TEXT_MODE_BEG "\e[" // begin of text mode sequence +#define TERM_TEXT_MODE_BEG0 "\e[0;" // begin of text mode sequence with reset #define TERM_TEXT_MODE_END "m" // end of text mode sequence #define TERM_TEXT_MODE_RESET "\e[m" // reset text mode @@ -206,6 +225,7 @@ extern ccp TermSetWrap; // enabled line wrapping extern ccp TermResetWrap; // disable line wrapping extern ccp TermTextModeBeg; // begin of text mode sequence +extern ccp TermTextModeBeg0; // begin of text mode sequence with reset extern ccp TermTextModeEnd; // end of text mode sequence extern ccp TermTextModeReset; // reset text mode @@ -276,53 +296,69 @@ typedef enum TermTextMode_t //--- these Colors are also used to setup 'ColorSet_t' // [[new-color]] - TTM_COL_SETUP = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, - TTM_COL_RUN = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, - TTM_COL_ABORT = TTM_BOLD | TTM_MAGENTA | TTM_BG_BLACK, - TTM_COL_FINISH = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, - TTM_COL_SCRIPT = TTM_NO_BOLD | TTM_BLACK | TTM_BG_WHITE, - TTM_COL_OPEN = TTM_NO_BOLD | TTM_RED | TTM_BG_BLACK, - TTM_COL_CLOSE = TTM_NO_BOLD | TTM_RED | TTM_BG_BLACK, - TTM_COL_FILE = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, - TTM_COL_JOB = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, + TTM_COL_SETUP = TTM_RESET | TTM_BOLD | TTM_BLUE, + TTM_COL_RUN = TTM_RESET | TTM_BOLD | TTM_WHITE, + TTM_COL_ABORT = TTM_RESET | TTM_BOLD | TTM_MAGENTA, + TTM_COL_FINISH = TTM_RESET | TTM_BOLD | TTM_CYAN, + TTM_COL_SCRIPT = TTM_RESET | TTM_NO_BOLD | TTM_BLACK | TTM_BG_WHITE, + TTM_COL_OPEN = TTM_RESET | TTM_NO_BOLD | TTM_RED, + TTM_COL_CLOSE = TTM_RESET | TTM_NO_BOLD | TTM_RED, + TTM_COL_FILE = TTM_RESET | TTM_NO_BOLD | TTM_YELLOW, + TTM_COL_JOB = TTM_RESET | TTM_BOLD | TTM_BLUE, + TTM_COL_DEBUG = TTM_RESET | TTM_BOLD | TTM_RED, + TTM_COL_LOG = TTM_RESET | TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, + + TTM_COL_MARK = TTM_RESET | TTM_BOLD | TTM_WHITE, + TTM_COL_INFO = TTM_RESET | TTM_BOLD | TTM_CYAN, + TTM_COL_HINT = TTM_RESET | TTM_BOLD | TTM_YELLOW, + TTM_COL_WARN = TTM_RESET | TTM_BOLD | TTM_RED, + TTM_COL_ERR = TTM_RESET | TTM_BOLD | TTM_MAGENTA, + TTM_COL_BAD = TTM_RESET | TTM_BOLD | TTM_MAGENTA, + + TTM_COL_NAME = TTM_RESET | TTM_NO_BOLD | TTM_CYAN, + TTM_COL_OP = TTM_RESET | TTM_BOLD | TTM_WHITE, + TTM_COL_VALUE = TTM_RESET | TTM_NO_BOLD | TTM_GREEN, + TTM_COL_SUCCESS = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_ERROR = TTM_RESET | TTM_NO_BOLD | TTM_BLACK | TTM_BG_YELLOW, + TTM_COL_FAIL = TTM_RESET | TTM_NO_BOLD | TTM_WHITE | TTM_BG_RED, + TTM_COL_FAIL2 = TTM_RESET | TTM_BOLD | TTM_WHITE | TTM_BG_RED, + TTM_COL_FATAL = TTM_RESET | TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, - TTM_COL_INFO = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, - TTM_COL_HINT = TTM_BOLD | TTM_YELLOW | TTM_BG_BLACK, - TTM_COL_WARN = TTM_BOLD | TTM_RED | TTM_BG_BLACK, - TTM_COL_DEBUG = TTM_BOLD | TTM_RED | TTM_BG_BLACK, - TTM_COL_LOG = TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, + TTM_COL_SELECT = TTM_RESET | TTM_NO_BOLD | TTM_YELLOW, + TTM_COL_DIFFER = TTM_RESET | TTM_BOLD | TTM_YELLOW | TTM_BG_BLUE, + TTM_COL_HL_LINE0 = TTM_RESET | TTM_NO_BOLD | TTM_WHITE, + TTM_COL_HL_LINE1 = TTM_RESET | TTM_BOLD | TTM_CYAN, + TTM_COL_HL_LINE2 = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_HL_LINE3 = TTM_RESET | TTM_BOLD | TTM_MAGENTA, - TTM_COL_NAME = TTM_NO_BOLD | TTM_CYAN | TTM_BG_BLACK, - TTM_COL_OP = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, - TTM_COL_VALUE = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_SUCCESS = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_ERROR = TTM_NO_BOLD | TTM_BLACK | TTM_BG_YELLOW, - TTM_COL_FAIL = TTM_NO_BOLD | TTM_WHITE | TTM_BG_RED, - TTM_COL_FAIL2 = TTM_BOLD | TTM_WHITE | TTM_BG_RED, - TTM_COL_FATAL = TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, - TTM_COL_MARK = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, - TTM_COL_BAD = TTM_BOLD | TTM_MAGENTA | TTM_BG_BLACK, + TTM_COL_ORDER0 = TTM_RESET | TTM_NO_BOLD | TTM_WHITE, + TTM_COL_ORDER1 = TTM_RESET | TTM_BOLD | TTM_BLUE, + TTM_COL_ORDER2 = TTM_RESET | TTM_BOLD | TTM_CYAN, + TTM_COL_ORDER3 = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_ORDER4 = TTM_RESET | TTM_BOLD | TTM_YELLOW, + TTM_COL_ORDER5 = TTM_RESET | TTM_BOLD | TTM_RED, + TTM_COL_ORDER6 = TTM_RESET | TTM_BOLD | TTM_MAGENTA, + TTM_COL_ORDER7 = TTM_RESET | TTM_BOLD | TTM_WHITE, + TTM_COL_ORDER8 = TTM_RESET | TTM_BOLD | TTM_WHITE, - TTM_COL_SELECT = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, - TTM_COL_DIFFER = TTM_BOLD | TTM_YELLOW | TTM_BG_BLUE, - TTM_COL_STAT_LINE = TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, - TTM_COL_WARN_LINE = TTM_BOLD | TTM_RED | TTM_BG_BLUE, - TTM_COL_PROC_LINE = TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, - TTM_COL_CITE = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_STATUS = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_HIGHLIGHT = TTM_BOLD | TTM_YELLOW | TTM_BG_BLACK, - TTM_COL_HIDE = TTM_BOLD | TTM_BLACK | TTM_BG_BLACK, + TTM_COL_STAT_LINE = TTM_RESET | TTM_BOLD | TTM_WHITE | TTM_BG_BLUE, + TTM_COL_WARN_LINE = TTM_RESET | TTM_BOLD | TTM_RED | TTM_BG_BLUE, + TTM_COL_PROC_LINE = TTM_RESET | TTM_BOLD | TTM_WHITE | TTM_BG_MAGENTA, + TTM_COL_CITE = TTM_RESET | TTM_NO_BOLD | TTM_GREEN, + TTM_COL_STATUS = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_HIGHLIGHT = TTM_RESET | TTM_BOLD | TTM_YELLOW, + TTM_COL_HIDE = TTM_RESET | TTM_BOLD | TTM_BLACK, - TTM_COL_HEADING = TTM_BOLD | TTM_BLUE | TTM_BG_BLACK, - TTM_COL_CAPTION = TTM_BOLD | TTM_CYAN | TTM_BG_BLACK, - TTM_COL_SECTION = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_SYNTAX = TTM_BOLD | TTM_WHITE | TTM_BG_BLACK, - TTM_COL_CMD = TTM_NO_BOLD | TTM_CYAN | TTM_BG_BLACK, - TTM_COL_OPTION = TTM_NO_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_PARAM = TTM_NO_BOLD | TTM_YELLOW | TTM_BG_BLACK, + TTM_COL_HEADING = TTM_RESET | TTM_BOLD | TTM_BLUE, + TTM_COL_CAPTION = TTM_RESET | TTM_BOLD | TTM_CYAN, + TTM_COL_SECTION = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_SYNTAX = TTM_RESET | TTM_BOLD | TTM_WHITE, + TTM_COL_CMD = TTM_RESET | TTM_NO_BOLD | TTM_CYAN, + TTM_COL_OPTION = TTM_RESET | TTM_NO_BOLD | TTM_GREEN, + TTM_COL_PARAM = TTM_RESET | TTM_NO_BOLD | TTM_YELLOW, - TTM_COL_ON = TTM_BOLD | TTM_GREEN | TTM_BG_BLACK, - TTM_COL_OFF = TTM_BOLD | TTM_RED | TTM_BG_BLACK, + TTM_COL_ON = TTM_RESET | TTM_BOLD | TTM_GREEN, + TTM_COL_OFF = TTM_RESET | TTM_BOLD | TTM_RED, } TermTextMode_t; @@ -386,7 +422,7 @@ typedef enum TermColorIndex_t TCI__N = TCI__IGNORE, // number of supported colors TCI__N_FONT = TCI_B_RED, // number of recommended font colors TCI__N_BG = TCI__N, // number of recommended background colors - TCI__N_GREY = TCI_RED, // number of gray tones + TCI__N_GREY = TCI_RED, // number of gray tones #else // !SUPPORT_36_COLORS @@ -418,7 +454,7 @@ typedef enum TermColorIndex_t TCI__N = TCI__IGNORE, // number of supported colors TCI__N_FONT = TCI_B_RED, // number of recommended font colors TCI__N_BG = TCI__N, // number of recommended background colors - TCI__N_GREY = TCI_RED, // number of gray tones + TCI__N_GREY = TCI_RED, // number of gray tones #endif // !SUPPORT_36_COLORS } @@ -433,9 +469,108 @@ TermColorIndex_t; extern const int Color18Index[18+1]; // 18 elements + -1 as terminator //----------------------------------------------------------------------------- -// [[ColorSet_t]] +// [[TermColorIndexEx_t]] + +typedef enum TermColorIndexEx_t +{ + TCE_GREY0, + TCE_GREY1, + TCE_GREY2, + TCE_GREY3, + TCE_GREY4, + TCE_GREY5, + TCE_GREY6, + TCE_GREY7, + TCE_GREY8, + TCE_GREY9, + + TCE_RED_MAGENTA, + TCE_RED, + TCE_RED_ORANGE, + TCE_ORANGE_RED, + TCE_ORANGE, + TCE_ORANGE_YELLOW, + TCE_YELLOW_ORANGE, + TCE_YELLOW, + TCE_YELLOW_GREEN, + TCE_GREEN_YELLOW, + TCE_GREEN, + TCE_GREEN_CYAN, + TCE_CYAN_GREEN, + TCE_CYAN, + TCE_CYAN_BLUE, + TCE_BLUE_CYAN, + TCE_BLUE, + TCE_BLUE_MAGENTA, + TCE_MAGENTA_BLUE, + TCE_MAGENTA, + TCE_MAGENTA_RED, + + TCE_B_RED_MAGENTA, + TCE_B_RED, + TCE_B_RED_ORANGE, + TCE_B_ORANGE_RED, + TCE_B_ORANGE, + TCE_B_ORANGE_YELLOW, + TCE_B_YELLOW_ORANGE, + TCE_B_YELLOW, + TCE_B_YELLOW_GREEN, + TCE_B_GREEN_YELLOW, + TCE_B_GREEN, + TCE_B_GREEN_CYAN, + TCE_B_CYAN_GREEN, + TCE_B_CYAN, + TCE_B_CYAN_BLUE, + TCE_B_BLUE_CYAN, + TCE_B_BLUE, + TCE_B_BLUE_MAGENTA, + TCE_B_MAGENTA_BLUE, + TCE_B_MAGENTA, + TCE_B_MAGENTA_RED, + + TCE__N, -#define N_COLORSET_GREY 10 + TCE_BLACK = TCE_GREY0, + TCE_WHITE = TCE_GREY9, +} +TermColorIndexEx_t; + +//----------------------------------------------------------------------------- +// [[TermColorWarn_t]] + +typedef enum TermColorWarn_t +{ + TCW_MARK, + TCW_INFO, + TCW_HINT, + TCW_WARN, + TCW_ERR, + TCW_BAD, + TCW__N, +} +TermColorWarn_t; + +//----------------------------------------------------------------------------- +// [[TermColorId_t]] + +typedef struct TermColorId_t +{ + union + { + u8 col[TCE__N]; + u8 gray[N_COLORSET_GREY]; + }; +} +TermColorId_t; + +// values 0x00-0x07 for standard, 0x10-0x17 for bold +extern const TermColorId_t color8id; + +// values for 38;5;x or 48;5;x +extern const TermColorId_t color256id; + +//----------------------------------------------------------------------------- +// [[ColorSet_t]] typedef struct ColorSet_t { @@ -445,12 +580,14 @@ typedef struct ColorSet_t u8 colorize; u16 n_colors; + //--- some strings for formatted output if colors active ccp space; // pointer to 'EmptyString' or to 'Space200'; ccp tab; // pointer to 'EmptyString' or to 'TAB20'; ccp lf; // pointer to 'EmptyString' or to 'LF20'; + //--- data, always begins with 'reset' // [[new-color]] @@ -465,13 +602,23 @@ typedef struct ColorSet_t ccp close; ccp file; ccp job; - - ccp info; - ccp hint; - ccp warn; ccp debug; ccp log; + union + { + ccp warn_level[TCW__N]; + struct + { + ccp mark; + ccp info; + ccp hint; + ccp warn; + ccp err; + ccp bad; + }; + }; + ccp name; ccp op; ccp value; @@ -480,9 +627,9 @@ typedef struct ColorSet_t ccp fail; ccp fail2; ccp fatal; - ccp mark; - ccp bad; + ccp hl_line[N_COLORSET_HL_LINE]; + ccp order[N_COLORSET_ORDER]; ccp select; ccp differ; ccp stat_line; @@ -556,6 +703,13 @@ typedef struct ColorSet_t ccp b_magenta_red; ccp matrix[TCI__N_FONT][TCI__N_BG]; + + + //--- line support (mutable) + + ccp line_begin; // use this at beginning of line + ccp line_reset; // use this in line to reset line color + ccp line_end; // use this at end of line } ColorSet_t; @@ -564,18 +718,20 @@ ColorSet_t; typedef enum ColorSelect_t { - COLSEL_COLOR = 0x01, - COLSEL_B_COLOR = 0x02, - COLSEL_GREY = 0x04, - COLSEL_NAME = 0x08, + COLSEL_COLOR = 0x01, // standard colors + COLSEL_B_COLOR = 0x02, // bold colors + COLSEL_GREY = 0x04, // grey scale + + COLSEL_NAME1 = 0x08, // semantic color names, part 1 + COLSEL_NAME2 = 0x10, // semantic color names, part 2 + COLSEL_NAME = 0x18, // semantic color names, both parts - COLSEL_F_ALT = 0x10, + COLSEL_F_ALT = 0x20, COLSEL_M_COLOR = COLSEL_COLOR|COLSEL_B_COLOR, COLSEL_M_NONAME = COLSEL_COLOR|COLSEL_B_COLOR|COLSEL_GREY, COLSEL_M_MODE = 0x0f, COLSEL_M_ALL = 0x1f, - } ColorSelect_t; @@ -631,7 +787,22 @@ const ColorSet_t * GetColorSetAuto ( bool force_on ); const ColorSet_t * GetFileColorSet ( FILE *f ); bool SetupColorSet ( ColorSet_t *cc, FILE *f ); +int GetColorOffsetByName ( ccp name ); // returns -1 if not found + +// returns EmptyString[] if not found ccp GetColorByName ( const ColorSet_t *colset, ccp name ); +ccp GetColorByOffsetOrName ( const ColorSet_t *colset, int * offset, ccp name ); + +extern const char EmptyString[]; +static inline ccp GetColorByOffset ( const ColorSet_t *colset, int offset ) + { return offset > 0 ? *(ccp*)((u8*)colset+offset) : EmptyString; } + +static inline ccp GetColorByValidOffset ( const ColorSet_t *colset, int offset ) + { return *(ccp*)((u8*)colset+offset); } + +// both will change line_col & line_reset +void ResetLineColor ( const ColorSet_t *colset ); +void SetLineColor ( const ColorSet_t *colset, int level ); void PrintColorSet ( @@ -713,6 +884,9 @@ void ViewColorsDC ( ColorView_t *cv, bool optimized ); void ViewColors256 ( ColorView_t *cv ); void ViewColorsPredef ( ColorView_t *cv, ColorSelect_t select ); +#define VIEW_COLOR_PAGES 10 +void ViewColorsPage ( ColorView_t *cv, uint page ); + /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // legacy functions (deprecated) ==> wrapper for ViewColors*() diff --git a/project/dclib/dclib-debug.c b/project/dclib/dclib-debug.c index 7017608..6e23f6e 100644 --- a/project/dclib/dclib-debug.c +++ b/project/dclib/dclib-debug.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -495,8 +495,13 @@ enumError PrintErrorArg ( ccp func, ccp file, unsigned int line, } #if defined(EXTENDED_ERRORS) && EXTENDED_ERRORS > 1 + #ifdef SVN_NEXT + fprintf(stdwrn,"%s -> %s/%s?annotate=%d#l%d\n", + prefix, URI_VIEWVC, file, SVN_NEXT, line ); + #elif defined(REVISION_NEXT) fprintf(stdwrn,"%s -> %s/%s?annotate=%d#l%d\n", prefix, URI_VIEWVC, file, REVISION_NEXT, line ); + #endif #endif fputs(prefix,stdwrn); @@ -674,14 +679,10 @@ void HexDiff ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, /////////////// trace functions /////////////// /////////////////////////////////////////////////////////////////////////////// -u_msec_t GetTimerMSec(); - -/////////////////////////////////////////////////////////////////////////////// - static void trace_helper ( int print_stderr, ccp format, va_list arg ) { static uint last_day = 0; - DayTime_t dt = GetDayTime(true); + DayTime_t dt = GetDayTime(false); if (!TRACE_FILE) { @@ -713,7 +714,7 @@ static void trace_helper ( int print_stderr, ccp format, va_list arg ) if ( last_day != dt.day ) { SetupTimezone(true); - dt = GetDayTime(true); + dt = GetDayTime(false); last_day = dt.day; snprintf(pre,sizeof(pre), "\n#DAY: %s, pid=%d\n%02u:%02u:%02u.%03u ", diff --git a/project/dclib/dclib-debug.h b/project/dclib/dclib-debug.h index 5851141..b8aefc8 100644 --- a/project/dclib/dclib-debug.h +++ b/project/dclib/dclib-debug.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -171,6 +171,20 @@ void HexDiff ( FILE * f, int indent, u64 addr, int addr_fw, int row_len, void HexDiff16 ( FILE * f, int indent, u64 addr, const void * data1, size_t count1, const void * data2, size_t count2 ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// debug helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PrintDebugFunc] + +typedef int (*PrintDebugFunc) +( + ccp format, // format string + ... // parameters for 'format' +) +__attribute__ ((__format__(__printf__,1,2))); + // /////////////////////////////////////////////////////////////////////////////// /////////////// DEBUG and TRACING /////////////// @@ -415,6 +429,12 @@ void WAIT_ARG_FUNC ( ccp format, va_list arg ); #undef PRINT_SIZEOF1 #undef BINGO1 +#undef PRINTD +#undef PRINT_IFD +#undef PRINT_SIZEOFD +#undef BINGOD +#undef HAVE_PRINTD + #undef xBINGO #undef HAVE_PRINT #undef PRINT_TIME @@ -437,27 +457,41 @@ void WAIT_ARG_FUNC ( ccp format, va_list arg ); #if defined(DEBUG) && defined(TEST) #define HAVE_PRINT 1 - #define PRINT PRINT1 #define PRINT_IF PRINT_IF1 #define PRINT_SIZEOF PRINT_SIZEOF1 #define BINGO BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) - void PRINT_TIME ( time_t time, ccp title ); #else #define HAVE_PRINT HAVE_TRACE - #define PRINT TRACE #define PRINT_IF TRACE_IF #define PRINT_SIZEOF TRACE_SIZEOF #define BINGO TRACELINE - #define PRINT_TIME(...) #endif +#if IS_DEVELOP + + #define HAVE_PRINTD 1 + #define PRINTD PRINT1 + #define PRINT_IFD PRINT_IF1 + #define PRINT_SIZEOFD PRINT_SIZEOF1 + #define BINGOD BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) + +#else + + #define HAVE_PRINTD 0 + #define PRINTD(...) + #define PRINT_IFD(cond,...) + #define PRINT_SIZEOFD(t) + #define BINGOD + +#endif + #define xBINGO BINGO_FUNC(__FUNCTION__,__LINE__,__FILE__) #define xTRACELINE TRACE_FUNC("%s() line #%d @ %s\n",__FUNCTION__,__LINE__,__FILE__) diff --git a/project/dclib/dclib-file.c b/project/dclib/dclib-file.c index f58ace1..aae70a3 100644 --- a/project/dclib/dclib-file.c +++ b/project/dclib/dclib-file.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -96,6 +96,317 @@ bool EnableSingleCharInput() // /////////////////////////////////////////////////////////////////////////////// +/////////////// log helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +LogFile_t GlobalLogFile = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +FILE * TryOpenFile ( FILE *f, ccp fname, ccp mode ) +{ + if ( f == TRY_OPEN_FILE ) + f = fopen(fname,mode); + else if (f) + { + rewind(f); + fflush(f); + } + return f; +} + +/////////////////////////////////////////////////////////////////////////////// + +int GetLogTimestamp ( char *buf, uint buf_size, TimestampMode_t ts_mode ) +{ + DASSERT(buf); + DASSERT(buf_size>2); + + uint len; + if ( ts_mode >= TSM_USEC ) + { + const u64 usec = GetTimeUSec(false); + const uint sec = usec/1000000 % SEC_PER_DAY; + len = snprintf(buf,buf_size, + "%02u:%02u:%02u.%06llu ", + sec / 3600, + sec / 60 % 60, + sec % 60, + usec % 1000000 ); + } + else if ( ts_mode >= TSM_MSEC ) + { + const u64 msec = GetTimeMSec(false); + const uint sec = msec/USEC_PER_MSEC % SEC_PER_DAY; + len = snprintf(buf,buf_size, + "%02u:%02u:%02u.%03llu ", + sec / 3600, + sec / 60 % 60, + sec % 60, + msec % 1000 ); + } + else if ( ts_mode >= TSM_SEC ) + { + const uint sec = GetTimeSec(false) % SEC_PER_DAY; + len = snprintf(buf,buf_size, + "%02u:%02u:%02u ", + sec / 3600, + sec / 60 % 60, + sec % 60 ); + } + else + { + *buf = 0; + len = 0; + } + + if ( len >= buf_size ) + { + len = buf_size - 1; + buf[len] = 0; + } + return len; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintLogTimestamp ( LogFile_t *lf ) +{ + DASSERT(lf); + DASSERT(lf->log); + + int stat = 0; + if ( lf && lf->log ) + { + char buf[50]; + stat = GetLogTimestamp(buf,sizeof(buf),lf->ts_mode); + if ( stat > 0 ) + fwrite(buf,stat,1,lf->log); + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +int PutLogFile ( LogFile_t *lf, ccp text, int text_len ) +{ + if (!lf) + lf = &GlobalLogFile; + + if ( !lf->log || !text ) + return 0; + + //--- collect data first to have a single print + + char buf[2000]; + char *dest = buf + GetLogTimestamp( buf, sizeof(buf), lf->ts_mode ); + dest = StringCopyEMem( dest, buf+sizeof(buf), lf->tag ); + dest = StringCopyEM( dest, buf+sizeof(buf), text, + text_len < 0 ? strlen(text) : text_len ); + + const int len = dest - buf; + fwrite(buf,len,1,lf->log); + if (lf->flush) + fflush(lf->log); + if ( lf->log == stdout ) + stdout_seq_count++; + return len; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintArgLogFile ( LogFile_t *lf, ccp format, va_list arg ) +{ + int stat = 0; + if (format) + { + char buf[2000]; + int len = vsnprintf(buf,sizeof(buf),format,arg); + if ( len >= 0 ) + { + if ( len >= sizeof(buf)-1 ) + { + len = sizeof(buf)-1; + buf[sizeof(buf)-1] = 0; + } + stat = PutLogFile(lf,buf,len); + } + } + return stat; +} + +/////////////////////////////////////////////////////////////////////////////// + +int PrintLogFile ( LogFile_t *lf, ccp format, ... ) +{ + va_list arg; + va_start(arg,format); + const int stat = PrintArgLogFile(lf,format,arg); + va_end(arg); + return stat; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// search tool /////////////// +/////////////////////////////////////////////////////////////////////////////// + +exmem_t SearchToolByPATH ( ccp tool ) +{ + exmem_t dest = {}; + if ( tool && *tool ) + { + char fname[PATH_MAX]; + if (strchr(tool,'/')) + { + struct stat st; + if ( !stat(tool,&st) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) ) + AssignExMemS(&dest,tool,-1,CPM_COPY); + } + else + { + ccp path = getenv("PATH"); + for(;;) + { + while ( *path == ':' ) + path++; + ccp start = path; + while ( *path && *path != ':' ) + path++; + const int len = path - start; + if (!len) + break; + + const int flen = snprintf(fname,sizeof(fname),"%.*s/%s",len,start,tool); + struct stat st; + if ( !stat(fname,&st) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH) ) + { + AssignExMemS(&dest,fname,flen,CPM_COPY); + break; + } + } + } + } + return dest; +} + +/////////////////////////////////////////////////////////////////////////////// + +exmem_t SearchToolByList ( ccp * list, int max ) +{ + exmem_t dest = {}; + + if (list) + { + if ( max < 0 ) + { + max = 0; + for ( ccp *ptr = list; *ptr; ptr++ ) + max++; + } + + while ( max-- > 0 ) + { + ccp arg = *list++; + if (arg) + { + if ( *arg == '>' ) + { + ccp env = getenv(arg+1); + if ( env && *env ) + { + AssignExMemS(&dest,env,-1,CPM_LINK); + break; + } + } + else + { + dest = SearchToolByPATH(arg); + if (dest.data.len) + break; + } + } + } + } + return dest; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// pipe to pager /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static FILE *pager_file = 0; + +/////////////////////////////////////////////////////////////////////////////// + +FILE * OpenPipeToPager() +{ + static bool done = false; + + if (!done) + { + done = true; + + static ccp search_tab[] = + { + ">DC_PAGER", + ">DC_PAGER1", + ">DC_PAGER2", + ">PAGER", + "less", + "more", + 0 + }; + + exmem_t pager = SearchToolByList(search_tab,-1); + if (pager.data.ptr) + pager_file = popen(pager.data.ptr,"we"); + FreeExMem(&pager); + } + + return pager_file; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClosePagerFile() +{ + if (pager_file) + { + pclose(pager_file); + pager_file = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool StdoutToPager() +{ + FILE *f = OpenPipeToPager(); + if (f) + { + fflush(stdout); + stdout = f; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseStdoutToPager() +{ + if ( pager_file && pager_file == stdout ) + { + ClosePagerFile(); + stdout = 0; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// enum FileMode_t /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -3246,6 +3557,69 @@ uint NumberedFilename return index; } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +static char * GetBlockDeviceHolderHelper + ( char *buf, char *endbuf, ccp name, ccp sep, int *level ) +{ + DASSERT(buf); + DASSERT(endbuf); + DASSERT(level); + + char path[200]; + snprintf(path,sizeof(path),"/sys/class/block/%s/holders",name); + if (IsDirectory(path,0)) + { + DIR *fdir = opendir(path); + if (fdir) + { + for(;;) + { + struct dirent *dent = readdir(fdir); + if (!dent) + break; + if ( dent->d_name[0] != '.' ) + { + buf = GetBlockDeviceHolderHelper(buf,endbuf,dent->d_name,sep,level); + (*level)++; + break; + } + } + closedir(fdir); + } + } + + return StringCat2E(buf,endbuf,sep,name); +} + +//----------------------------------------------------------------------------- + +ccp GetBlockDeviceHolder ( ccp name, ccp sep, int *ret_level ) +{ + static ParamField_t db = { .free_data = true }; + + bool old_found; + ParamFieldItem_t *item = FindInsertParamField(&db,name,false,0,&old_found); + DASSERT(item) + if (!old_found) + { + int level = 0; + char buf[1000]; + ccp end = GetBlockDeviceHolderHelper(buf,buf+sizeof(buf)-strlen(sep)-1,name,sep,&level); + ccp beg = buf + strlen(sep); + if ( beg < end ) + { + item->num = level; + item->data = MEMDUP(beg,end-beg+1); + } + } + + if (ret_level) + *ret_level = item->num; + return item->data; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// normalize filenames /////////////// @@ -3305,13 +3679,13 @@ char * NormalizeFileName if ( isalnum(ch) || ( is_utf8 ? ch >= 0x80 - : ch == 0xe4 // ä - || ch == 0xf6 // ö - || ch == 0xfc // ü - || ch == 0xdf // ß - || ch == 0xc4 // Ä - || ch == 0xd6 // Ö - || ch == 0xdc // Ü + : ch == 0xe4 // ä + || ch == 0xf6 // ö + || ch == 0xfc // ü + || ch == 0xdf // ß + || ch == 0xc4 // Ä + || ch == 0xd6 // Ö + || ch == 0xdc // Ü ) || strchr("_+-=%'\"$%&#,.!()[]{}<>",ch) ) @@ -3439,7 +3813,7 @@ uint NormalizeFilenameCygwin } else dest = buf; - #else // + #else // *dest++ = '/'; src += 2; if (*src) @@ -3623,7 +3997,7 @@ static int add_search // returns 0 if !stop_if_found, 2 if added+found, 1 if added, 0 else search_file_list_t *sfl, // valid data - ccp config_fname, // default filename (without path) of config file + ccp config_fname, // default filename (without path) of config file ccp *list, // NULL or list of filenames int n_list, // num of 'list' elements, -1:null terminated list ccp prefix, // prefix for relative filenames @@ -3659,7 +4033,7 @@ static int add_search search_file_t *sf = AppendSearchFile( sfl, res.data.ptr, res.is_alloced ? CPM_MOVE : CPM_COPY, config_fname ); if (sf) - { + { sf->hint |= hint; if ( sf->itype == INTY_REG && stop_if_found > 0 ) return 2; @@ -3755,6 +4129,7 @@ void ClearFDList ( FDList_t *fdl ) fdl->now_usec = GetTimeUSec(false); fdl->timeout_usec = M1(fdl->timeout_usec); + fdl->timeout_nsec = M1(fdl->timeout_nsec); } /////////////////////////////////////////////////////////////////////////////// @@ -3815,10 +4190,11 @@ uint AddFDList FDList_t *fdl, // valid socket list int sock, // socket to add - uint events // bit field: POLLIN|POLLOUT|POLLERR + uint events // bit field: POLLIN|POLLPRI|POLLOUT|POLLRDHUP|... ) { - if ( sock == -1 || !(events&(POLLIN|POLLOUT|POLLERR)) ) + events &= ~(POLLERR|POLLHUP|POLLNVAL); // only result! + if ( sock == -1 || !events ) return ~0; fdl->n_sock++; @@ -3840,7 +4216,7 @@ uint AddFDList FD_SET(sock,&fdl->readfds); if ( events & POLLOUT ) FD_SET(sock,&fdl->writefds); - if ( events & POLLERR ) + if ( events & POLLPRI ) FD_SET(sock,&fdl->exceptfds); return ~0; } @@ -3849,7 +4225,7 @@ uint AddFDList uint GetEventFDList ( - // returns bit field: POLLIN|POLLOUT|POLLERR + // returns bit field: POLLIN|POLLOUT|POLLERR|... FDList_t *fdl, // valid socket list int sock, // socket to look for @@ -3879,13 +4255,53 @@ uint GetEventFDList if (FD_ISSET(sock,&fdl->writefds)) revents |= POLLOUT; if (FD_ISSET(sock,&fdl->exceptfds)) - revents |= POLLERR; + revents |= POLLPRI; return revents; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// +static s_usec_t prepare_wait_fdl ( FDList_t *fdl ) +{ + DASSERT(fdl); + const s_usec_t now_usec = GetTimeUSec(false); + + if (fdl->timeout_nsec) + { + const u_usec_t wait_until + = ( (s_usec_t)fdl->timeout_nsec - (s_usec_t)GetTimerNSec() ) + / NSEC_PER_USEC + now_usec + 1; + if ( !fdl->timeout_usec || fdl->timeout_usec > wait_until ) + fdl->timeout_usec = wait_until; + } + + if ( fdl->min_wait_usec && fdl->timeout_usec ) + { + const u_usec_t wait_until = now_usec + fdl->min_wait_usec; + if ( fdl->timeout_usec < wait_until ) + fdl->timeout_usec = wait_until; + } + + return now_usec; +} + +//----------------------------------------------------------------------------- + +static void finish_wait_fdl ( FDList_t *fdl, u_usec_t start_usec ) +{ + DASSERT(fdl); + UpdateCurrentTime(); + fdl->now_usec = current_time.usec; + fdl->last_wait_usec = fdl->now_usec - start_usec; + fdl->wait_usec += fdl->last_wait_usec; + fdl->wait_count++; + + UpdateCpuUsageIncrement(); +} + +/////////////////////////////////////////////////////////////////////////////// + int WaitFDList ( FDList_t *fdl // valid socket list @@ -3893,49 +4309,75 @@ int WaitFDList { DASSERT(fdl); - int stat; - u64 now_usec = GetTimeUSec(false); + const s_usec_t now_usec = prepare_wait_fdl(fdl); + int stat; if (fdl->use_poll) { int timeout; - if ( fdl->timeout_usec > now_usec ) + if ( !fdl->timeout_usec ) + timeout = -1; + else if ( fdl->timeout_usec > now_usec ) { - u64 delta = ( fdl->timeout_usec - now_usec ) / 1000; - timeout = delta > 0x7fffffffull ? -1 : delta; + u_msec_t delta = ( fdl->timeout_usec - now_usec ) / USEC_PER_MSEC; + timeout = delta < 0x7fffffff ? delta : 0x7fffffff; + if (!timeout) + timeout = 1; } else timeout = 0; + if (fdl->debug_file) + { + fprintf(fdl->debug_file,"POLL: timeout=%d\n",timeout); + fflush(fdl->debug_file); + } stat = poll( fdl->poll_list, fdl->poll_used, timeout ); } else { struct timeval tv, *ptv = &tv; - if ( fdl->timeout_usec > now_usec ) + if ( !fdl->timeout_usec ) + ptv = NULL; + else if ( fdl->timeout_usec > now_usec ) { - const u64 delta = fdl->timeout_usec - now_usec; - if ( delta > 1000000ull * 0x7fffffffull ) - ptv = NULL; + const u_usec_t delta = fdl->timeout_usec - now_usec; + if ( delta > USEC_PER_YEAR ) + { + tv.tv_sec = SEC_PER_YEAR; + tv.tv_usec = 0; + } + else if ( delta > USEC_PER_MSEC ) + { + tv.tv_sec = delta / USEC_PER_SEC; + tv.tv_usec = delta % USEC_PER_SEC; + } else { - tv.tv_sec = delta / 1000000; - tv.tv_usec = delta % 1000000; + tv.tv_sec = 0; + tv.tv_usec = USEC_PER_MSEC; } } else tv.tv_sec = tv.tv_usec = 0; -//DEL fprintf(stderr,"#SELECT %lld: ptv=%d: %lu.%06lu\n", -//DEL fdl->timeout_usec-now_usec, ptv!=0, tv.tv_sec, tv.tv_usec ); + if (fdl->debug_file) + { + if (ptv) + fprintf(fdl->debug_file,"# SELECT: usec=%lld, Δnow=%lld, timeout=%llu.%06llu\n", + fdl->timeout_usec, fdl->timeout_usec - now_usec, + (u64)ptv->tv_sec, (u64)ptv->tv_usec ); + else + fprintf(fdl->debug_file,"# SELECT: usec=%lld, Δnow=%lld, timeout=NULL\n", + fdl->timeout_usec, fdl->timeout_usec - now_usec ); + fflush(fdl->debug_file); + } + stat = select( fdl->max_fd+1, &fdl->readfds, &fdl->writefds, &fdl->exceptfds, ptv ); } - fdl->now_usec = GetTimeUSec(false); - fdl->cur_usec = fdl->now_usec - now_usec; - fdl->wait_usec += fdl->cur_usec; - fdl->wait_count++; + finish_wait_fdl(fdl,now_usec); return stat; } @@ -3943,7 +4385,7 @@ int WaitFDList int PWaitFDList ( - FDList_t *fdl, // valid socket list + FDList_t *fdl, // valid socket list const sigset_t *sigmask // NULL or signal mask ) { @@ -3963,18 +4405,24 @@ int PWaitFDList } #endif + const u_usec_t now_usec = prepare_wait_fdl(fdl); + struct timespec ts, *pts = &ts; - u64 now_usec = GetTimeUSec(false); if ( fdl->timeout_usec > now_usec ) { const u64 delta = fdl->timeout_usec - now_usec; if ( delta > 1000000ull * 0x7fffffffull ) pts = NULL; - else + else if ( delta > USEC_PER_MSEC ) { ts.tv_sec = delta / 1000000; ts.tv_nsec = delta % 1000000 * 1000; } + else + { + ts.tv_sec = 0; + ts.tv_nsec = NSEC_PER_MSEC; + } } else ts.tv_sec = ts.tv_nsec = 0; @@ -3989,10 +4437,7 @@ int PWaitFDList &fdl->exceptfds, pts, sigmask ); #endif - fdl->now_usec = GetTimeUSec(false); - fdl->cur_usec = fdl->now_usec - now_usec; - fdl->wait_usec += fdl->cur_usec; - fdl->wait_count++; + finish_wait_fdl(fdl,now_usec); return stat; } @@ -5391,6 +5836,8 @@ void RestoreStateByTable uint aelem_count = 0; uint aelem_offset = 0; + bool mode_add = false; + for ( ; srt->type != SRT__TERM; srt++ ) { if ( srt->type == SRT_DEF_ARRAY ) @@ -5407,6 +5854,16 @@ void RestoreStateByTable continue; } + if ( srt->type >= SRT_MODE__BEGIN && srt->type <= SRT_MODE__END ) + { + switch (srt->type) + { + case SRT_MODE_ASSIGN: mode_add = false; break; + case SRT_MODE_ADD: mode_add = true; break; + } + continue; + } + if ( srt->type == SRT__IS_LIST || !srt->name ) continue; @@ -5456,13 +5913,22 @@ void RestoreStateByTable if ( !i && srt->type == SRT_COUNT && num <= UINT_MAX ) last_count = num; - switch(srt->size) - { - case 1: *(u8*) val = num; break; - case 2: *(u16*)val = num; break; - case 4: *(u32*)val = num; break; - case 8: *(u64*)val = num; break; - } + if (mode_add) + switch(srt->size) + { + case 1: *(u8*) val += num; break; + case 2: *(u16*)val += num; break; + case 4: *(u32*)val += num; break; + case 8: *(u64*)val += num; break; + } + else + switch(srt->size) + { + case 1: *(u8*) val = num; break; + case 2: *(u16*)val = num; break; + case 4: *(u32*)val = num; break; + case 8: *(u64*)val = num; break; + } if ( ++i == n_elem ) break; val += elem_off; @@ -5485,13 +5951,22 @@ void RestoreStateByTable while (*src) { s64 num = str2ll(src,&src,10); - switch(srt->size) - { - case 1: *(s8*) val = num; break; - case 2: *(s16*)val = num; break; - case 4: *(s32*)val = num; break; - case 8: *(s64*)val = num; break; - } + if (mode_add) + switch(srt->size) + { + case 1: *(s8*) val += num; break; + case 2: *(s16*)val += num; break; + case 4: *(s32*)val += num; break; + case 8: *(s64*)val += num; break; + } + else + switch(srt->size) + { + case 1: *(s8*) val = num; break; + case 2: *(s16*)val = num; break; + case 4: *(s32*)val = num; break; + case 8: *(s64*)val = num; break; + } if ( ++i == n_elem ) break; val += elem_off; @@ -5514,12 +5989,25 @@ void RestoreStateByTable uint i = 0; while (*src) { - if ( srt->size == sizeof(float) ) - *(float*)val = strtof(src,&src); - else if ( srt->size == sizeof(double) ) - *(double*)val = strtod(src,&src); - else if ( srt->size == sizeof(long double) ) - *(long double*)val = strtold(src,&src); + if (mode_add) + { + if ( srt->size == sizeof(float) ) + *(float*)val += strtof(src,&src); + else if ( srt->size == sizeof(double) ) + *(double*)val += strtod(src,&src); + else if ( srt->size == sizeof(long double) ) + *(long double*)val += strtold(src,&src); + } + else + { + if ( srt->size == sizeof(float) ) + *(float*)val = strtof(src,&src); + else if ( srt->size == sizeof(double) ) + *(double*)val = strtod(src,&src); + else if ( srt->size == sizeof(long double) ) + *(long double*)val = strtold(src,&src); + } + if ( ++i == n_elem ) break; val += elem_off; @@ -6378,6 +6866,192 @@ int PrintScriptVars // /////////////////////////////////////////////////////////////////////////////// +/////////////// CpuStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +CpuStatus_t GetCpuStatus ( CpuStatus_t * prev_cpuinfo ) +{ + //--- retrieve static data + + static int n_cpu = -1; + static int clock_ticks = 100; + + char buf[1000]; + + if ( n_cpu < 0 ) + { + n_cpu = 0; + FILE *f = fopen("/proc/cpuinfo","r"); + if (f) + { + while (fgets(buf,sizeof(buf),f)) + if ( !memcmp(buf,"processor",9) && buf[9] <= ' ' ) + n_cpu++; + fclose(f); + } + + clock_ticks = sysconf(_SC_CLK_TCK); + if ( clock_ticks <= 0 ) + clock_ticks = 100; + } + + + //--- retrieve cpu data + + CpuStatus_t current = + { + .valid = true, + .nsec = GetTimerNSec(), + .n_cpu = n_cpu, + .clock_ticks = clock_ticks + }; + + + // man 5 proc | oo +/proc/loadavg + FILE *f_load = fopen("/proc/loadavg","r"); + if (f_load) + { + if (fgets(buf,sizeof(buf),f_load)) + { + char *ptr = buf; + current.loadavg_1m = strtod(ptr,&ptr); + current.loadavg_5m = strtod(ptr,&ptr); + current.loadavg_15m = strtod(ptr,&ptr); + + current.running_proc = strtol(ptr,&ptr,10); + current.total_proc = strtol(ptr+1,&ptr,10); + } + fclose(f_load); + } + + + // man 5 proc | oo +/proc/stat + FILE * f_stat = fopen("/proc/stat","r"); + if (f_stat) + { + while (fgets(buf,sizeof(buf),f_stat)) + if ( !memcmp(buf,"cpu ",4) ) + { + char *ptr = buf; + current.user = strtol(ptr+4,&ptr,10) * 10000 / clock_ticks; + current.nice = strtol(ptr,&ptr,10) * 10000 / clock_ticks; + current.system = strtol(ptr,&ptr,10) * 10000 / clock_ticks; + current.idle = strtol(ptr,&ptr,10) * 10000 / clock_ticks; + break; + } + fclose(f_stat); + } + + if (!prev_cpuinfo) + return current; + + + //--- get delta for result + + if (!prev_cpuinfo->valid) + *prev_cpuinfo = current; + + CpuStatus_t res = current; + res.nsec -= prev_cpuinfo->nsec; + res.user -= prev_cpuinfo->user; + res.nice -= prev_cpuinfo->nice; + res.system -= prev_cpuinfo->system; + res.idle -= prev_cpuinfo->idle; + + *prev_cpuinfo = current; + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintCpuStatus ( const CpuStatus_t * cpuinfo ) +{ + if ( !cpuinfo || !cpuinfo->valid ) + return "INVALID"; + + return PrintCircBuf( + "ticks=%d, cpus=%d, times=%lld+%lld+%lld+%lld=%lld, avg=%4.2f,%4.2f,%4.2f %u/%u, nsec=%llu", + cpuinfo->clock_ticks, + cpuinfo->n_cpu, + cpuinfo->user, cpuinfo->nice, cpuinfo->system, cpuinfo->idle, + cpuinfo->user + cpuinfo->nice + cpuinfo->system + cpuinfo->idle, + cpuinfo->loadavg[0], cpuinfo->loadavg[1], cpuinfo->loadavg[2], + cpuinfo->running_proc, cpuinfo->total_proc, + cpuinfo->nsec ); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MemoryStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +MemoryStatus_t GetMemoryStatus(void) +{ + MemoryStatus_t mem = {0}; + + FILE *f = fopen("/proc/meminfo","r"); + if (f) + { + char buf[200]; + int count = 5; // abort, if all 5 params scanned + + while ( count > 0 && fgets(buf,sizeof(buf),f) ) + { + ccp colon = strchr(buf,':'); + if (colon) + { + u64 num = strtoul(colon+1,0,10) * 1024; + switch (colon-buf) + { + case 6: + if (!memcmp(buf,"Cached",6)) { count--; mem.cached = num; break; } + break; + + case 7: + if (!memcmp(buf,"MemFree",7)) { count--; mem.free = num; break; } + if (!memcmp(buf,"Buffers",7)) { count--; mem.buffers = num; break; } + break; + + case 8: + if (!memcmp(buf,"MemTotal",8)) { count--; mem.total = num; break; } + break; + + case 12: + if (!memcmp(buf,"MemAvailable",12)) { count--; mem.avail = num; break; } + break; + } + } + } + fclose(f); + mem.used = mem.total - mem.free - mem.buffers - mem.cached; + } + return mem; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintMemoryStatus ( const MemoryStatus_t * ms ) +{ + MemoryStatus_t temp; + if (!ms) + { + temp = GetMemoryStatus(); + ms = &temp; + } + + const sizeform_mode_t mode = DC_SFORM_NARROW; + return PrintCircBuf( + "tot=%s, free=%s, avail=%s, used=%s, buf=%s, cache=%s\n", + PrintNumberU7(0,0,ms->total,mode), + PrintNumberU7(0,0,ms->free,mode), + PrintNumberU7(0,0,ms->avail,mode), + PrintNumberU7(0,0,ms->used,mode), + PrintNumberU7(0,0,ms->buffers,mode), + PrintNumberU7(0,0,ms->cached,mode) ); +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-file.h b/project/dclib/dclib-file.h index 041929d..f9dd2fc 100644 --- a/project/dclib/dclib-file.h +++ b/project/dclib/dclib-file.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -44,6 +44,63 @@ // /////////////////////////////////////////////////////////////////////////////// +/////////////// log helpers /////////////// +/////////////////////////////////////////////////////////////////////////////// + +#define TRY_OPEN_FILE ((FILE*)1) +#define SETUP_TRY_OPEN_FILE(f,fname,mode) \ + static FILE *f = TRY_OPEN_FILE; f = TryOpenFile(f,fname,mode) + +FILE * TryOpenFile ( FILE *f, ccp fname, ccp mode ); + +//----------------------------------------------------------------------------- +// [[TimestampMode_t] + +typedef enum TimestampMode_t +{ + TSM_OFF, // no timestamp + TSM_SEC, // timstamp HH:MM:SS + TSM_MSEC, // timstamp HH:MM:SS.123 + TSM_USEC, // timstamp HH:MM:SS.123456 +} +TimestampMode_t; + +//----------------------------------------------------------------------------- +// [[LogFile_t] + +typedef struct LogFile_t +{ + FILE *log; // NULL or log file + mem_t tag; // A string to print before message + TimestampMode_t ts_mode; // How to print the timestamp + bool flush; // TRUE: fflush() for each log +} +LogFile_t; + +//----------------------------------------------------------------------------- + +extern LogFile_t GlobalLogFile; + +int GetLogTimestamp ( char *buf, uint buf_size, TimestampMode_t ts_mode ); +int PrintLogTimestamp ( LogFile_t *lf ); + +int PutLogFile ( LogFile_t *lf, ccp text, int text_len ); +int PrintArgLogFile ( LogFile_t *lf, ccp format, va_list arg ); +int PrintLogFile ( LogFile_t *lf, ccp format, ... ) + __attribute__ ((__format__(__printf__,2,3))); + +//----------------------------------------------------------------------------- + +exmem_t SearchToolByPATH ( ccp tool ); +exmem_t SearchToolByList ( ccp * list, int max ); // max<0: NULL is list terminator + +FILE * OpenPipeToPager(); +void ClosePagerFile(); +bool StdoutToPager(); +void CloseStdoutToPager(); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// enum FileMode_t /////////////// /////////////////////////////////////////////////////////////////////////////// // [[FileMode_t]] @@ -750,7 +807,6 @@ typedef struct TraceLog_t ccp fname; // name of file to open FILE *log; // NULL or open log file int level; // log level, don't log if <0 - } TraceLog_t; @@ -892,6 +948,7 @@ int ExistDirectory bool IsSameFilename ( ccp fn1, ccp fn2 ); /////////////////////////////////////////////////////////////////////////////// +// [[FindConfigFile_t]] typedef enum FindConfigFile_t { @@ -1008,11 +1065,17 @@ uint NumberedFilename #endif +/////////////////////////////////////////////////////////////////////////////// + +// return a string of the internal cache +// if ret_level: store level here. +ccp GetBlockDeviceHolder ( ccp name, ccp sep, int *ret_level ); + // /////////////////////////////////////////////////////////////////////////////// /////////////// normalize filenames /////////////// /////////////////////////////////////////////////////////////////////////////// -// [[trailing_slash]] +// [[trailing_slash]] typedef enum trailing_slash { @@ -1180,8 +1243,11 @@ typedef struct FDList_t { bool use_poll; // false: use select(), true: use poll() - u64 now_usec; // set on Clear() and Wait(), result of GetTimeUSec(false) - u64 timeout_usec; // next timeout, based on GetTimeUSec(false) + u_usec_t now_usec; // set on Clear() and Wait(), result of GetTimeUSec(false) + u_usec_t timeout_usec; // next timeout, based on GetTimeUSec(false) (TIME!) + u_nsec_t timeout_nsec; // next timeout, based on GetTimerNSec() (TIMER!) + u_usec_t min_wait_usec; // if >0: minimal waiting time (increment 'timeout_usec') + //--- select() params @@ -1198,11 +1264,12 @@ typedef struct FDList_t //--- statistics + FILE *debug_file; // not NULL: print debug line each select() and poll() uint n_sock; // current number of registered sockets // equals poll_used if poll is used uint wait_count; // total number of waits - u64 wait_usec; // total wait time in usec - u64 cur_usec; // current wait time in usec + u_usec_t wait_usec; // total wait time in usec + u_usec_t last_wait_usec; // last wait time in usec } FDList_t; @@ -1222,14 +1289,14 @@ uint AddFDList ( // returns the pool-index if available, ~0 otherwise - FDList_t *fdl, // valid socket list - int sock, // socket to add - uint events // bit field: POLLIN|POLLOUT|POLLERR + FDList_t *fdl, // valid socket list + int sock, // socket to add + uint events // bit field: POLLIN|POLLPRI|POLLOUT|POLLRDHUP|... ); uint GetEventFDList ( - // returns bit field: POLLIN|POLLOUT|POLLERR + // returns bit field: POLLIN|POLLOUT|POLLERR|... FDList_t *fdl, // valid socket list int sock, // socket to look for @@ -1506,7 +1573,7 @@ typedef struct PrintScript_t ccp var_name; // NULL or variable name for result ccp var_prefix; // NULL or prefix for member names int var_size; // if > 0: print size of var name (some 'fform' only) - int eq_tabstop; // if > 0: indent '=' to set tabstop (some 'fform' only) + int eq_tabstop; // if > 0: indent '=' to set tabstop (some 'fform' only) LowerUpper_t force_case; // change case of var names if not LOUP_AUTO bool create_array; // true: create ar arrays bool auto_quote; // true: detect quotes and use EscapeString() @@ -1552,6 +1619,75 @@ __attribute__ ((__format__(__printf__,3,4))); // /////////////////////////////////////////////////////////////////////////////// +/////////////// CpuStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[CpuStatus_t]] + +typedef struct CpuStatus_t +{ + bool valid; // TRUE: data valid (setup done) + u_nsec_t nsec; // GetTimerNSec() + int clock_ticks; // number of clock ticks per second + int n_cpu; // total number of cpus + + // all times in 1e4 ticks per second (in 0.01%) + u64 user; // time spent in user mode. + u64 nice; // time spent in user mode with low priority. + u64 system; // time spent in system mode. + u64 idle; // time spent in the idle task. + + int running_proc; // currently runnable processes + int total_proc; // currently total processes + + union + { + struct + { + double loadavg_1m; // load average last minute + double loadavg_5m; // load average last 5 minutes + double loadavg_15m; // load average last 12 minutes + }; + + double loadavg[3]; // load average as array + }; +} +CpuStatus_t; + +//----------------------------------------------------------------------------- + +// prev_cpuinfo == NULL: get absolute values +// prev_cpuinfo != NULL: update 'prev_cpuinfo' and get delta +CpuStatus_t GetCpuStatus ( CpuStatus_t * prev_cpuinfo ); + +ccp PrintCpuStatus ( const CpuStatus_t * cpuinfo ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// MemoryStatus_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[MemoryStatus_t]] + +typedef struct MemoryStatus_t +{ + // man 5 proc | oo +//proc/meminfo + + u64 total; // Total usable RAM. + u64 free; // The sum of LowFree + HighFree. + u64 avail; // An estimate of available memory for starting new applications. + u64 buffers; // Relatively temporary storage. + u64 cached; // In-memory cache for files read from the disk. + + u64 used; // = total - free - buffers - cached; +} +MemoryStatus_t; + +//----------------------------------------------------------------------------- + +MemoryStatus_t GetMemoryStatus(void); +ccp PrintMemoryStatus ( const MemoryStatus_t * ms ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// E N D /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-gen-ui.h b/project/dclib/dclib-gen-ui.h index 81314fa..ed97cd4 100644 --- a/project/dclib/dclib-gen-ui.h +++ b/project/dclib/dclib-gen-ui.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-gen-ui.inc b/project/dclib/dclib-gen-ui.inc index 4cbc594..0ac5694 100644 --- a/project/dclib/dclib-gen-ui.inc +++ b/project/dclib/dclib-gen-ui.inc @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-mysql.c b/project/dclib/dclib-mysql.c index a378c7d..e83838a 100644 --- a/project/dclib/dclib-mysql.c +++ b/project/dclib/dclib-mysql.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -35,16 +35,126 @@ #define _GNU_SOURCE 1 #include <string.h> +#include <stddef.h> #include <mysql/mysql.h> #include <mysql/errmsg.h> #include "dclib-mysql.h" +#include "dclib-file.h" #include "dclib-network.h" // http://dev.mysql.com/doc/refman/5.1/en/c-api-functions.html // /////////////////////////////////////////////////////////////////////////////// +/////////////// global vars /////////////// +/////////////////////////////////////////////////////////////////////////////// + +UsageDuration_t mysql_query_usage = { .par.used = 0, .par.enabled = true }; + +const UsageCtrl_t mysql_query_usage_ctrl = +{ + .par = &mysql_query_usage.par, + .ud = &mysql_query_usage, + .title = "Database queries", + .key1 = "DB", + .key2 = "MYSQL", + .srt_prefix = "my-q-", +}; + +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_Mysql[] = +{ + #undef SRT_NAME + #define SRT_NAME MySql_t + + DEF_SRT_COMMENT("mysql stats"), + DEF_SRT_SEPARATOR(), + + DEF_SRT_MODE_ADD(), + DEF_SRT_UINT( total_connect_count, "mystat-connect" ), + DEF_SRT_UINT( total_query_count, "mystat-query-c" ), + DEF_SRT_UINT( total_query_size, "mystat-query-s" ), + DEF_SRT_UINT( total_result_count, "mystat-result" ), + DEF_SRT_UINT( total_row_count, "mystat-row" ), + DEF_SRT_UINT( total_field_count, "mystat-field-c" ), + DEF_SRT_UINT( total_field_size, "mystat-field-s" ), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// watch long queries /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp wq_fname = 0; // NULL or opened file +static LogFile_t wq_log = {0}; // log data +static bool wq_failed = false; // true, if fopen() faild => don't try again + +static u_nsec_t wq_nsec = M1(wq_nsec); // >0: log queries that need ≥# nsec +static uint wq_size = M1(wq_size); // >0: log queries with ≥# bytes + +/////////////////////////////////////////////////////////////////////////////// + +void SetupQueryLogMYSQL +( + ccp fname, // filename of log file, openend with mode "a" + ccp tag, // identification of the tool for shared log files + u_nsec_t nsec, // >0: log queries that need e# nsec + uint size // >0: log queries that are longer e# bytes +) +{ + CloseQueryLogMYSQL(); + + wq_fname = fname; + wq_failed = false; + wq_nsec = nsec ? nsec : M1(wq_nsec); + wq_size = size ? size : M1(wq_size); + + wq_log.tag = MemByString0(tag); + wq_log.ts_mode = TSM_MSEC; + wq_log.flush = true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CloseQueryLogMYSQL() +{ + if (wq_log.log) + { + fclose(wq_log.log); + wq_log.log = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +LogFile_t * OpenQueryLogMYSQL() +{ + if (!wq_log.log) + { + if ( wq_failed || !wq_fname || !*wq_fname ) + { + wq_failed = true; + return 0; + } + + wq_log.log = fopen(wq_fname,"ab"); + if (!wq_log.log) + { + wq_failed = true; + return 0; + } + } + + return &wq_log; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct MySql_t /////////////// /////////////////////////////////////////////////////////////////////////////// // management @@ -71,6 +181,8 @@ void InitializeMYSQL ( MySql_t *my ) my->auto_reconnect = true; my->prefix = EmptyString; InitializeStatusMYSQL(&my->status); + + AddToUsageMgr(&mysql_query_usage_ctrl); } /////////////////////////////////////////////////////////////////////////////// @@ -266,6 +378,52 @@ static enumError CheckOpenMYSQL ( MySql_t *my, uint loglevel ) /////////////////////////////////////////////////////////////////////////////// // queries +static void update_query_duration + ( MySql_t *my, u_nsec_t start_time, ccp query, uint qlen ) +{ + DASSERT(my); + const u_usec_t dur_nsec = GetTimerNSec() - start_time; + my->last_duration_usec = dur_nsec / NSEC_PER_USEC; + if ( my->max_duration_usec < my->last_duration_usec ) + my->max_duration_usec = my->last_duration_usec; + my->total_duration_usec += my->last_duration_usec; + + UpdateUsageDurationIncrement(&mysql_query_usage,dur_nsec); + + if ( dur_nsec >= wq_nsec || qlen >= wq_size ) + { + LogFile_t *lf = OpenQueryLogMYSQL(); + if (lf) + { + char buf[2000], *dest = buf; + bool have_space = false; + while ( *query && dest < buf+sizeof(buf)-2 ) + { + const uchar ch = *query++; + if ( ch > ' ' ) + { + if (have_space) + { + have_space = false; + *dest++ = ' '; + } + *dest++ = ch; + } + else if ( dest > buf ) + have_space = true; + } + *dest = 0; + + PrintLogFile(lf,"%5u %s : %s\n", + qlen, + PrintTimerNSec6(0,0,dur_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH), + buf ); + } + } +} + +//----------------------------------------------------------------------------- + enumError DoQueryMYSQL ( MySql_t *my, // valid struct @@ -277,7 +435,7 @@ enumError DoQueryMYSQL DASSERT(my); DASSERT(query); - const u64 start_time = GetTimeUSec(false); + const u_nsec_t start_time = GetTimerNSec(); enumError err = CheckOpenMYSQL(my,loglevel); if (err) @@ -319,10 +477,7 @@ enumError DoQueryMYSQL ERROR0(ERR_DATABASE,"%s (#%d)\n", my->status.message, my->status.status ); - my->last_duration_usec = GetTimeUSec(false) - start_time; - if ( my->max_duration_usec < my->last_duration_usec ) - my->max_duration_usec = my->last_duration_usec; - my->total_duration_usec += my->last_duration_usec; + update_query_duration(my,start_time,query,query_length); return ERR_DATABASE; } } @@ -332,11 +487,7 @@ enumError DoQueryMYSQL my->field_count = mysql_field_count(my->mysql); ClearStatusMYSQL(&my->status); - - my->last_duration_usec = GetTimeUSec(false) - start_time; - if ( my->max_duration_usec < my->last_duration_usec ) - my->max_duration_usec = my->last_duration_usec; - my->total_duration_usec += my->last_duration_usec; + update_query_duration(my,start_time,query,query_length); return ERR_OK; } @@ -591,6 +742,7 @@ s64 FetchIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number s64 return_default // return this value on non existent result ) { @@ -602,7 +754,7 @@ s64 FetchIntMYSQL char *end; const s64 val = strtoll(res.ptr,&end,10); FreeMemMYSQL(my,res); - return end == res.ptr + res.len ? val : return_default; + return allow_trail || end == res.ptr + res.len ? val : return_default; } /////////////////////////////////////////////////////////////////////////////// @@ -611,6 +763,7 @@ u64 FetchUIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number u64 return_default // return this value on non existent result ) { @@ -622,7 +775,7 @@ u64 FetchUIntMYSQL char *end; const u64 val = strtoull(res.ptr,&end,10); FreeMemMYSQL(my,res); - return end == res.ptr + res.len ? val : return_default; + return allow_trail || end == res.ptr + res.len ? val : return_default; } // @@ -686,6 +839,7 @@ s64 PrintFetchIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number s64 return_default, // return this value on non existent result ccp format, // format string for vsnprintf() ... // parameters for 'format' @@ -705,7 +859,7 @@ s64 PrintFetchIntMYSQL char *end; const s64 val = strtoll(res.ptr,&end,10); FreeMemMYSQL(my,res); - return end == res.ptr + res.len ? val : return_default; + return allow_trail || end == res.ptr + res.len ? val : return_default; } /////////////////////////////////////////////////////////////////////////////// @@ -714,6 +868,7 @@ u64 PrintFetchUIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number u64 return_default, // return this value on non existent result ccp format, // format string for vsnprintf() ... // parameters for 'format' @@ -733,7 +888,7 @@ u64 PrintFetchUIntMYSQL char *end; const u64 val = strtoull(res.ptr,&end,10); FreeMemMYSQL(my,res); - return end == res.ptr + res.len ? val : return_default; + return allow_trail || end == res.ptr + res.len ? val : return_default; } // @@ -865,7 +1020,7 @@ int CountTablesMYSQL mem_t db = EscapeStringMYSQL(my, database && *database ? database : my->database ); mem_t tab = EscapeStringMYSQL(my, table && *table ? table : "%" ); - stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,false,-1, "SELECT COUNT(DISTINCT TABLE_NAME) FROM information_schema.COLUMNS\n" "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"\n", db.ptr, tab.ptr ); @@ -894,7 +1049,7 @@ int CountColumnsMYSQL if ( column && *column ) { mem_t col = EscapeStringMYSQL(my,column); - stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,false,-1, "SELECT COUNT(*) FROM information_schema.COLUMNS\n" "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"" " && COLUMN_NAME = \"%s\"\n", @@ -903,7 +1058,7 @@ int CountColumnsMYSQL } else { - stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,-1, + stat = PrintFetchIntMYSQL(my,MYLL_QUERY_ON_ERROR,false,-1, "SELECT COUNT(*) FROM information_schema.COLUMNS\n" "WHERE TABLE_SCHEMA = \"%s\" && TABLE_NAME like \"%s\"\n", db.ptr, tab.ptr ); diff --git a/project/dclib/dclib-mysql.h b/project/dclib/dclib-mysql.h index c251fda..8ae7e2c 100644 --- a/project/dclib/dclib-mysql.h +++ b/project/dclib/dclib-mysql.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -66,6 +66,22 @@ LogLevelMYSQL; // /////////////////////////////////////////////////////////////////////////////// +/////////////// watch long queries /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetupQueryLogMYSQL +( + ccp fname, // filename of log file, openend with mode "a" + ccp tag, // identification of the tool for shared log files + u_nsec_t nsec, // >0: log queries that need ≥# nsec + uint size // >0: log queries that are longer ≥# bytes +); + +void CloseQueryLogMYSQL(void); +struct LogFile_t * OpenQueryLogMYSQL(void); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct MySqlStatus_t /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -142,7 +158,7 @@ typedef struct MySql_t //--- statistics - uint total_connect_count; // total number of connections (usualy 1) + uint total_connect_count; // total number of connections (usually 1) uint total_query_count; // total number of queries u64 total_query_size; // total size of all queries uint total_result_count; // total number of fetched results @@ -158,6 +174,8 @@ typedef struct MySql_t } MySql_t; +extern const SaveRestoreTab_t SRT_Mysql[]; + // /////////////////////////////////////////////////////////////////////////////// /////////////// interface for MySql_t /////////////// @@ -264,6 +282,7 @@ s64 FetchIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number s64 return_default // return this value on non existent result ); @@ -271,6 +290,7 @@ u64 FetchUIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number u64 return_default // return this value on non existent result ); @@ -302,21 +322,23 @@ s64 PrintFetchIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number s64 return_default, // return this value on non existent result ccp format, // format string for vsnprintf() ... // parameters for 'format' ) -__attribute__ ((__format__(__printf__,4,5))); +__attribute__ ((__format__(__printf__,5,6))); u64 PrintFetchUIntMYSQL ( MySql_t *my, // valid struct LogLevelMYSQL loglevel, // log level for mysql errors and queries + bool allow_trail, // allow additional chars behind number u64 return_default, // return this value on non existent result ccp format, // format string for vsnprintf() ... // parameters for 'format' ) -__attribute__ ((__format__(__printf__,4,5))); +__attribute__ ((__format__(__printf__,5,6))); //----------------------------------------------------------------------------- // fetch single row @@ -535,6 +557,10 @@ MySqlServerStats_t * Max2MySqlServerStats const MySqlServerStats_t *src // NULL or source ); +/////////////////////////////////////////////////////////////////////////////// + +extern UsageDuration_t mysql_query_usage; + // /////////////////////////////////////////////////////////////////////////////// /////////////// E N D /////////////// diff --git a/project/dclib/dclib-network-linux.c b/project/dclib/dclib-network-linux.c index 9116588..dc6c32b 100644 --- a/project/dclib/dclib-network-linux.c +++ b/project/dclib/dclib-network-linux.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -148,7 +148,7 @@ ssize_t SendUDPv4 struct in_pktinfo *pki = (struct in_pktinfo*) CMSG_DATA(cp); pki->ipi_ifindex = 0; pki->ipi_spec_dst.s_addr = src_addr; - + return sendmsg(sock,&msg,flags); } diff --git a/project/dclib/dclib-network-linux.h b/project/dclib/dclib-network-linux.h index 9366191..1760bde 100644 --- a/project/dclib/dclib-network-linux.h +++ b/project/dclib/dclib-network-linux.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-network.c b/project/dclib/dclib-network.c index 6e1e542..76390e3 100644 --- a/project/dclib/dclib-network.c +++ b/project/dclib/dclib-network.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -47,8 +47,11 @@ #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> +#include <stddef.h> +#include <ifaddrs.h> #include "dclib-network.h" +#include "dclib-punycode.h" /////////////////////////////////////////////////////////////////////////////// @@ -239,6 +242,1431 @@ bool ResolveHostMem // /////////////////////////////////////////////////////////////////////////////// +/////////////// enum PrintModeIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +PrintModeIP_t ScanPrintModeIP ( PrintModeIP_t base, ccp arg ) +{ + if (arg) + { + bool neg = false; + while (*arg) + { + PrintModeIP_t val = 0; + PrintModeIP_t mask = 0; + switch(*arg++) + { + case '-': neg = !neg; break; + + case 'n': val = PMIP_NEVER; mask = PMIP_M_BITS; break; + case 'r': val = PMIP_RELEVANT; mask = PMIP_M_BITS; break; + case 'i': val = PMIP_IF_SET; mask = PMIP_M_BITS; break; + case 'a': val = PMIP_ALWAYS; mask = PMIP_M_BITS; break; + + case 'p': val = PMIP_PORT; break; + case 's': val = PMIP_SERVICE; break; + case 'm': val = PMIP_MASK; break; + case 'b': val = PMIP_BRACKETS; break; + case 'f': val = PMIP_FULL_IPV6; break; + + case '0': val = PMIP_0DOTS; mask = PMIP_M_DOTS; break; + case '1': val = PMIP_1DOT; mask = PMIP_M_DOTS; break; + case '2': val = PMIP_2DOTS; mask = PMIP_M_DOTS; break; + case '3': val = PMIP_3DOTS; mask = PMIP_M_DOTS; break; + } + + if (mask) // ignore 'neg' + { + base = base & ~mask | val; + neg = false; + } + else if (val) + { + if (neg) + base &= ~val; + else + base |= val; + neg = false; + } + } + } + + return base; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintPrintModeIP ( PrintModeIP_t val, bool align ) +{ + char buf[20], *dest = buf; + + DASSERT( PMIP_M_BITS == 3 ); + *dest++ = "nria"[ val & PMIP_M_BITS ]; + + if ( val & PMIP_PORT ) *dest++ = 'p'; else if (align) *dest++ = '-'; + if ( val & PMIP_SERVICE ) *dest++ = 's'; else if (align) *dest++ = '-'; + if ( val & PMIP_MASK ) *dest++ = 'm'; else if (align) *dest++ = '-'; + if ( val & PMIP_BRACKETS ) *dest++ = 'b'; else if (align) *dest++ = '-'; + if ( val & PMIP_FULL_IPV6 ) *dest++ = 'f'; else if (align) *dest++ = '-'; + + if ( val & PMIP_0DOTS ) + *dest++ = '0'; + else if ( val & PMIP_1DOT ) + *dest++ = '1'; + else if ( val & PMIP_2DOTS ) + *dest++ = '2'; + else if (align) + *dest++ = '3'; + + DASSERT( dest < buf+sizeof(buf) ); + return CopyCircBuf0(buf,dest-buf); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum IPClass_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetIPClassColor ( const ColorSet_t *colset, IPClass_t ipc ) +{ + if ( !colset || !colset->colorize ) + return ""; + + switch (ipc) + { + case IPCL_INVALID: return colset->b_red; + case IPCL_LOOPBACK: return colset->b_cyan; + case IPCL_LOCAL: return colset->b_green; + case IPCL_STANDARD: return colset->reset; + case IPCL_SPECIAL: return colset->b_yellow; + default: return colset->b_magenta; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct SplitIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +bool SplitIP ( SplitIP_t *sip, ccp addr ) +{ + DASSERT(sip); + memset(sip,0,sizeof(*sip)); + if (!addr) + return false; + + while ( isblank((int)*addr) ) + addr++; + + int bits = -1; + ccp slash = strchr(addr,'/'); + if (slash) + { + if (strchr(slash,'.')) + { + // is there a netmask? + struct in_addr inp; + if (inet_aton(slash+1,&inp)) + { + const int bits2 = Count1Bits32(inp.s_addr); + const ipv4_t mask = GetIP4Mask(bits2); + if ( mask == inp.s_addr ) + bits = bits2; + } + } + + if ( bits < 0 ) + { + const int val = strtol(slash+1,0,10); + if ( val >= 0 && val <= 128 ) + bits = val; + } + } + + ccp have_port = 0; + if ( *addr == '[' ) + { + ccp close = strchr(++addr,']'); + if (!close) + return false; + + StringCopySM(sip->name,sizeof(sip->name),addr,close-addr); + if ( close[1] == ':' ) + have_port = close+2; + } + else + { + char *end_name = slash + ? StringCopySM(sip->name,sizeof(sip->name),addr,slash-addr) + : StringCopyS(sip->name,sizeof(sip->name),addr); + + if ( end_name > sip->name && end_name[-1] == '.' ) + { + // remove trailing point if not a valid IPv4 (domains only) + if ( CheckIP4(sip->name,end_name-sip->name) <= CIP4_INVALID ) + *--end_name = 0; + } + + ccp point = strchr(sip->name,'.'); + if (point) + { + char *colon = strchr(point,':'); + if (colon) + { + *colon++ = 0; + have_port = colon; + } + } + } + + if (!*sip->name) + return false; + + if (have_port) + { + if ( *have_port >= '0' && *have_port <= '9' ) + { + const uint port = strtol(have_port,0,10); + if ( port <= 0xffff ) + sip->port = port; + } + else + { + char serv[100]; + if (slash) + { + StringCopySM(serv,sizeof(serv),have_port,slash-have_port); + have_port = serv; + } + + struct servent result_buf; + struct servent *result = 0; + char buf[1000]; + if ( !getservbyname_r(have_port,0,&result_buf,buf,sizeof(buf),&result) && result ) + sip->port = ntohs(result->s_port); + } + } + + sip->bits = bits; + return true; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct NamesIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +struct ip_names_list_t +{ + int offset; + char name[8]; +}; + +//----------------------------------------------------------------------------- + +#undef RECORD +#define RECORD(v) { offsetof(NamesIP_t,v), #v }, + +static const struct ip_names_list_t ip_names_list[] = +{ + RECORD(binaddr) + RECORD(ipvers) + RECORD(addr1) + RECORD(addr2) + RECORD(bits) + RECORD(mask1) + RECORD(mask2) + {-1,""} +}; + +#undef RECORD + +#undef GET_NAME_IP_PTR +#define GET_NAME_IP_PTR(nip,offset) ((ccp*)( (char*)(nip)+offset )) + +/////////////////////////////////////////////////////////////////////////////// + +void SetupNamesIP ( NamesIP_t *dest, const NamesIP_t *src ) +{ + if (!dest) + return; + + if (src) + { + for ( const struct ip_names_list_t *in = ip_names_list; in->offset >= 0; in++ ) + { + ccp *s = GET_NAME_IP_PTR(src,in->offset); + ccp *d = GET_NAME_IP_PTR(dest,in->offset); + *d = *s && **s ? *s : in->name; + } + } + else + { + for ( const struct ip_names_list_t *in = ip_names_list; in->offset >= 0; in++ ) + { + ccp *d = GET_NAME_IP_PTR(dest,in->offset); + *d = in->name; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetupNamesByListIP ( NamesIP_t *dest, const exmem_list_t *src ) +{ + if (!dest) + return; + + // [[2do]] ??? qqq +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNamesIP ( const NamesIP_t *nip ) +{ + return nip + ? PrintCircBuf("ba=%s, iv=%s, a1=%s, a2=%s, b=%s, m1=%s, m2=%s\n", + nip->binaddr, nip->ipvers, nip->addr1, nip->addr2, + nip->bits, nip->mask1, nip->mask2 ) + : 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct BinIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +BinIP_t GetIPMask ( const BinIP_t *src ) +{ + BinIP_t res = {0}; + if (src) + { + if ( src->ipvers == 4 ) + res.ip4 = GetIP4Mask(src->bits); + else if ( src->ipvers == 6 ) + res.ip6 = GetIP6Mask(src->bits); + else + goto abort; + + res.ipvers = src->ipvers; + res.bits = src->bits; + } + + abort: + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ApplyIPMask ( BinIP_t *bip ) +{ + if (bip) + { + BinIP_t mask = GetIPMask(bip); + bip->ip6_64[0] &= mask.ip6_64[0]; + bip->ip6_64[1] &= mask.ip6_64[1]; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +IPClass_t GetIPClassBIP ( const BinIP_t * bip ) +{ + struct ip4_tab + { + ipv4_t addr; + ipv4_t mask; + IPClass_t ipc; + }; + + static const struct ip4_tab ip4_tab[] = + { + { 0x00000000, 0xffffffff, IPCL_INVALID }, // 0.0.0.0 + { 0x7f000000, 0xff000000, IPCL_LOOPBACK }, // 127.0.0.0/8 + { 0x0a000000, 0xff000000, IPCL_LOCAL }, // 10.0.0.0/8 + { 0xac100000, 0xfff00000, IPCL_LOCAL }, // 172.16.0.0/12 + { 0xc0a80000, 0xffff0000, IPCL_LOCAL }, // 192.168.0.0/16 + { 0xa9fe0000, 0xffff0000, IPCL_LOCAL }, // 169.254.0.0/16 + { 0xe0000000, 0xe0000000, IPCL_SPECIAL }, // 224.0.0.0/3 + { 0,0,IPCL__N }, + }; + + struct ip6_tab + { + IPClass_t ipc; + ccp name; + ipv6_t addr; + ipv6_t mask; + }; + + static struct ip6_tab ip6_tab[] = + { + { IPCL_INVALID, "::" }, + { IPCL_LOOPBACK, "::1" }, + { IPCL_LOCAL, "fe80::/10" }, + { IPCL_SPECIAL, "f00::/8" }, + { 0,0 }, + }; + + if (bip) + { + if ( bip->ipvers == 4 ) + { + const ipv4_t addr = ntohl(bip->ip4); + for ( const struct ip4_tab *ptr = ip4_tab; ptr->ipc < IPCL__N; ptr++ ) + if ( ( addr & ptr->mask ) == ptr->addr ) + return ptr->ipc; + return IPCL_STANDARD; + } + else if ( bip->ipvers == 6 ) + { + ipv6_t addr; + memcpy(&addr,bip->ip6_64,sizeof(addr)); + PRINT0("> %016llx.%016llx\n",ntoh64(addr.v64[0]),ntoh64(addr.v64[1])); + + static bool init_done = false; + if (!init_done) + { + init_done = true; + ManageIP_t mip = {{0}}; + for ( struct ip6_tab *ptr = ip6_tab; ptr->name; ptr++ ) + { + ScanMIP(&mip,ptr->name); + memcpy(&ptr->addr,mip.bin.ip6_64,sizeof(ptr->addr)); + ptr->mask = GetIP6Mask(mip.bin.bits); + } + ResetMIP(&mip); + } + + for ( struct ip6_tab *ptr = ip6_tab; ptr->name; ptr++ ) + { + PRINT0(" %016llx.%016llx / %016llx.%016llx : %s\n", + ntoh64(ptr->addr.v64[0]), ntoh64(ptr->addr.v64[1]), + ntoh64(ptr->mask.v64[0]), ntoh64(ptr->mask.v64[1]), + ptr->name ); + + if ( ( addr.v64[0] & ptr->mask.v64[0] ) == ptr->addr.v64[0] + && ( addr.v64[1] & ptr->mask.v64[1] ) == ptr->addr.v64[1] ) + { + return ptr->ipc; + } + } + return IPCL_STANDARD; + } + } + + return IPCL_INVALID; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintBIP ( const BinIP_t * bip ) +{ + ccp res; + int bitlen = 32; + char buf[INET6_ADDRSTRLEN+1]; + + switch ( bip ? bip->ipvers : 0 ) + { + case 4: + res = inet_ntop(AF_INET,&bip->ip4,buf,sizeof(buf)); + break; + + case 6: + res = inet_ntop(AF_INET6,bip->ip6_32,buf,sizeof(buf)); + bitlen = 128; + break; + + default: + res = 0; + break; + } + + if (!res) + return 0; + + return bip->bits < bitlen + ? PrintCircBuf("%s/%u",res,bip->bits) + : CopyCircBuf(res,strlen(res)+1); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsSameBIP ( const BinIP_t *a1, const BinIP_t *a2, uint use_mask ) +{ + if ( a1 && a2 && a1->ipvers == a2->ipvers ) + { + int bits; + switch ( use_mask & 3 ) + { + case 1: bits = a1->bits; break; + case 2: bits = a2->bits; break; + case 3: bits = a1->bits < a2->bits ? a1->bits : a2->bits; break; + default: bits = 128; break; + } + + if ( a1->ipvers == 4 ) + { + ipv4_t mask = GetIP4Mask(bits); + return ( a1->ip4 & mask ) == ( a2->ip4 & mask ); + } + else if ( a1->ipvers == 6 ) + { + ipv6_t mask = GetIP6Mask(bits); + return !( ( a1->ip6.v64[0] ^ a2->ip6.v64[0] ) & mask.v64[0] ) + && !( ( a1->ip6.v64[1] ^ a2->ip6.v64[1] ) & mask.v64[1] ); + } + } + return false; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct BinIPList_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetBIL ( BinIPList_t *bil ) +{ + if (bil) + { + ClearBIL(bil); + FREE(bil->list); + InitializeBIL(bil); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearBIL ( BinIPList_t *bil ) +{ + if (bil) + { + BinIPItem_t *it = bil->list; + for ( int i = 0; i < bil->used; i++, it++ ) + { + FreeString(it->name); + it->name = 0; + } + bil->used = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SetSizeBIL ( BinIPList_t *bil, int size ) +{ + DASSERT(bil); + if ( size < 0 ) + size = 0; + + if ( bil->used > size ) + { + for ( int i = size; i < bil->used; i++ ) + { + BinIPItem_t *it = bil->list + i; + FreeString(it->name); + } + bil->used = size; + } + + if ( bil->size != size ) + { + bil->size = size; + bil->list = REALLOC( bil->list, size * sizeof(*bil->list) ); + } + + DASSERT( !bil->size || bil->list ); + DASSERT( bil->used <= bil->size ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SetMinSizeBIL ( BinIPList_t *bil, int min_size ) +{ + DASSERT(bil); + if ( min_size > bil->size ) + { + const int good_size = bil->size / 8 + 10; + if ( min_size < good_size ) + min_size = good_size; + SetSizeBIL(bil,min_size); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +BinIPItem_t * CreateItemBIL ( BinIPList_t *bil, int n_item ) +{ + DASSERT(bil); + if ( n_item < 0 ) + n_item = 0; + + SetMinSizeBIL(bil,bil->used+n_item); + BinIPItem_t *res = bil->list + bil->used; + if ( n_item > 0 ) + memset( res, 0, n_item * sizeof(*res) ); + bil->used += n_item; + + DASSERT( !bil->size || bil->list ); + DASSERT( bil->used <= bil->size ); + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#if 0 + +BinIPItem_t * InsertBIL ( BinIPList_t *bil, int index, ccp key, CopyMode_t copy_mode ) +{ + DASSERT(bil); + // ??? + return 0; +} + +#endif +/////////////////////////////////////////////////////////////////////////////// +#if 0 + +BinIPItem_t * InsertByKeyBIL ( BinIPList_t *bil, ccp key, CopyMode_t copy_mode ) +{ + DASSERT(bil); + // ??? + return 0; +} + +#endif +/////////////////////////////////////////////////////////////////////////////// +#if 0 + +int FindIndexBIL ( const BinIPList_t *bil, ccp key ) +{ + DASSERT(bil); + // ??? + return 0; +} + +#endif +/////////////////////////////////////////////////////////////////////////////// + +BinIPIterate_t FindAddressBIL ( const BinIPList_t *bil, BinIP_t *addr, uint use_mask ) +{ + BinIPIterate_t iter; + if ( bil && bil->used && addr && addr->ipvers ) + { + iter.valid = true; + iter.bil = bil; + iter.cur = bil->list - 1; + iter.addr = *addr; + iter.use_mask = use_mask; + FindNextAddressBIL(&iter); + } + else + memset(&iter,0,sizeof(iter)); + return iter; +} + +//----------------------------------------------------------------------------- + +void FindNextAddressBIL ( BinIPIterate_t *iter ) +{ + if ( iter && iter->valid && iter->bil && iter->cur ) + { + for ( uint idx = iter->cur + 1 - iter->bil->list; idx < iter->bil->used; idx++ ) + { + BinIPItem_t *it = iter->bil->list + idx; + if (IsSameBIP(&it->bip,&iter->addr,iter->use_mask)) + { + iter->cur = it; + return; + } + } + } + + iter->valid = false; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +BinIP_t GetBIPBySA ( sockaddr_t *addr, sockaddr_t *mask ) +{ + BinIP_t res = {0}; + if (addr) + { + if ( addr->sa_family == AF_INET ) + { + res.ipvers = 4; + res.bits = GetBitsBySA(mask,32); + res.ip4 = ((sockaddr_in4_t*)addr)->sin_addr.s_addr; + } + else if ( addr->sa_family == AF_INET6 ) + { + res.ipvers = 6; + res.bits = GetBitsBySA(mask,128); + memcpy(&res.ip6,&((sockaddr_in6_t*)addr)->sin6_addr,sizeof(res.ip6)); + } + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +uint ScanInterfaceAddresses ( BinIPList_t *bil, bool init_bil ) +{ + DASSERT(bil); + if (init_bil) + InitializeBIL(bil); + else + ClearBIL(bil); + + struct ifaddrs *ifaddr; + if (getifaddrs(&ifaddr)) + return -1; + + for ( const struct ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next ) + { + BinIP_t temp = GetBIPBySA(ifa->ifa_addr,ifa->ifa_netmask); + if (temp.ipvers) + { + BinIPItem_t *it = CreateItemBIL(bil,1); + it->name = STRDUP(ifa->ifa_name); + it->bip = temp; + } + } + freeifaddrs(ifaddr); + + return bil->used; +} + +/////////////////////////////////////////////////////////////////////////////// + +const BinIPList_t * GetInterfaceAddressList ( bool update ) +{ + static BinIPList_t *list = 0; + + if ( !list || update ) + { + bool init = !list; + if (init) + list = MALLOC(sizeof(*list)); + ScanInterfaceAddresses(list,init); + } + return list; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool IsLocalBIL ( BinIP_t *addr, uint use_mask ) +{ + const BinIPList_t *ial = GetInterfaceAddressList(false); + BinIPIterate_t iter = FindAddressBIL(ial,addr,use_mask); + return iter.valid; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ManageIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetMIP ( ManageIP_t *mip ) +{ + if (mip) + { + FreeString(mip->name); + FreeString(mip->decoded_name); + memset(mip,0,sizeof(*mip)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearNameMIP ( ManageIP_t *mip ) +{ + if (mip) + { + FreeString(mip->name); + FreeString(mip->decoded_name); + mip->name = mip->decoded_name = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +ManageIP_t GetCopyMIP ( const ManageIP_t *mip ) +{ + ManageIP_t res; + if (mip) + { + memcpy(&res,mip,sizeof(res)); + if (res.name) + res.name = STRDUP(res.name); + if (res.decoded_name) + res.decoded_name = STRDUP(res.decoded_name); + } + else + memset(&res,0,sizeof(res)); + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignMIP ( ManageIP_t *dest, const ManageIP_t *src ) +{ + if (dest) + { + ClearNameMIP(dest); + *dest = GetCopyMIP(src); + return dest->bin.ipvers != 0; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignBinMIP ( ManageIP_t *mip, cvp bin, int bits ) +{ + DASSERT(mip); + ResetMIP(mip); + if (!bin) + return false; + + const BinIP_t *bip = bin; + if ( bip->ipvers == 4 ) + mip->relevant_bits = 32; + else if ( bip->ipvers == 6 ) + mip->relevant_bits = 128; + else + return false; + + memcpy(&mip->bin,bin,sizeof(mip->bin)); + if ( bits >= 0 ) + { + mip->have_bits = true; + mip->bin.bits = bits < mip->relevant_bits ? bits : mip->relevant_bits; + } + + if ( mip->bin.bits < mip->relevant_bits ) + mip->have_bits = true; + else + mip->bin.bits = mip->relevant_bits; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool AssignSockaddrMIP ( ManageIP_t *mip, cvp sockaddr, u16 port, int bits ) +{ + DASSERT(mip); + ResetMIP(mip); + if (!sockaddr) + return false; + + const sockaddr_in46_t *sa = sockaddr; + if ( sa->family == AF_INET ) + { + mip->bin.ipvers = 4; + mip->bin.bits = 32; + mip->bin.ip4 = sa->in4.sin_addr.s_addr; + mip->port = ntohs(sa->in4.sin_port); + } + else if ( sa->family == AF_INET6 ) + { + mip->bin.ipvers = 6; + mip->bin.bits = 128; + memcpy(mip->bin.ip6_8,sa->in6.sin6_addr.s6_addr,sizeof(mip->bin.ip6_8)); + mip->port = ntohs(sa->in6.sin6_port); + } + + mip->relevant_bits = mip->bin.bits; + if ( bits >= 0 ) + { + mip->have_bits = true; + if ( mip->bin.bits > bits ) + mip->bin.bits = bits; + } + + if ( !mip->port ) + mip->port = port; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool ScanMIP ( ManageIP_t *mip, ccp addr ) +{ + DASSERT(mip); + ResetMIP(mip); + + SplitIP_t sip; + if (!SplitIP(&sip,addr)) + return false; + + if (strchr(sip.name,':')) + { + if ( inet_pton(AF_INET6,sip.name,mip->bin.ip6_32) != 1 ) + return false; + mip->bin.ipvers = 6; + mip->relevant_bits = 128; + } + else + { + struct in_addr inp; + if (!dclib_aton(sip.name,&inp)) + return false; + + mip->bin.ipvers = 4; + mip->bin.ip4 = inp.s_addr; + mip->relevant_bits = 32; + } + + mip->bin.bits = mip->relevant_bits; + if ( sip.bits >= 0 ) + { + mip->have_bits = true; + if ( sip.bits < mip->relevant_bits ) + mip->bin.bits = sip.bits; + } + + mip->port = sip.port; + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +ManageIP_t GetSockNameMIP ( int sock ) +{ + ManageIP_t mip = {{0}}; + sockaddr_in46_t sa; + socklen_t sa_len = sizeof(sa); + if (!getsockname(sock,(sockaddr_t*)&sa,&sa_len)) + AssignSockaddrMIP(&mip,&sa,0,-1); + return mip; +} + +/////////////////////////////////////////////////////////////////////////////// + +ManageIP_t GetPeerNameMIP ( int sock ) +{ + ManageIP_t mip = {{0}}; + sockaddr_in46_t sa; + socklen_t sa_len = sizeof(sa); + if (!getpeername(sock,(sockaddr_t*)&sa,&sa_len)) + AssignSockaddrMIP(&mip,&sa,0,-1); + return mip; +} + +/////////////////////////////////////////////////////////////////////////////// + +ManageIP_t GetAddrInfoMIP ( ccp addr, int protocol, IpMode_t ipm ) +{ + ManageIP_t mip = {{0}}; + + SplitIP_t sip; + if (SplitIP(&sip,addr)) + { + struct addrinfo hints + = { .ai_family = AF_UNSPEC, + .ai_protocol = protocol < 0 ? IPPROTO_TCP : protocol }; + struct addrinfo *result; + if (!getaddrinfo(sip.name,0,&hints,&result)) + { + struct addrinfo *ip4 = 0, *ip6 = 0; + for ( struct addrinfo *rp = result; rp; rp = rp->ai_next) + { + if ( !ip4 && rp->ai_family == AF_INET ) + ip4 = rp; + else if ( !ip6 && rp->ai_family == AF_INET6 ) + ip6 = rp; + } + + switch (ipm) + { + case IPV4_ONLY: break; // nothing to do + case IPV6_ONLY: ip4 = ip6; break; + case IPV4_FIRST: if (!ip4) ip4 = ip6; break; + case IPV6_FIRST: if (ip6) ip4 = ip6; break; + } + + if (ip4) + AssignSockaddrMIP(&mip,ip4->ai_addr,sip.port,sip.bits); + + freeaddrinfo(result); + } + } + + return mip; +} + +/////////////////////////////////////////////////////////////////////////////// + +sockaddr_in46_t GetSockaddrMIP ( const ManageIP_t *mip ) +{ + DASSERT(mip); + sockaddr_in46_t sa = { .family = 0 }; + + if ( mip->bin.ipvers == 4 ) + { + sa.in4.sin_family = AF_INET; + sa.in4.sin_addr.s_addr = mip->bin.ip4; + sa.in4.sin_port = htons(mip->port); + } + else if ( mip->bin.ipvers == 6 ) + { + sa.in6.sin6_family = AF_INET6; + sa.in6.sin6_port = htons(mip->port); + memcpy(sa.in6.sin6_addr.s6_addr,mip->bin.ip6_8,sizeof(sa.in6.sin6_addr)); + } + + return sa; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetNameInfoMIP ( ManageIP_t *mip ) +{ + DASSERT(mip); + if ( !mip->name && mip->bin.ipvers ) + { + char host1[NI_MAXHOST+20], host2[NI_MAXHOST+20]; + sockaddr_in46_t sa = GetSockaddrMIP(mip); + + if (!getnameinfo( (sockaddr_t*)&sa,sizeof(sa), host1,sizeof(host1), 0,0, 0 )) + { + mip->name = STRDUP(host1); + + FreeString(mip->decoded_name); + const uint stat = Domain2UTF8(host2,sizeof(host2),host1,-1); + mip->decoded_name = stat && *host2 && strcmp(host1,host2) ? STRDUP(host2) : 0; + } + } + return mip->name; +} + +/////////////////////////////////////////////////////////////////////////////// + +void ApplyMaskMIP ( ManageIP_t *mip, int bits ) +{ + DASSERT(mip); + if ( bits >= 0 && bits < mip->bin.bits ) + mip->bin.bits = bits; + ApplyIPMask(&mip->bin); +} + +/////////////////////////////////////////////////////////////////////////////// + +void MaskPrefixMIP ( ManageIP_t *mip ) +{ + DASSERT(mip); + if ( mip->bin.ipvers == 4 || mip->bin.ip6_64[0] != 0ull ) + ApplyMaskMIP(mip,64); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAddrMIP ( const ManageIP_t *mip, PrintModeIP_t pmode ) +{ + DASSERT(mip); + + ccp res; + char buf[INET6_ADDRSTRLEN+1]; + + switch (mip->bin.ipvers) + { + case 4: + res = buf; + if ( pmode & PMIP_0DOTS ) + snprintf(buf,sizeof(buf),"%u",ntohl(mip->bin.ip4)); + else if ( pmode & PMIP_1DOT ) + snprintf(buf,sizeof(buf), "%u.%u", + mip->bin.ip4x.v8[0], ntohl(mip->bin.ip4)&0xffffff ); + else if ( pmode & PMIP_2DOTS ) + snprintf(buf,sizeof(buf), "%u.%u.%u", + mip->bin.ip4x.v8[0], mip->bin.ip4x.v8[1], ntohs(mip->bin.ip4x.v16[1]) ); + else + snprintf(buf,sizeof(buf), "%u.%u.%u.%u", + mip->bin.ip4x.v8[0], mip->bin.ip4x.v8[1], + mip->bin.ip4x.v8[2], mip->bin.ip4x.v8[3] ); + break; + + case 6: + if ( pmode & PMIP_FULL_IPV6 ) + { + snprintf(buf,sizeof(buf), "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + ntohs(mip->bin.ip6.v16[0]), + ntohs(mip->bin.ip6.v16[1]), + ntohs(mip->bin.ip6.v16[2]), + ntohs(mip->bin.ip6.v16[3]), + ntohs(mip->bin.ip6.v16[4]), + ntohs(mip->bin.ip6.v16[5]), + ntohs(mip->bin.ip6.v16[6]), + ntohs(mip->bin.ip6.v16[7]) ); + res = buf; + } + else + res = inet_ntop(AF_INET6,mip->bin.ip6_32,buf,sizeof(buf)); + break; + + default: + res = 0; + } + + if (!res) + return 0; + + char port[100]; + *port = 0; + if ( mip->port && pmode & (PMIP_SERVICE|PMIP_PORT) ) + { + if ( pmode & PMIP_SERVICE ) + { + struct servent result_buf; + struct servent *result; + char buf[1000]; + const int stat = getservbyport_r(htons(mip->port),0,&result_buf,buf,sizeof(buf),&result); + if ( !stat && result ) + snprintf(port,sizeof(port),":%s",result_buf.s_name); + } + + if (!*port) + snprintf(port,sizeof(port),":%u",mip->port); + pmode |= PMIP_BRACKETS; + } + + char bits[20]; + if (PrintBitsMIP(mip,pmode)) + { + if ( mip->bin.ipvers == 4 && pmode & PMIP_MASK ) + { + ipv4x_t mask; + mask.ip4 = GetIP4Mask(mip->bin.bits); + snprintf(bits,sizeof(bits),"/%d.%d.%d.%d", + mask.v8[0], mask.v8[1], mask.v8[2], mask.v8[3] ); + } + else + snprintf(bits,sizeof(bits),"/%u",mip->bin.bits); + } + else + *bits = 0; + + return pmode & PMIP_BRACKETS && mip->bin.ipvers == 6 + ? PrintCircBuf("[%s]%s%s",res,port,bits) + : PrintCircBuf("%s%s%s",res,port,bits); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintVersAddrMIP ( const ManageIP_t *mip, PrintModeIP_t pmode ) +{ + DASSERT(mip); + ccp res = PrintAddrMIP(mip,pmode); + return res ? PrintCircBuf("IPv%u %s",mip->bin.ipvers,res) : 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlSetAddrMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + if ( mip->bin.ipvers == 4 ) + { + const ipv4_t mask = GetIP4Mask(mip->bin.bits); + return PrintCircBuf("%s=4,%s=0,%s=0x%08x", + nip.ipvers, nip.addr1, + nip.addr2, ntohl( mip->bin.ip4 & mask )); + } + else if ( mip->bin.ipvers == 6 ) + { + const ipv6_t mask = GetIP6Mask(mip->bin.bits); + return PrintCircBuf("%s=6,%s=0x%llx,%s=0x%llx", + nip.ipvers, + nip.addr1, ntoh64( mip->bin.ip6_64[0] & mask.v64[0] ), + nip.addr2, ntoh64( mip->bin.ip6_64[1] & mask.v64[1] )); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlSetMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + if ( mip->bin.ipvers == 4 ) + { + const ipv4_t mask = GetIP4Mask(mip->bin.bits); + return PrintCircBuf("%s=4,%s=0,%s=0x%08x,%s=0,%s=0x%08x", + nip.ipvers, nip.addr1, + nip.addr2, ntohl( mip->bin.ip4 & mask ), + nip.mask1, nip.mask2, ntohl(mask)); + } + else if ( mip->bin.ipvers == 6 ) + { + const ipv6_t mask = GetIP6Mask(mip->bin.bits); + return PrintCircBuf("%s=6,%s=0x%llx,%s=0x%llx,%s=0x%llx,%s=0x%llx", + nip.ipvers, + nip.addr1, ntoh64( mip->bin.ip6_64[0] & mask.v64[0] ), + nip.addr2, ntoh64( mip->bin.ip6_64[1] & mask.v64[1] ), + nip.mask1, ntoh64(mask.v64[0]), + nip.mask2, ntoh64(mask.v64[1]) ); + } + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlSetBinMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + return PrintCircBuf("%s=unhex('%02x%02x%016llx%016llx')", + nip.binaddr, mip->bin.ipvers, mip->bin.bits, + ntoh64(mip->bin.ip6_64[0]), ntoh64(mip->bin.ip6_64[0]) ); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlCondAddrMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + if ( mip->bin.ipvers == 4 ) + return PrintCircBuf("%s=4 && %s=0x%08x", + nip.ipvers, nip.addr2, ntohl(mip->bin.ip4) ); + else if ( mip->bin.ipvers == 6 ) + return PrintCircBuf("%s=6 && %s=0x%llx && %s=0x%llx", + nip.ipvers, + nip.addr1, ntoh64(mip->bin.ip6_64[0]), + nip.addr2, ntoh64(mip->bin.ip6_64[1]) ); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlCondMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + if ( mip->bin.ipvers == 4 ) + return PrintCircBuf("%s=4 && %s=(%s&0x%08x)", + nip.ipvers, + nip.addr2, nip.mask2, ntohl(mip->bin.ip4) ); + else if ( mip->bin.ipvers == 6 ) + return PrintCircBuf("%s=6 && %s=(%s&0x%llx) && %s=(%s&0x%llx)", + nip.ipvers, + nip.addr1, nip.mask1, ntoh64(mip->bin.ip6_64[0]), + nip.addr2, nip.mask2, ntoh64(mip->bin.ip6_64[1]) ); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSqlCondBinMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ) +{ + if (!mip) + return 0; + + NamesIP_t nip; + SetupNamesIP(&nip,names); + + if ( mip->bin.ipvers == 4 ) + return PrintCircBuf("compare_binaddr(%s,4,0,0x%08x,%s,%s)", + nip.binaddr, ntohl(mip->bin.ip4), + nip.mask1, nip.mask2 ); + else if ( mip->bin.ipvers == 6 ) + return PrintCircBuf("compare_binaddr(%s,6,0x%llx,0x%llx,%s,%s)", + nip.binaddr, + ntoh64(mip->bin.ip6_64[0]), ntoh64(mip->bin.ip6_64[1]), + nip.mask1, nip.mask2 ); + + return 0; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ResolveIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +void ResetRIP ( ResolveIP_t *rip ) +{ + if (rip) + { + ResetMIP(&rip->mip4); + ResetMIP(&rip->mip6); + memset(rip,0,sizeof(*rip)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void ClearNamesRIP ( ResolveIP_t *rip ) +{ + if (rip) + { + ClearNameMIP(&rip->mip4); + ClearNameMIP(&rip->mip6); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int SetupListRIP ( ResolveIP_t *rip ) +{ + DASSERT(rip); + memset(rip->mip_list,0,sizeof(rip->mip_list)); + + ManageIP_t **mp = rip->mip_list; + switch (rip->ip_mode) + { + case IPV6_FIRST: + if (rip->mip6.bin.ipvers) + *mp++ = &rip->mip6; + // fall though + case IPV4_ONLY: + if (rip->mip4.bin.ipvers) + *mp++ = &rip->mip4; + break; + + case IPV4_FIRST: + if (rip->mip4.bin.ipvers) + *mp++ = &rip->mip4; + // fall though + case IPV6_ONLY: + if (rip->mip6.bin.ipvers) + *mp++ = &rip->mip6; + break; + } + + return mp - rip->mip_list; +} + +/////////////////////////////////////////////////////////////////////////////// + +ResolveIP_t GetCopyRIP ( const ResolveIP_t *rip ) +{ + ResolveIP_t res; + if (rip) + { + res.ip_mode = rip->ip_mode; + res.mip4 = GetCopyMIP(&rip->mip4); + res.mip6 = GetCopyMIP(&rip->mip6); + SetupListRIP(&res); + } + else + memset(&res,0,sizeof(res)); + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AssignRIP ( ResolveIP_t *dest, const ResolveIP_t *src ) +{ + if (dest) + { + ClearNamesRIP(dest); + *dest = GetCopyRIP(src); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +int ScanRIP +( + // return 0..2 = number of records + + ResolveIP_t *rip, // valid data, ResetRIP() + ccp addr, // address to scan + int protocol, // protocol to use: 0=all, -1=default (IPPROTO_TCP at the moment) + IpMode_t ip_mode, // IPv4 and/or IPV6, order of 'mip_list' + bool get_names // true: call GetNameInfoMIP() +) +{ + DASSERT(rip); + ResetRIP(rip); + + SplitIP_t sip; + if (!SplitIP(&sip,addr)) + return 0; + NormalizeNameSIP(&sip); + + rip->ip_mode = ip_mode; + + struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM }; + struct addrinfo *result; + char aname[300]; + Domain2ASCII(aname,sizeof(aname),sip.name,-1); + if (!getaddrinfo(aname,0,&hints,&result)) + { + struct addrinfo *ip4 = 0, *ip6 = 0; + for ( struct addrinfo *rp = result; rp; rp = rp->ai_next) + { + if ( !ip4 && rp->ai_family == AF_INET ) + ip4 = rp; + else if ( !ip6 && rp->ai_family == AF_INET6 ) + ip6 = rp; + } + + if (ip4) + AssignSockaddrMIP(&rip->mip4,ip4->ai_addr,sip.port,sip.bits); + + if (ip6) + AssignSockaddrMIP(&rip->mip6,ip6->ai_addr,sip.port,sip.bits); + + if (get_names) + { + GetNameInfoMIP(&rip->mip4); + GetNameInfoMIP(&rip->mip6); + } + + freeaddrinfo(result); + } + + return SetupListRIP(rip); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#if 0 + +void DumpRIP ( FILE *f, int indent, const ResolveIP_t *rip ) +{ + if ( !f || !rip ) + return; + indent = NormalizeIndent(indent); +} + +#endif +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct AllowIP4_t /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -262,6 +1690,9 @@ const KeywordTab_t AllowIP4KeyTable[] = { ALLOW_MODE_LOG, "LOG", 0, 0 }, { ALLOW_MODE_VERBOSE, "VERBOSE", 0, 0 }, + { ALLOW_RELEASE, "RELEASE", 0, 0 }, + { ALLOW_DEBUG, "DEBUG", 0, 0 }, + { ALLOW_MODE_NOT, "NOT", 0, 0 }, { ALLOW_MODE_SET, "SET", 0, ALLOW_MODE__OP }, { ALLOW_MODE_AND, "AND", 0, ALLOW_MODE__OP }, @@ -382,6 +1813,13 @@ enumError ScanLineAllowIP4 if ( res & ALLOW_MODE_NOT ) res = res ^ ALLOW_MODE__MASK; + #if defined(DEBUG) && DEBUG > 0 || defined(IS_DEVELOP) && IS_DEVELOP > 0 + if ( res & ALLOW_RELEASE ) + return ERR_OK; + #else + if ( res & ALLOW_DEBUG ) + return ERR_OK; + #endif //--- add item @@ -3120,12 +4558,15 @@ void OnCloseStream PRINT("CLOSE: %d\n",ts->sock); if (ts->OnClose) ts->OnClose(ts,now_usec); + if ( ts->sock != -1 ) { + // if not closed by OnClose() shutdown(ts->sock,SHUT_RDWR); close(ts->sock); ts->sock = -1; } + if ( !ts->protect && ts->handler ) ts->handler->need_maintenance |= 1; @@ -3312,7 +4753,7 @@ void CheckTimeoutTCP ( TCPHandler_t *th ) /////////////////////////////////////////////////////////////////////////////// -bool MaintenanceTCP ( TCPHandler_t *th ) +bool MaintainTCP ( TCPHandler_t *th ) { DASSERT(th); @@ -3338,7 +4779,7 @@ bool MaintenanceTCP ( TCPHandler_t *th ) void ManageSocketsTCP ( - // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintenanceTCP() + // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintainTCP() TCPHandler_t *th, // valid TCP handler FDList_t *fdl, // valid socket list @@ -3351,7 +4792,7 @@ void ManageSocketsTCP CheckTimeoutTCP(th); if (th->need_maintenance) - MaintenanceTCP(th); + MaintainTCP(th); } /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-network.h b/project/dclib/dclib-network.h index 1f89690..41f371f 100644 --- a/project/dclib/dclib-network.h +++ b/project/dclib/dclib-network.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -40,6 +40,8 @@ #include "dclib-basics.h" #include "dclib-file.h" +#include <stddef.h> + // /////////////////////////////////////////////////////////////////////////////// /////////////// definitions /////////////// @@ -108,29 +110,401 @@ bool ResolveHostMem // /////////////////////////////////////////////////////////////////////////////// +/////////////// enum PrintModeIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[PrintModeIP_t]] + +typedef enum PrintModeIP_t +{ + // characters for ScanPrintModeIP(): nria + PMIP_NEVER = 0x000, ///< '/Bits' unterdrücken. + PMIP_RELEVANT = 0x001, ///< '/Bits' nur falls relevant. + PMIP_IF_SET = 0x002, ///< '/Bits' nur falls gesetzt. + PMIP_ALWAYS = 0x003, ///< '/Bits' immer ausgeben. + PMIP_M_BITS = 0x003, ///< Maske für Bit-Modi. + + // characters for ScanPrintModeIP(): psmb + PMIP_PORT = 0x004, ///< Flag: Print port if >0. + PMIP_SERVICE = 0x008, ///< Flag: Print service instead of port number. + PMIP_MASK = 0x010, ///< Flag: Print netmask instead of bits for IPv4. + PMIP_BRACKETS = 0x020, ///< Flag: Print bracktes if [IPv6]. + + // characters for ScanPrintModeIP(): f + PMIP_FULL_IPV6 = 0x100, ///< Print IPv6 without short numbers. + + // characters for ScanPrintModeIP(): 0123 + PMIP_0DOTS = 0x200, ///< Print IPv4 as single number (highest priority). + PMIP_1DOT = 0x400, ///< Print IPv4 as "1.2" (A-Class notation). + PMIP_2DOTS = 0x800, ///< Print IPv4 as "1.2.3" (B-Class notation). + PMIP_3DOTS = 0, ///< Print IPv4 as "1.2.3.4" (C-Class notation, default). + PMIP_M_DOTS = 0xc00 ///< Maske für Dot-Modi. +} +PrintModeIP_t; + +PrintModeIP_t ScanPrintModeIP ( PrintModeIP_t base, ccp arg ); +ccp PrintPrintModeIP ( PrintModeIP_t val, bool align ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// enum IPClass_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[IPClass_t]] + +typedef enum IPClass_t +{ + IPCL_INVALID, // 0.0.0.0 :: + IPCL_LOOPBACK, // 127.0/8 ::1 + IPCL_LOCAL, // 10.0/8 172.16/12 192.168/16 169.254/16 fe80::/10 + IPCL_STANDARD, // * * + IPCL_SPECIAL, // 224.0.0.0/3 f00::/8 + IPCL__N, +} +IPClass_t; + +//----------------------------------------------------------------------------- + +ccp GetIPClassColor ( const ColorSet_t *colset, IPClass_t ipc ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct SplitIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[SplitIP_t]] + +typedef struct SplitIP_t +{ + int bits; + u16 port; + char name[260]; +} +SplitIP_t; + +//----------------------------------------------------------------------------- + +bool SplitIP ( SplitIP_t *sip, ccp addr ); + +static inline void NormalizeNameSIP ( SplitIP_t *sip ) + { DASSERT(sip); NormalizeIP4(sip->name,sizeof(sip->name),true,sip->name,-1); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct NamesIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[NamesIP_t]] + +typedef struct NamesIP_t +{ + ccp binaddr; + ccp ipvers; + ccp addr1; + ccp addr2; + ccp bits; + ccp mask1; + ccp mask2; +} +NamesIP_t; + +//----------------------------------------------------------------------------- + +// reset if src==NULL +void SetupNamesIP ( NamesIP_t *dest, const NamesIP_t *src ); +void SetupNamesByListIP ( NamesIP_t *dest, const exmem_list_t *src ); + +ccp PrintNamesIP ( const NamesIP_t *nip ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct BinIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +enum +{ + IP_SIZE_ADDR = 39, ///< Maximale Zeichenlänge einer Adresse ohne Maske. + IP_SIZE_ADDR_MASK = 43, ///< Maximale Zeichenlänge einer Adresse mit Maske. + IP_SIZE_PREFIX = 22, ///< Maximale Zeichenlänge einer Adresse, die mit MaskPrefixMIP() gekürzt wurde. + IP_SIZE_ADDR_BIN = 22, ///< Size für eine binäre IP (ohne Maske). + + IP_SIZE_BINIP_C = 20, ///< Size für binäres Tupel (padding,ipvers,bits,addr). + IP_SIZE_BINIP = 18, ///< Size für binäres Tupel (ipvers,bits,addr). + IP_OFFSET_BINIP = 2, ///< Offset für DB-Transfer. +}; + +/////////////////////////////////////////////////////////////////////////////// +// [[BinIP_t]] + +typedef struct BinIP_t +{ + u16 user_id; // any id defined by user + u8 ipvers; // 0:invalid, 4:IPv4, 6:IPv6 + u8 bits; // number of 1-bits in netmask + + union // different views of ip address + { + u8 ip6_8[16]; + u16 ip6_16[8]; + u32 ip6_32[4]; + u64 ip6_64[2]; + ipv6_t ip6; + + struct + { + ipv4_t ip4_padding[3]; + ipv4_t ip4; + }; + + struct + { + ipv4_t ip4x_padding[3]; + ipv4x_t ip4x; + }; + }; +} +__attribute__ ((packed)) BinIP_t; + +_Static_assert( offsetof(BinIP_t,ipvers) == IP_OFFSET_BINIP, "wrong order of BinIP_t members" ); +_Static_assert( sizeof(BinIP_t) == IP_SIZE_BINIP_C, "wrong size of BinIP_t" ); + +//----------------------------------------------------------------------------- + +static inline u8 * GetCorePtrBIP ( const BinIP_t *src ) { return (u8*)&src->ipvers; } +static inline uint GetCoreSizeBIP ( const BinIP_t *src ) { return IP_SIZE_BINIP; } + +BinIP_t GetIPMask ( const BinIP_t *src ); +void ApplyIPMask ( BinIP_t *bip ); + +IPClass_t GetIPClassBIP ( const BinIP_t * bip ); +static inline ccp GetColorBIP( const ColorSet_t *colset, const BinIP_t * bip ) + { return GetIPClassColor(colset,GetIPClassBIP(bip)); } + +ccp PrintBIP ( const BinIP_t * bip ); + +// returns false, if !a1 || !a2 || invalid || differ_after_mask +// use_mask: bit 0 == 1 : use mask of a1; bit 1 == 2 : use mask of a2 +bool IsSameBIP ( const BinIP_t *a1, const BinIP_t *a2, uint use_mask ); + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct BinIPList_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[BinIPItem_t]] + +typedef struct BinIPItem_t +{ + ccp name; // optional name, alloced + BinIP_t bip; // IP data +} +BinIPItem_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[BinIPList_t]] + +typedef struct BinIPList_t +{ + BinIPItem_t *list; // list of elements + uint used; // number of used elements in 'list' + uint size; // number of allocated elements in 'list' +} +BinIPList_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[BinIPIterate_t]] + +typedef struct BinIPIterate_t +{ + bool valid; // iterator is valid + const BinIPList_t *bil; // list data + const BinIPItem_t *cur; // current element + + BinIP_t addr; // address to serch + uint use_mask; // flags for IsSameBIP() +} +BinIPIterate_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline void InitializeBIL ( BinIPList_t *bil ) + { DASSERT(bil); memset(bil,0,sizeof(*bil)); } + +void ResetBIL ( BinIPList_t *bil ); +void ClearBIL ( BinIPList_t *bil ); + +void SetSizeBIL ( BinIPList_t *bil, int size ); +void SetMinSizeBIL ( BinIPList_t *bil, int min_size ); +BinIPItem_t * CreateItemBIL ( BinIPList_t *bil, int n_item ); + +#if 0 // not implemented +BinIPItem_t * InsertBIL ( BinIPList_t *bil, int index, ccp key, CopyMode_t copy_mode ); +BinIPItem_t * InsertByKeyBIL ( BinIPList_t *bil, ccp key, CopyMode_t copy_mode ); +static inline BinIPItem_t * Append ( BinIPList_t *bil, int index, ccp key, CopyMode_t copy_mode ) + { return InsertBIL(bil,INT_MAX,key,copy_mode); } + +int FindIndexBIL ( const BinIPList_t *bil, ccp key ); +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// bits for use_mask: +// bit 0 == 1 : use mask of BinIPList_t::items +// bit 1 == 2 : use mask of addr + +BinIPIterate_t FindAddressBIL ( const BinIPList_t *bil, BinIP_t *addr, uint use_mask ); +void FindNextAddressBIL ( BinIPIterate_t *iter ); + +uint ScanInterfaceAddresses ( BinIPList_t *bil, bool init_bil ); +const BinIPList_t * GetInterfaceAddressList ( bool update ); + +bool IsLocalBIL ( BinIP_t *addr, uint use_mask ); +static inline bool IsLocalAddress ( BinIP_t *addr ) { return IsLocalBIL(addr,2); } +static inline bool IsLocalNetwork ( BinIP_t *addr ) { return IsLocalBIL(addr,3); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ManageIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ManageIP_t]] + +typedef struct ManageIP_t +{ + BinIP_t bin; // binary data + u16 port; // 0: not specified + bool have_bits; // true: bits were specified on input + u8 relevant_bits; // number of relevant bits + ccp name; // NULL or getnameinfo(), alloced + ccp decoded_name; // NULL or punycode-decoded 'name' if differ, alloced +} +ManageIP_t; + +/////////////////////////////////////////////////////////////////////////////// + +static inline bool IsIPv4MIP ( const ManageIP_t *mip ) + { return mip && mip->bin.ipvers == 4; } + +static inline bool IsIPv6MIP ( const ManageIP_t *mip ) + { return mip && mip->bin.ipvers == 6; } + +//----------------------------------------------------------------------------- +// if bits==-1: ignore parameter + +void ResetMIP ( ManageIP_t *mip ); // mip can be NULL +void ClearNameMIP ( ManageIP_t *mip ); // mip can be NULL + +bool AssignMIP ( ManageIP_t *dest, const ManageIP_t *src ); +bool AssignBinMIP ( ManageIP_t *mip, cvp bin, int bits ); +bool AssignSockaddrMIP ( ManageIP_t *mip, cvp sockaddr, u16 port, int bits ); + +bool ScanMIP ( ManageIP_t *mip, ccp addr ); + +ManageIP_t GetCopyMIP ( const ManageIP_t *mip ); +ManageIP_t GetSockNameMIP ( int sock ); +ManageIP_t GetPeerNameMIP ( int sock ); + +// protocol: protocol to use: 0=all, -1=default (IPPROTO_TCP at the moment) +ManageIP_t GetAddrInfoMIP ( ccp addr, int protocol, IpMode_t ipm ); + +sockaddr_in46_t GetSockaddrMIP ( const ManageIP_t *mip ); +ccp GetNameInfoMIP ( ManageIP_t *mip ); + +void ApplyMaskMIP ( ManageIP_t *mip, int bits ); +void MaskPrefixMIP ( ManageIP_t *mip ); // includes ApplyMaskMIP() + +//----------------------------------------------------------------------------- + +static inline bool PrintBitsMIP ( const ManageIP_t *mip, PrintModeIP_t pmode ) +{ + DASSERT(mip); + pmode &= PMIP_M_BITS; + return pmode > PMIP_NEVER + && ( mip->bin.bits < mip->relevant_bits + || pmode == PMIP_IF_SET && mip->have_bits + || pmode >= PMIP_ALWAYS + ); +} + +ccp PrintAddrMIP ( const ManageIP_t *mip, PrintModeIP_t pmode ); +ccp PrintVersAddrMIP ( const ManageIP_t *mip, PrintModeIP_t pmode ); + +static inline IPClass_t GetIPClassMIP ( const ManageIP_t * mip ) + { DASSERT(mip); return GetIPClassBIP(&mip->bin); } + +//----------------------------------------------------------------------------- + +ccp GetSqlSetAddrMIP ( const ManageIP_t *mip, const NamesIP_t *names ); +ccp GetSqlSetMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ); +ccp GetSqlSetBinMIP ( const ManageIP_t *mip, const NamesIP_t *names ); + +ccp GetSqlCondAddrMIP ( const ManageIP_t *mip, const NamesIP_t *names ); +ccp GetSqlCondMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ); +ccp GetSqlCondBinMaskMIP ( const ManageIP_t *mip, const NamesIP_t *names ); + +static inline ccp GetSqlCondBinAddrMIP ( const ManageIP_t *mip, const NamesIP_t *names ) + { return GetSqlSetBinMIP(mip,names); } + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// struct ResolveIP_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[ResolveIP_t]] + +typedef struct ResolveIP_t +{ + IpMode_t ip_mode; // used mode + ManageIP_t mip4; // resolved IPv4 + ManageIP_t mip6; // resolved IPv6 + ManageIP_t *mip_list[3]; // ordered and 0-termiated list of valid 'mip*' +} +ResolveIP_t; + +//----------------------------------------------------------------------------- + +void ResetRIP ( ResolveIP_t *rip ); // rip can be NULL +void ClearNamesRIP ( ResolveIP_t *rip ); // rip can be NULL + +ResolveIP_t GetCopyRIP ( const ResolveIP_t *rip ); +void AssignRIP ( ResolveIP_t *dest, const ResolveIP_t *src ); + +int ScanRIP +( + // return 0..2 = number of records + + ResolveIP_t *rip, // valid data, ResetRIP() + ccp addr, // address to scan + int protocol, // protocol to use: 0=all, -1=default (IPPROTO_TCP at the moment) + IpMode_t ip_mode, // IPv4 and/or IPV6, order of 'mip_list' + bool get_names // true: call GetNameInfoMIP() +); + +// not implemented yet +// void DumpRIP ( FILE *f, int indent, const ResolveIP_t *rip ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// struct AllowIP4_t /////////////// /////////////////////////////////////////////////////////////////////////////// // standard values #define ALLOW_MODE_DENY 0 // access denied -#define ALLOW_MODE_ALLOW 0x001 // access allowed -#define ALLOW_MODE_AKEY 0x002 // access allowed by access key +#define ALLOW_MODE_ALLOW 0x0001 // access allowed +#define ALLOW_MODE_AKEY 0x0002 // access allowed by access key // locations -#define ALLOW_MODE_EXTERN 0x004 // keyword 'EXTERN' set -#define ALLOW_MODE_LAN 0x008 // keyword 'LAN' set -#define ALLOW_MODE_LOCAL 0x010 // keyword 'LOCAL' set +#define ALLOW_MODE_EXTERN 0x0004 // keyword 'EXTERN' set +#define ALLOW_MODE_LAN 0x0008 // keyword 'LAN' set +#define ALLOW_MODE_LOCAL 0x0010 // keyword 'LOCAL' set // users -#define ALLOW_MODE_PUBLIC 0x020 // keyword 'PUBLIC' set -#define ALLOW_MODE_USER 0x040 // keyword 'USER' set -#define ALLOW_MODE_MOD 0x080 // keyword 'MOD' or 'MODERATOR' set -#define ALLOW_MODE_ADMIN 0x100 // keyword 'ADMIN' or 'ADMINISTRATOR' set -#define ALLOW_MODE_DEVELOP 0x200 // keyword 'DEVELOP' or 'DEVELOPER' set +#define ALLOW_MODE_PUBLIC 0x0020 // keyword 'PUBLIC' set +#define ALLOW_MODE_USER 0x0040 // keyword 'USER' set +#define ALLOW_MODE_MOD 0x0080 // keyword 'MOD' or 'MODERATOR' set +#define ALLOW_MODE_ADMIN 0x0100 // keyword 'ADMIN' or 'ADMINISTRATOR' set +#define ALLOW_MODE_DEVELOP 0x0200 // keyword 'DEVELOP' or 'DEVELOPER' set // logging -#define ALLOW_MODE_LOG 0x400 // keyword 'LOG' set -#define ALLOW_MODE_VERBOSE 0x800 // keyword 'VERBOSE' set +#define ALLOW_MODE_LOG 0x0400 // keyword 'LOG' set +#define ALLOW_MODE_VERBOSE 0x0800 // keyword 'VERBOSE' set + +// context +#define ALLOW_RELEASE 0x1000 // accept this record if neither DEBUG nor IS_DEVELOP is set +#define ALLOW_DEBUG 0x2000 // accept this record if DEBUG or IS_DEVELOP is set // special processing values #define ALLOW_MODE_NOT 0x1000000000000000ull // negate bits @@ -685,7 +1059,7 @@ ccp PrintTransferStatsSQL // NULL: use a local circulary static buffer size_t buf_size, // size of 'buf', ignored if buf==NULL const TransferStats_t *ts, // valid source - ccp prefix // not NULL: prefix memebr names + ccp prefix // not NULL: prefix member names ); //----------------------------------------------------------------------------- @@ -1085,7 +1459,7 @@ typedef struct TCPHandler_t TCPHandlerFunc OnAcceptStream; // not NULL: called before accpeting a connection TCPCreateFunc OnCreateStream; // not NULL: called to create // and initialize a stream object - TCPStreamFunc OnAddedStream; // not NULL: called after the stream + TCPStreamFunc OnAddedStream; // not NULL: called after the stream // is created and added TCPStreamFunc OnDestroyStream; // not NULL: called by ResetTCPStream() @@ -1282,11 +1656,11 @@ void CheckSocketsTCP ); void CheckTimeoutTCP ( TCPHandler_t *th ); -bool MaintenanceTCP ( TCPHandler_t *th ); +bool MaintainTCP ( TCPHandler_t *th ); void ManageSocketsTCP ( - // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintenanceTCP() + // call CheckSocketsTCP(), CheckTimeoutTCP(), MaintainTCP() TCPHandler_t *th, // valid TCP handler FDList_t *fdl, // valid socket list @@ -1294,7 +1668,7 @@ void ManageSocketsTCP ); /////////////////////////////////////////////////////////////////////////////// -// special case: send single data packtes +// special case: send single data packets TCPStream_t * SendSingleUnixTCP ( diff --git a/project/dclib/dclib-numeric.c b/project/dclib/dclib-numeric.c index 50fd266..0b21975 100644 --- a/project/dclib/dclib-numeric.c +++ b/project/dclib/dclib-numeric.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -36,10 +36,12 @@ #include <string.h> #include <unistd.h> +#include <stddef.h> #include <time.h> #include <stdio.h> #include <errno.h> #include <sys/time.h> +#include <sys/resource.h> #include <fcntl.h> #include "dclib-basics.h" @@ -966,6 +968,213 @@ int CheckIndexCEnd ( int max, int * p_begin, int count ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// lt/eq/gt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanCompareOp ( compare_operator_t *dest_op, ccp arg ) +{ + compare_operator_t op = COP_FALSE; + ccp ptr = arg; + if (ptr) + { + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + if ( (uchar)ptr[0] == 0xe2 && (uchar)ptr[1] == 0x89 ) + { + switch ((uchar)ptr[2]) + { + case 0xa0: op = COP_NE; ptr += 3; break; // UTF8 ≠ + case 0xa4: op = COP_LE; ptr += 3; break; // UTF8 ≤ + case 0xa5: op = COP_GE; ptr += 3; break; // UTF8 ≥ + } + } + else if ( *ptr == '!' ) + { + if ( *++ptr == '=' ) + { + op = COP_NE; + ptr++; + } + } + else + switch (*ptr) + { + case '=': + op = COP_EQ; + if ( *++ptr == '=' ) + ptr++; + break; + + case '<': + op = COP_LT; + switch (*++ptr) + { + case '=': + op = COP_LE; + ptr++; + break; + case '>': + op = COP_NE; + ptr++; + break; + } + break; + + case '>': + op = COP_GT; + if ( *++ptr == '=' ) + { + op = COP_GE; + ptr++; + } + break; + } + } + + if (dest_op) + *dest_op = op; + return op == COP_FALSE ? (char*)arg : (char*)ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetCompareOpName ( compare_operator_t op ) +{ + switch (op) + { + case COP_EQ: return "=="; + case COP_LT: return "<"; + case COP_GT: return ">"; + case COP_NE: return "!="; + case COP_LE: return "<="; + case COP_GE: return ">="; + case COP_TRUE: return "*"; + default: return "%"; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpStat ( int stat, compare_operator_t op ) +{ + switch (op) + { + case COP_EQ: return stat == 0; + case COP_LT: return stat < 0; + case COP_GT: return stat > 0; + case COP_NE: return stat != 0; + case COP_LE: return stat <= 0; + case COP_GE: return stat >= 0; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpINT ( int a, compare_operator_t op, int b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpUINT ( uint a, compare_operator_t op, uint b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpI64 ( s64 a, compare_operator_t op, s64 b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpU64 ( u64 a, compare_operator_t op, u64 b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpFLT ( float a, compare_operator_t op, float b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpDBL ( double a, compare_operator_t op, double b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// encoding/decoding /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -990,7 +1199,7 @@ ccp GetEncodingName ( EncodeMode_t em ) uint GetEscapedSize ( - // returns the needed buffer size for PrintEscapedString() + // returns the needed buffer size for PrintEscapedString() ccp source, // NULL or string to print int src_len, // length of string. if -1, str is null terminated @@ -1435,7 +1644,7 @@ uint EncodeBase64 // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -1564,7 +1773,7 @@ uint EncodeBase64ml // ml: multi line // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -1985,7 +2194,7 @@ uint EncodeByMode // returns the number of valid bytes in 'buf'. Result is NULL-terminated. char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 ccp source, // string to encode int slen, // length of string. if -1, str is null terminated EncodeMode_t emode // encoding mode @@ -2172,7 +2381,7 @@ char * EscapeString buf[0] = '$'; buf[1] = buf[len+2] = quote_char; buf[len+3] = 0; - + } else if ( quote_mode == 2 ) { @@ -2190,9 +2399,132 @@ char * EscapeString // /////////////////////////////////////////////////////////////////////////////// +/////////////// struct Escape_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// https://en.wikipedia.org/wiki/ANSI_escape_code + +const char ecape_stat_name[ESCST__N+1][5] = +{ + "-", // ESCST_NONE + "CHAR", // ESCST_CHAR + "FE", // ESCST_FE + "SS2", // ESCST_SS2 + "SS3", // ESCST_SS3 + "CSI", // ESCST_CSI + "OSC", // ESCST_OSC + "", // ESCST__N +}; + +/////////////////////////////////////////////////////////////////////////////// + +EscapeStat_t CheckEscape ( Escape_t *esc, cvp source, cvp end, bool check_esc ) +{ + DASSERT(esc); + memset(esc,0,sizeof(*esc)); + if (!source) + return 0; + + ccp src = source; + if (!end) + end = src + strlen(src); + if ( src >= (ccp)end ) + return 0; + + ccp beg = src; + if (check_esc) + { + if ( *src != '\e' ) + { + esc->status = ESCST_CHAR; + esc->code = *src++; + goto term; + } + + src++; + if ( src >= (ccp)end ) + return 0; + } + esc->code = *src++; + + int term_wd = 0; + switch (esc->code) + { + case 'N': //--- SS2: Single Shift 2 --- + if ( src < (ccp)end ) + { + esc->status = ESCST_SS2; + esc->code = *src++; + } + break; + + case 'O': //--- SS3: Single Shift 3 --- + if ( src < (ccp)end ) + { + esc->status = ESCST_SS3; + esc->code = *src++; + } + break; + + case '[': //--- CSI --- + while ( src < (ccp)end && *src >= 0x30 && *src <= 0x3f ) + src++; + while ( src < (ccp)end && *src >= 0x20 && *src <= 0x2f ) + src++; + if ( src < (ccp)end && *src >= 0x40 && *src <= 0x7e ) + { + esc->status = ESCST_CSI; + esc->code = *src++; + } + break; + + + case ']': //--- OSC --- + // terminate by ST (ESC \ == 0x9c) or BEL (7) + for ( ; src < (ccp)end; src++ ) + { + if ( *src == 7 || (u8)*src == 0x9c ) + { + src++; + term_wd = 1; + esc->status = ESCST_OSC; + break; + } + if ( src+1 < (ccp)end && src[0] == '\e' && src[1] == '\\' ) + { + src += 2; + term_wd = 2; + esc->status = ESCST_OSC; + break; + } + } + break; + + default: //--- single char --- + esc->status = ESCST_CHAR; + break; + } + + term:; + if ( esc->status != ESCST_NONE ) + { + esc->scanned_len = src - (ccp)source; + if ( esc->status > ESCST_CHAR ) + { + esc->esc.ptr = beg; + esc->esc.len = src - beg - term_wd; + } + } + + return esc->status; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// time /////////////// /////////////////////////////////////////////////////////////////////////////// +ccp micro_second_char = "u"; + s64 timezone_adjust_sec = -1; s64 timezone_adjust_usec = -1; s64 timezone_adjust_nsec = -1; @@ -2285,6 +2617,24 @@ struct timespec GetClockTime ( bool localtime ) return ts; } +//----------------------------------------------------------------------------- + +struct timespec GetClockTimer(void) +{ + struct timespec ts; + + #if HAVE_CLOCK_GETTIME + if (!clock_gettime(CLOCK_MONOTONIC,&ts)) + return ts; + #endif + + // fall back + struct timeval tval = GetTimeOfDay(false); + ts.tv_sec = tval.tv_sec; + ts.tv_nsec = tval.tv_usec * NSEC_PER_USEC; + return ts; +} + /////////////////////////////////////////////////////////////////////////////// DayTime_t GetDayTime ( bool localtime ) @@ -2346,50 +2696,114 @@ u_nsec_t GetTimeNSec ( bool localtime ) return (u_nsec_t)(ts.tv_sec) * NSEC_PER_SEC + ts.tv_nsec; } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CurrentTime_t current_time = {0}; + +CurrentTime_t GetCurrentTime ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + + CurrentTime_t ct; + ct.sec = tval.tv_sec; + ct.usec = tval.tv_sec * USEC_PER_SEC + tval.tv_usec; + ct.msec = ct.usec / 1000; + return ct; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// timer /////////////// /////////////////////////////////////////////////////////////////////////////// -static time_t time_base = 0; +u_sec_t GetTimerSec() +{ + struct timespec ts = GetClockTimer(); + return ts.tv_sec; +} /////////////////////////////////////////////////////////////////////////////// u_msec_t GetTimerMSec() { - struct timeval tv; - gettimeofday(&tv,NULL); - if (!time_base) - time_base = tv.tv_sec; - - return (u_msec_t)( tv.tv_sec - time_base ) * MSEC_PER_SEC - + tv.tv_usec/USEC_PER_MSEC; + struct timespec ts = GetClockTimer(); + return ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC; } /////////////////////////////////////////////////////////////////////////////// u_usec_t GetTimerUSec() { - struct timeval tv; - gettimeofday(&tv,NULL); - if (!time_base) - time_base = tv.tv_sec; + struct timespec ts = GetClockTimer(); + return ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} - return (u_usec_t)( tv.tv_sec - time_base ) * USEC_PER_SEC + tv.tv_usec; +/////////////////////////////////////////////////////////////////////////////// + +u_nsec_t GetTimerNSec() +{ + struct timespec ts = GetClockTimer(); + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; } /////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintRefTime +( -u_usec_t GetTimerNSec() + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +) { - struct timespec ts = GetClockTime(false); - if (!time_base) - time_base = ts.tv_sec; + u_sec_t ref; + if (!ref_tim) + ref = GetTimeSec(false); + else + { + if (!*ref_tim) + *ref_tim = GetTimeSec(false); + ref = *ref_tim; + } - return (u_usec_t)( ts.tv_sec - time_base ) * NSEC_PER_SEC + ts.tv_nsec; + ccp format = tim >= ref - 10*SEC_PER_HOUR && tim <= ref + 10*SEC_PER_HOUR + ? " %k:%M:%S" + : tim >= ref - 12*SEC_PER_DAY && tim <= ref + 10*SEC_PER_DAY + ? " %e. %k:%M" + : "%F"; + return PrintTimeByFormat(format,tim); +} + +//----------------------------------------------------------------------------- + +ccp PrintRefTimeUTC +( + + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +) +{ + u_sec_t ref; + if (!ref_tim) + ref = GetTimeSec(false); + else + { + if (!*ref_tim) + *ref_tim = GetTimeSec(false); + ref = *ref_tim; + } + + ccp format = tim >= ref - 10*SEC_PER_HOUR && tim <= ref + 10*SEC_PER_HOUR + ? " %k:%M:%S" + : tim >= ref - 12*SEC_PER_DAY && tim <= ref + 10*SEC_PER_DAY + ? " %e. %k:%M" + : "%F"; + return PrintTimeByFormatUTC(format,tim); } -/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ccp PrintTimeByFormat @@ -2397,14 +2811,12 @@ ccp PrintTimeByFormat // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ) { - if (!tim) - tim = time(0); - char buf[100]; - struct tm *tm = localtime(&tim); + const time_t reftime = tim ? tim : time(0); + struct tm *tm = localtime(&reftime); const uint len = strftime(buf,sizeof(buf),format,tm); return CopyCircBuf(buf,len+1); } @@ -2416,14 +2828,12 @@ ccp PrintTimeByFormatUTC // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ) { - if (!tim) - tim = time(0); - char buf[100]; - struct tm *tm = gmtime(&tim); + const time_t reftime = tim ? tim : time(0); + struct tm *tm = gmtime(&reftime); const uint len = strftime(buf,sizeof(buf),format,tm); return CopyCircBuf(buf,len+1); } @@ -2484,6 +2894,90 @@ ccp PrintNSecByFormatUTC return CopyCircBuf(buf,len+1); } +//----------------------------------------------------------------------------- + +ccp PrintNTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +) +{ + return PrintNSecByFormat(format,ntime/NSEC_PER_SEC,ntime%NSEC_PER_SEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintNTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +) +{ + return PrintNSecByFormatUTC(format,ntime/NSEC_PER_SEC,ntime%NSEC_PER_SEC); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintUTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +) +{ + return PrintNSecByFormat(format,utime/USEC_PER_SEC,utime%USEC_PER_SEC*NSEC_PER_USEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintUTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +) +{ + return PrintNSecByFormatUTC(format,utime/USEC_PER_SEC,utime%USEC_PER_SEC*NSEC_PER_USEC); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintMTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +) +{ + return PrintNSecByFormat(format,mtime/MSEC_PER_SEC,mtime%MSEC_PER_SEC*NSEC_PER_MSEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintMTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +) +{ + return PrintNSecByFormatUTC(format,mtime/MSEC_PER_SEC,mtime%MSEC_PER_SEC*NSEC_PER_MSEC); +} + /////////////////////////////////////////////////////////////////////////////// ccp PrintTimevalByFormat @@ -2811,13 +3305,16 @@ ccp PrintTimer3 u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { if (!buf) buf = GetCircBuf( buf_size = 4 ); + const bool aligned = mode & DC_SFORM_ALIGN; - if ( !sec && usec >= 0 && usec <= 999999 ) + if ( !sec && !usec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( !sec && usec >= 0 && usec <= 999999 ) { if (!usec) StringCopyS(buf,buf_size,aligned?" 0":"0"); @@ -2868,13 +3365,16 @@ ccp PrintTimer4 u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { if (!buf) buf = GetCircBuf( buf_size = 5 ); + const bool aligned = mode & DC_SFORM_ALIGN; - if ( sec < 10 && usec >= 0 && usec <= 999999 ) + if ( !sec && !usec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( sec < 10 && usec >= 0 && usec <= 999999 ) { if (sec) snprintf(buf,buf_size,"%llu.%us",sec,usec/100000); @@ -2915,38 +3415,53 @@ ccp PrintTimer4 /////////////////////////////////////////////////////////////////////////////// -// [[nsec]] -> PrintTimer6U(), PrintTimer6N() - -ccp PrintTimer6 +ccp PrintTimer6N ( char * buf, // result buffer (>6 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u64 sec, // seconds to print - int usec, // 0...999999: usec fraction, - // otherwise suppress ms/us output - bool aligned // true: print aligned 6 character output + int nsec, // 0...999999999: nsec fraction, + // otherwise suppress ms/us/ns output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ) { + if ( mode & DC_SFORM_CENTER ) + { + char buf2[20]; + PrintTimer6N(buf2,sizeof(buf2),sec,nsec,mode&~(DC_SFORM_CENTER|DC_SFORM_ALIGN)); + return StringCenterS(buf,buf_size,buf2,6); + } + + char temp[20]; if (!buf) - buf = GetCircBuf( buf_size = 7 ); + { + buf = temp; + buf_size = sizeof(temp); + } - if (aligned) + const bool nsec_valid = nsec >= 0 && nsec <= 999999999; + + if ( mode & DC_SFORM_ALIGN ) { - if ( sec < 10 && usec >= 0 && usec <= 999999 ) + if ( !sec && !nsec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( sec < 10 && nsec_valid ) { - usec += 1000000 * sec; - if (!usec) + u_nsec_t sum = sec *NSEC_PER_SEC + nsec; + if (!sum) StringCopyS(buf,buf_size," 0"); - else if ( usec < 10000 ) - snprintf(buf,buf_size,"%4uus",usec); + else if ( sum < 10000 ) + snprintf(buf,buf_size,"%4lluns",sum); + else if ( sum < 10000000 ) + snprintf(buf,buf_size,"%4lluus",sum/1000); else - snprintf(buf,buf_size,"%4ums",usec/1000); + snprintf(buf,buf_size,"%4llums",sum/1000000); } else if ( sec < 100 ) { - if ( usec >= 0 && usec <= 999999 ) - snprintf(buf,buf_size,"%2llu.%02us",sec,usec/10000); + if (nsec_valid ) + snprintf(buf,buf_size,"%2llu.%02us",sec,nsec/10000000); else snprintf(buf,buf_size,"%5llus",sec); } @@ -2975,21 +3490,25 @@ ccp PrintTimer6 } else { - if ( sec < 10 ) + if ( !sec && !nsec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size,"-"); + else if ( sec < 10 && nsec_valid ) { - usec += 1000000 * sec; - if (!usec) + u_nsec_t sum = sec *NSEC_PER_SEC + nsec; + if (!sum) StringCopyS(buf,buf_size,"0"); - else if ( usec < 10000 ) - snprintf(buf,buf_size,"%uus",usec); + else if ( sum < 10000 ) + snprintf(buf,buf_size,"%lluns",sum); + else if ( sum < 10000000 ) + snprintf(buf,buf_size,"%lluus",sum/1000); else - snprintf(buf,buf_size,"%ums",usec/1000); + snprintf(buf,buf_size,"%llums",sum/1000000); } else if ( sec < 100 ) { - if ( usec >= 0 && usec <= 999999 ) + if ( nsec_valid ) { - const uint val2 = usec/10000; + const uint val2 = nsec/10000000; if (val2) snprintf(buf,buf_size,"%llu.%02us",sec,val2); else @@ -3052,7 +3571,7 @@ ccp PrintTimer6 } } - return buf; + return buf == temp ? CopyCircBuf0(buf,strlen(buf)) : buf; } /////////////////////////////////////////////////////////////////////////////// @@ -3065,14 +3584,19 @@ ccp PrintTimerUSec4s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { if ( !buf || buf_size < 2 ) buf = GetCircBuf( buf_size = 5 ); if (!usec) - StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } else { char sign; @@ -3084,7 +3608,7 @@ ccp PrintTimerUSec4s else sign = mode & DC_SFORM_PLUS ? '+' : ' '; - PrintTimerUSec3(buf+1,buf_size-1,usec,mode&DC_SFORM_ALIGN); + PrintTimerUSec3(buf+1,buf_size-1,usec,mode&~DC_SFORM_PLUS); *buf = ' '; char *ptr = buf+1; @@ -3097,22 +3621,25 @@ ccp PrintTimerUSec4s /////////////////////////////////////////////////////////////////////////////// -// [[nsec]] -> PrintTimer7Us(), PrintTimer7Ns() - ccp PrintTimerUSec7s ( char * buf, // result buffer (>7 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { if ( !buf || buf_size < 2 ) buf = GetCircBuf( buf_size = 8 ); if (!usec) - StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } else { char sign; @@ -3135,6 +3662,123 @@ ccp PrintTimerUSec7s return buf; } +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerNSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_nsec_t nsec, // nansoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH +) +{ + if ( !buf || buf_size < 2 ) + buf = GetCircBuf( buf_size = 8 ); + + if (!nsec) + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } + else + { + char sign; + if ( nsec < 0 ) + { + nsec = -nsec; + sign = '-'; + } + else + sign = mode & DC_SFORM_PLUS ? '+' : ' '; + + PrintTimerNSec6(buf+1,buf_size-1, nsec, mode & DC_SFORM_ALIGN ); + *buf = ' '; + + char *ptr = buf+1; + while ( *ptr == ' ' ) + ptr++; + ptr[-1] = sign; + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge3Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 ? " +" : PrintTimer3(0,0,now,-1,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge4Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 + ? PrintTimerSec4s(0,0,now,DC_SFORM_ALIGN) + : PrintTimer4(0,0,now,-1,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge6Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 ? " +" : PrintTimerSec6(0,0,now,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge7Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + return PrintTimerSec7s(0,0,now-time,DC_SFORM_ALIGN); +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// scan date & time /////////////// @@ -3211,7 +3855,7 @@ char * ScanIntervalUSec { struct timespec ts; char *res = ScanIntervalTS(&ts,source); - + if (res_usec) *res_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; return res; @@ -3227,7 +3871,7 @@ char * ScanIntervalNSec { struct timespec ts; char *res = ScanIntervalTS(&ts,source); - + if (res_nsec) *res_nsec = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; return res; @@ -3657,6 +4301,1821 @@ time_t year2time ( int year ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// usage counter /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int usage_count_enabled = 0; + +void EnableUsageCount() +{ + usage_count_enabled++; + UpdateUsageByMgr(0); +} + +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_UsageCount[] = +{ + #undef SRT_NAME + #define SRT_NAME UsageCount_t + + DEF_SRT_UINT( par.enabled, "enabled" ), + DEF_SRT_UINT( par.used, "used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].count, "count" ), + DEF_SRT_UINT( entry[0].elapsed_nsec, "elapsed" ), + DEF_SRT_UINT( entry[0].time_sec, "time" ), + DEF_SRT_FLOAT( entry[0].top1s, "top1s" ), + DEF_SRT_FLOAT( entry[0].top5s, "top5s" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +void ClearUsageCount ( UsageCount_t *uc, int count ) +{ + if (!uc) + return; + + uint index = CheckIndex1(uc->par.used,count); + if ( index >= uc->par.used - 1 ) + return; + + uc->par.used = index; + if (!index) + { + const bool enabled = uc->par.enabled; + memset( uc, 0, sizeof(*uc) ); + uc->par.enabled = enabled; + } + else + { + UsageCountEntry_t *e = uc->entry + index; + memset( e, 0, PTR_DISTANCE((u8*)uc->entry+sizeof(uc->entry),e) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[UpdateUsageCount]] + +void UpdateUsageCount ( UsageCount_t *uc, u_nsec_t timer_nsec ) +{ + if (!uc) + return; + + if ( !uc->par.enabled || usage_count_enabled <= 0 ) + { + uc->ref.elapsed_nsec = 0; + uc->ref.count = 0; + uc->par.dirty = false; + return; + } + + if ( !uc->par.is_active ) + { + if ( !uc->par.used && !uc->ref.count ) + return; + uc->par.is_active = true; + } + + UsageCountEntry_t cur = {0}; + cur.elapsed_nsec = timer_nsec ? timer_nsec : GetTimerNSec(); + + uc->par.update_nsec = uc->ref.elapsed_nsec + NSEC_PER_SEC; + if ( cur.elapsed_nsec >= uc->par.update_nsec ) + { + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( uc->par.used > USAGE_COUNT_ENTRIES ) + uc->par.used = USAGE_COUNT_ENTRIES; + + cur.time_sec = GetTimeSec(false); + + if (uc->ref.elapsed_nsec) + { + //--- find index and move data + + int index; + if ( uc->par.used < USAGE_COUNT_FIRST_ADD ) + index = uc->par.used + 1; + else + { + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + for ( index = USAGE_COUNT_FIRST_ADD; + index < uc->par.used; + index++, min = USAGE_COUNT_NEXT_MIN(min) + ) + { + UsageCountEntry_t *e = uc->entry + index; + if ( e->elapsed_nsec < min ) + { + AddUsageCountEntry(e,e-1); + index--; + break; + } + } + if ( ++index > USAGE_COUNT_ENTRIES ) + index = USAGE_COUNT_ENTRIES; + } + + if ( index > 1 ) + memmove( uc->entry+1, uc->entry, (index-1) * sizeof(*uc->entry) ); + + if ( uc->par.used < index ) + uc->par.used = index; + + + //--- update first entry + + UsageCountEntry_t *e = uc->entry; + *e = uc->ref; + e->elapsed_nsec = cur.elapsed_nsec - e->elapsed_nsec; + + + //--- update top value (1s) + + if (e->elapsed_nsec) + e->top1s = (double)( NSEC_PER_SEC * e->count ) / e->elapsed_nsec; + + + //--- update top value (5s) + + u_nsec_t elapsed5 = 0, count5 = 0; + UsageCountEntry_t *e5 = uc->entry; + for ( int i = 0; i < uc->par.used && elapsed5 < 5*NSEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_nsec; + count5 += e5->count; + } + + if (elapsed5) + { + if ( count5 == e->count ) + elapsed5 = 5*NSEC_PER_SEC; + e->top5s = (double)( NSEC_PER_SEC * count5 ) / elapsed5; + } + } + + uc->ref = cur; + uc->par.update_nsec = uc->ref.elapsed_nsec + NSEC_PER_SEC; + } + + uc->par.force_update_nsec = uc->ref.elapsed_nsec + NSEC_PER_MIN; + uc->par.dirty = uc->ref.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageCountIncrement ( UsageCount_t *uc ) +{ + DASSERT(uc); + uc->par.is_active = true; + UpdateUsageCount(uc,0); + if ( usage_count_enabled > 0 ) + { + uc->ref.count++; + uc->par.dirty = true; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageCountAdd ( UsageCount_t *uc, int add ) +{ + DASSERT(uc); + if ( add > 0 ) + { + uc->par.is_active = true; + UpdateUsageCount(uc,0); + if ( usage_count_enabled > 0 ) + { + uc->ref.count += add; + uc->par.dirty = true; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddUsageCountEntry ( UsageCountEntry_t *dest, const UsageCountEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->count += add->count; + dest->elapsed_nsec += add->elapsed_nsec; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top1s < add->top1s ) + dest->top1s = add->top1s; + if ( dest->top5s < add->top5s ) + dest->top5s = add->top5s; +} + +/////////////////////////////////////////////////////////////////////////////// + +UsageCountEntry_t GetUsageCount ( const UsageCount_t *uc, s_nsec_t nsec ) +{ + DASSERT(uc); + UsageCountEntry_t res = {0}; + const UsageCountEntry_t *e = uc->entry; + for ( int i = 0; i < uc->par.used && nsec > 0; i++, e++ ) + { + nsec -= e->elapsed_nsec; + AddUsageCountEntry(&res,e); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +PointerList_t usage_count_mgr = { .used = 0, .grow = 10 }; + +/////////////////////////////////////////////////////////////////////////////// + +u_nsec_t MaintainUsageByMgr ( u_nsec_t now_nsec ) +{ + // returns delta time to next maintenance based on GetTimerNSec() + + if (!now_nsec) + now_nsec = GetTimerNSec(); + u_nsec_t next_nsec = now_nsec + NSEC_PER_MIN; + + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci = usage_count_mgr.list[i]; + if (!uci) + continue; + + const UsageParam_t *par = GetUsageParam(uci); + if (par) + { + if ( par->dirty && par->update_nsec && par->update_nsec <= now_nsec + || par->force_update_nsec && par->force_update_nsec <= now_nsec ) + { + UpdateByUCI(uci,now_nsec); + } + if ( par->dirty && par->update_nsec && next_nsec > par->update_nsec ) + next_nsec = par->update_nsec; + } + } + + return next_nsec; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateByUCI ( const UsageCtrl_t *uci, u_nsec_t now_nsec ) +{ + if (uci) + { + if (uci->update_func) + uci->update_func(uci,now_nsec); + else if (uci->uc) + UpdateUsageCount(uci->uc,now_nsec); + else if (uci->ud) + UpdateUsageDuration(uci->ud,now_nsec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageByMgr ( u_nsec_t timer_nsec ) +{ + if (!timer_nsec) + timer_nsec = GetTimerNSec(); + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci = usage_count_mgr.list[i]; + if (uci) + { + if (uci->uc) + UpdateUsageCount(uci->uc,timer_nsec); + else if (uci->ud) + UpdateUsageDuration(uci->ud,timer_nsec); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 + static int sort_usage_count ( const UsageCtrl_t *a, const UsageCtrl_t *b ) + { + DASSERT( a && b ); + return strcmp(a->key1,b->key1); + } +#endif + +//----------------------------------------------------------------------------- + +void AddToUsageMgr ( const UsageCtrl_t *uci ) +{ + if ( !uci || !uci->key1 ) + return; + + // no duplicates! + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci2 = usage_count_mgr.list[i]; + if ( uci2 && ( uci2->uc && uci2->uc == uci->uc || uci2->ud && uci2->ud == uci->ud )) + return; + } + + AddToPointerMgr(&usage_count_mgr,uci); + + #if 0 + qsort( usage_count_mgr.list, usage_count_mgr.used, + sizeof(*usage_count_mgr.list), (qsort_func)sort_usage_count ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateByUsageCountMgr ( FILE *f, uint fw_name ) +{ + DASSERT(f); + const u_nsec_t timer_nsec = GetTimerNSec(); + void **ucip = usage_count_mgr.list; + for ( int i = 0; i < usage_count_mgr.used; i++, ucip++ ) + { + UsageCtrl_t *uci = *ucip; + if (uci) + { + UpdateUsageCount(uci->uc,timer_nsec); + if (uci->srt_prefix) + { + if (uci->uc) + SaveCurrentStateByTable(f,uci->uc,SRT_UsageCount,uci->srt_prefix,fw_name); + else if (uci->ud) + SaveCurrentStateByTable(f,uci->ud,SRT_UsageDuration,uci->srt_prefix,fw_name); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateByUsageCountMgr ( RestoreState_t *rs ) +{ + DASSERT(rs); + void **ucip = usage_count_mgr.list; + for ( int i = 0; i < usage_count_mgr.used; i++, ucip++ ) + { + UsageCtrl_t *uci = *ucip; + if (uci) + { + if (uci->uc) + { + UsageCount_t *uc = uci->uc; + if (uci->srt_load) + RestoreStateByTable(rs,uc,SRT_UsageCount,uci->srt_load); + if (uci->srt_prefix) + RestoreStateByTable(rs,uc,SRT_UsageCount,uci->srt_prefix); + + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( uc->par.used > USAGE_COUNT_ENTRIES ) + uc->par.used = USAGE_COUNT_ENTRIES; + } + else if (uci->ud) + { + UsageDuration_t *ud = uci->ud; + if (uci->srt_load) + RestoreStateByTable(rs,ud,SRT_UsageDuration,uci->srt_load); + if (uci->srt_prefix) + RestoreStateByTable(rs,ud,SRT_UsageDuration,uci->srt_prefix); + + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( ud->par.used > USAGE_COUNT_ENTRIES ) + ud->par.used = USAGE_COUNT_ENTRIES; + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// wait counter /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_UsageDuration[] = +{ + #undef SRT_NAME + #define SRT_NAME UsageDuration_t + + DEF_SRT_UINT( par.enabled, "enabled" ), + DEF_SRT_UINT( par.used, "used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].elapsed_nsec, "elapsed" ), + DEF_SRT_UINT( entry[0].total_nsec, "total" ), + DEF_SRT_UINT( entry[0].top_nsec, "top" ), + DEF_SRT_UINT( entry[0].count, "count" ), + DEF_SRT_UINT( entry[0].time_sec, "time" ), + DEF_SRT_FLOAT( entry[0].top_cnt_1s, "top-c1" ), + DEF_SRT_FLOAT( entry[0].top_cnt_5s, "top-c5" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +// n_rec<0: clear from end, n_rec>=0: clear from index +void ClearUsageDuration ( UsageDuration_t *ud, int count ) +{ + if (!ud) + return; + + uint index = CheckIndex1(ud->par.used,count); + if ( index >= ud->par.used - 1 ) + return; + + ud->par.used = index; + if (!index) + { + const bool enabled = ud->par.enabled; + memset( ud, 0, sizeof(*ud) ); + ud->par.enabled = enabled; + } + else + { + UsageDurationEntry_t *e = ud->entry + index; + memset( e, 0, PTR_DISTANCE((u8*)ud->entry+sizeof(ud->entry),e) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[UpdateUsageDuration]] + +// TIMER_NSEC: 0 or GetTimerNSec(), but not GetTime*() +void UpdateUsageDuration ( UsageDuration_t *ud, u_nsec_t timer_nsec ) +{ + if (!ud) + return; + + if ( !ud->par.enabled || usage_count_enabled <= 0 ) + { + ud->ref.elapsed_nsec = 0; + ud->ref.count = 0; + ud->par.dirty = false; + return; + } + + if ( !ud->par.is_active ) + { + if ( !ud->par.used && !ud->ref.count ) + return; + ud->par.is_active = true; + } + + UsageDurationEntry_t cur = {0}; + cur.elapsed_nsec = timer_nsec ? timer_nsec : GetTimerNSec(); + + ud->par.update_nsec = ud->ref.elapsed_nsec + NSEC_PER_SEC; + if ( cur.elapsed_nsec >= ud->par.update_nsec ) + { + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( ud->par.used > USAGE_COUNT_ENTRIES ) + ud->par.used = USAGE_COUNT_ENTRIES; + + cur.time_sec = GetTimeSec(false); + + if (ud->ref.elapsed_nsec) + { + //--- find index and move data + + int index; + if ( ud->par.used < USAGE_COUNT_FIRST_ADD ) + index = ud->par.used + 1; + else + { + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + for ( index = USAGE_COUNT_FIRST_ADD; + index < ud->par.used; + index++, min = USAGE_COUNT_NEXT_MIN(min) + ) + { + UsageDurationEntry_t *e = ud->entry + index; + if ( e->elapsed_nsec < min ) + { + AddUsageDurationEntry(e,e-1); + index--; + break; + } + } + if ( ++index > USAGE_COUNT_ENTRIES ) + index = USAGE_COUNT_ENTRIES; + } + + if ( index > 1 ) + memmove( ud->entry+1, ud->entry, (index-1) * sizeof(*ud->entry) ); + + if ( ud->par.used < index ) + ud->par.used = index; + + + //--- update first entry + + UsageDurationEntry_t *e = ud->entry; + *e = ud->ref; + e->elapsed_nsec = cur.elapsed_nsec - e->elapsed_nsec; + + + //--- update top value (1s) + + if (e->elapsed_nsec) + e->top_cnt_1s = (double)( NSEC_PER_SEC * e->count ) / e->elapsed_nsec; + + + //--- update top value (5s) + + u_nsec_t elapsed5 = 0, count5 = 0; + UsageDurationEntry_t *e5 = ud->entry; + for ( int i = 0; i < ud->par.used && elapsed5 < 5*NSEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_nsec; + count5 += e5->count; + } + + if (elapsed5) + { + if ( count5 == e->count ) + elapsed5 = 5*NSEC_PER_SEC; + e->top_cnt_5s = (double)( NSEC_PER_SEC * count5 ) / elapsed5; + } + } + + ud->ref = cur; + ud->par.update_nsec = ud->ref.elapsed_nsec + NSEC_PER_SEC; + } + + ud->par.force_update_nsec = ud->ref.elapsed_nsec + NSEC_PER_MIN; + ud->par.dirty = ud->ref.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageDurationAdd + ( UsageDuration_t *ud, int add, u_nsec_t wait_nsec, u_nsec_t top_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + UpdateUsageDuration(ud,0); + + if ( add > 0 && usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count += add; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < top_nsec ) + ud->ref.top_nsec = top_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + UpdateUsageDuration(ud,0); + if ( usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count++; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < wait_nsec ) + ud->ref.top_nsec = wait_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + if ( usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count++; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < wait_nsec ) + ud->ref.top_nsec = wait_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +UsageDurationEntry_t GetUsageDuration ( const UsageDuration_t *ud, s_nsec_t nsec ) +{ + DASSERT(ud); + UsageDurationEntry_t res = {0}; + const UsageDurationEntry_t *e = ud->entry; + for ( int i = 0; i < ud->par.used && nsec > 0; i++, e++ ) + { + nsec -= e->elapsed_nsec; + AddUsageDurationEntry(&res,e); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddUsageDurationEntry ( UsageDurationEntry_t *dest, const UsageDurationEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->count += add->count; + dest->elapsed_nsec += add->elapsed_nsec; + dest->total_nsec += add->total_nsec; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top_nsec < add->top_nsec ) + dest->top_nsec = add->top_nsec; + + if ( dest->top_cnt_1s < add->top_cnt_1s ) + dest->top_cnt_1s = add->top_cnt_1s; + if ( dest->top_cnt_5s < add->top_cnt_5s ) + dest->top_cnt_5s = add->top_cnt_5s; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// list usage count/wait /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_usage_count_val ( double val, const float *color_val ) +{ + if ( val <= 0 ) + return " -"; + + static const float color_val0[] = { 100, 200, 300, 500, 1000, 2000 }; + if (!color_val) + color_val = color_val0; + + if ( val > color_val[0] || val >= 9999.9994 ) + { + ccp col = val < color_val[2] + ? ( val < color_val[1] ? colout->mark : colout->info ) + : val < color_val[4] + ? ( val < color_val[3] ? colout->hint : colout->warn ) + : ( val < color_val[5] ? colout->err : colout->bad ); + + return val < 9999.9994 + ? PrintCircBuf("%s%8.3f%s",col,val,colout->reset) + : val < 999999.94 + ? PrintCircBuf("%s%8.1f%s",col,val,colout->reset) + : PrintCircBuf("%s%8.0f%s",col,val,colout->reset); + } + + char *res = PrintCircBuf("%8.3f",val); + if ( val < 1.0 && res[3] == '0' ) + res[3] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_count + ( u32 n_wait, u_nsec_t elapsed_nsec, const float *color_val ) +{ + return elapsed_nsec + ? print_usage_count_val( (double)( NSEC_PER_SEC * n_wait ) / elapsed_nsec, color_val ) + : " ×"; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_dur_val ( u_nsec_t dur, const u_nsec_t *color_dur ) +{ + if (!dur) + return " -"; + + ccp res = PrintTimerNSec6(0,0,dur,DC_SFORM_ALIGN); + + static const u_nsec_t color_dur0[] = + { + 10 * NSEC_PER_MSEC, // mark + 50 * NSEC_PER_MSEC, // info + 200 * NSEC_PER_MSEC, // hint + NSEC_PER_SEC, // warn + 8 * NSEC_PER_SEC, // err + NSEC_PER_MIN, // bad + }; + if (!color_dur) + color_dur = color_dur0; + + if ( dur > color_dur[0] ) + { + ccp col = dur < color_dur[2] + ? ( dur < color_dur[1] ? colout->mark : colout->info ) + : dur < color_dur[4] + ? ( dur < color_dur[3] ? colout->hint : colout->warn ) + : ( dur < color_dur[5] ? colout->err : colout->bad ); + + return PrintCircBuf("%s%s%s",col,res,colout->reset); + } + + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_dur + ( u_nsec_t total, u64 count, const u_nsec_t *color_dur ) +{ + return count + ? print_usage_dur_val( total/count, color_dur ) + : " -"; +} + +// +/////////////////////////////////////////////////////////////////////////////// + +void ListUsageCount +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + if (!uci) + return; + + if (uci->list_func) + { + uci->list_func(p_pm,uci,print_mode,limit_nsec); + return; + } + + if (!uci->uc) + { + if (uci->ud) + ListUsageDuration(p_pm,uci,print_mode,limit_nsec); + return; + } + + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + if ( limit_nsec <= 0 ) + limit_nsec = S_NSEC_MAX; + + UsageCount_t *uc = uci->uc; + fprintf(pm.fout,"%s%s%*s %s%s (%u record%s%s%s)%s%s", + pm.compact ? "" : pm.eol, pm.prefix, pm.indent, "", + pm.cout->caption, uci->title, + uc->par.used, uc->par.used == 1 ? "" : "s", + usage_count_enabled > 0 ? "" : ", global disabled", + uc->par.enabled ? "" : ", disabled", + pm.cout->reset, pm.eol ); + + if (!uc->par.used) + return; + + + //--- print table + + UsageCountEntry_t sum = {0}; + u_sec_t reftime = 0; + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + fprintf(pm.fout, + "%s%*s%s╔════════╦════════╤═════════╤══════════╦═══════════╤═══════════╗%s" + "%s%*s%s║ range ║ period │ counter │ count/s ║ top~1s /s │ top~5s /s ║%s" + "%s%*s%s╠════════╬════════╪═════════╪══════════╬═══════════╪═══════════╣%s%s" + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,colout->reset ,pm.eol + ); + + const IntervalInfo_t *interval = interval_info; + const UsageCountEntry_t *e = uc->entry; + for ( int i = 0; i < uc->par.used; i++, e++ ) + { + AddUsageCountEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_nsec >= limit_nsec; + if ( sum.elapsed_nsec < interval->nsec ) + { + is_last = i+1 == uc->par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_nsec < interval->nsec ) + name = "*"; + else + { + while ( sum.elapsed_nsec > interval->nsec ) + interval++; + name = interval[-1].name; + } + + fprintf(pm.fout, + "%s%*s%s║%s %-6.6s %s║%s %s %s│%s %s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s║%s" + ,print_usage_count_val(sum.top1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(sum.top5s,uci->color_val) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + + fprintf(pm.fout, + "%s ╚════════╩════════╧═════════╧══════════╩═══════════╧═══════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } + else + { + fprintf(pm.fout, + "%s%*s%s╔═══════════╦═════════════════════════════╦══════════╦══════════╦═════════════════════════════╗%s" + "%s%*s%s║ ║ Single Record ║ top 1s ║ top 5s ║ Summed Values ║%s" + "%s%*s%s║ real time ║ period │ counter │ count/s ║ count/s ║ count/s ║ period │ counter │ count/s ║%s" + "%s%*s%s╠═══════════╬════════╪═════════╪══════════╬══════════╬══════════╬════════╪═════════╪══════════╣%s%s" + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,colout->reset ,pm.eol + ); + + const IntervalInfo_t *interval = interval_info+1; + const UsageCountEntry_t *e = uc->entry; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= uc->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #else + for ( int i = 0; i < uc->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #endif + { + AddUsageCountEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼──────────╫" + "──────────╫──────────╫" + "────────┼─────────┼──────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s%s %s║%s %s %s│%s %s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,e->elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,e->count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(e->count,e->elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s║%s %s %s║%s" + ,print_usage_count_val(e->top1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(e->top5s,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s│%s %s %s║%s" + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + if (pm.debug) + { + if ( pm.debug >= 2 && i >= USAGE_COUNT_FIRST_ADD ) + { + fprintf(pm.flog," %2u %s %s%4llu%%%s %s%5llu%%%s%s", + i, + PrintTimerNSec6(0,0,min,DC_SFORM_ALIGN|DC_SFORM_DASH), + e->elapsed_nsec < min ? "" + : e->elapsed_nsec < min*2 ? colout->hint : colout->warn, + 100 * e->elapsed_nsec / min, colout->reset, + sum.elapsed_nsec < min ? colout->info : "", + 100 * sum.elapsed_nsec / min, colout->reset, pm.eol ); + min = USAGE_COUNT_NEXT_MIN(min); + } + else + fprintf(pm.flog," %2u%s",i,pm.eol); + } + else + fputs(pm.eol,pm.fout); + } + + fprintf(pm.fout, + "%s ╚═══════════╩════════╧═════════╧══════════╩" + "══════════╩══════════╩" + "════════╧═════════╧══════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// + +void ListUsageDuration +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + if (!uci) + return; + + if (uci->list_func) + { + uci->list_func(p_pm,uci,print_mode,limit_nsec); + return; + } + + if (!uci->ud) + { + if (uci->uc) + ListUsageCount(p_pm,uci,print_mode,limit_nsec); + return; + } + + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + if ( limit_nsec <= 0 ) + limit_nsec = S_NSEC_MAX; + + UsageDuration_t *ud = uci->ud; + fprintf(pm.fout,"%s%s%*s %s%s (%u record%s%s%s)%s%s", + pm.compact ? "" : pm.eol, pm.prefix, pm.indent, "", + pm.cout->caption, uci->title, + ud->par.used, ud->par.used == 1 ? "" : "s", + usage_count_enabled > 0 ? "" : ", global disabled", + ud->par.enabled ? "" : ", disabled", + pm.cout->reset, pm.eol ); + + if (!ud->par.used) + return; + + + //--- print table + + UsageDurationEntry_t sum = {0}; + u_sec_t reftime = 0; + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + printf( + "%s ╔════════╦═════════════════════════════════════╦═════════╤═════════╤════════╗%s" + "%s ║ ║ Summed Record ║ top 1s │ top 5s │ top ║%s" + "%s ║ range ║ period │ counter │ count/s │ Ø time ║ count/s │ count/s │ time ║%s" + "%s ╠════════╬════════╪═════════╪═════════╪════════╬═════════╪═════════╪════════╣%s%s" + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading ,colout->reset, pm.eol + ); + + const IntervalInfo_t *interval = interval_info; + const UsageDurationEntry_t *e = ud->entry; + for ( int i = 0; i < ud->par.used; i++, e++ ) + { + AddUsageDurationEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_nsec >= limit_nsec; + if ( sum.elapsed_nsec < interval->nsec ) + { + is_last = i+1 == ud->par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_nsec < interval->nsec ) + name = "*"; + else + { + while ( sum.elapsed_nsec > interval->nsec ) + interval++; + name = interval[-1].name; + } + + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼─────────┼────────╫" + "─────────┼────────╫─────────┼────────╫" + "────────┼─────────┼─────────┼────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s %-6.6s %s║%s %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(sum.total_nsec,sum.count,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + "%s %s│%s%s %s│%s %s %s║%s" + ,print_usage_count_val(sum.top_cnt_1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(sum.top_cnt_5s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur_val(sum.top_nsec,uci->color_dur) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + + fprintf(pm.fout, + "%s ╚════════╩════════╧═════════╧═════════╧════════╩" + "═════════╧═════════╧════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } + else + { + printf( + "%s ╔═══════════╦═════════════════════════════════════╦═════════╤═════════╤════════╦═════════════════════════════════════╗%s" + "%s ║ ║ Single Record ║ top 1s │ top 5s │ top ║ Summed Values ║%s" + "%s ║ real time ║ period │ counter │ count/s │ Ø time ║ count/s │ count/s │ time ║ period │ counter │ count/s │ Ø time ║%s" + "%s ╠═══════════╬════════╪═════════╪═════════╪════════╬═════════╪═════════╪════════╬════════╪═════════╪═════════╪════════╣%s%s" + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading ,colout->reset, pm.eol + ); + + const IntervalInfo_t *interval = interval_info+1; + const UsageDurationEntry_t *e = ud->entry; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= ud->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #else + for ( int i = 0; i < ud->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #endif + { + AddUsageDurationEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼─────────┼────────╫" + "─────────┼─────────┼────────╫" + "────────┼─────────┼─────────┼────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s%s %s║%s %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,e->elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,e->count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(e->count,e->elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(e->total_nsec,e->count,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + "%s %s│%s%s %s│%s %s %s║%s" + ,print_usage_count_val(e->top_cnt_1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(e->top_cnt_5s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur_val(e->top_nsec,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(sum.total_nsec,sum.count,uci->color_dur) + ,colout->heading, colout->reset + ); + + if (pm.debug) + { + if ( pm.debug >= 2 && i >= USAGE_COUNT_FIRST_ADD ) + { + fprintf(pm.flog," %2u %s %s%4llu%%%s %s%5llu%%%s%s", + i, + PrintTimerNSec6(0,0,min,DC_SFORM_ALIGN|DC_SFORM_DASH), + e->elapsed_nsec < min ? "" + : e->elapsed_nsec < min*2 ? colout->hint : colout->warn, + 100 * e->elapsed_nsec / min, colout->reset, + sum.elapsed_nsec < min ? colout->info : "", + 100 * sum.elapsed_nsec / min, colout->reset, pm.eol ); + min = USAGE_COUNT_NEXT_MIN(min); + } + else + fprintf(pm.flog," %2u%s",i,pm.eol); + } + else + fputs(pm.eol,pm.fout); + } + + fprintf(pm.fout, + "%s ╚═══════════╩════════╧═════════╧═════════╧════════╩" + "═════════╧═════════╧════════╩" + "════════╧═════════╧═════════╧════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cpu usage /////////////// +/////////////////////////////////////////////////////////////////////////////// + +CpuUsage_t cpu_usage = { .par.used=0, .par.enabled = CPU_USAGE_ENABLED }; + +const UsageCtrl_t cpu_usage_ctrl = +{ + .par = &cpu_usage.par, + .update_func = UpdateCpuUsageByUCI, + .list_func = ListCpuUsage, + .title = "CPU usage (extern)", + .key1 = "CPU", +}; + +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_cpu_percent ( double val ) +{ + if (!val) + return " -"; + + //static const double collist[] = { 40, 60, 80, 95 }; + static const double collist[] = { 25, 50, 75, 95 }; + + if ( val >= collist[0] ) + { + ccp col = val < collist[2] + ? ( val < collist[1] ? colout->info : colout->hint ) + : ( val < collist[3] ? colout->warn : colout->err ); + return PrintCircBuf("%s%6.2f%s", col, val, colout->reset ); + } + + char *res = PrintCircBuf("%6.2f",val); + if ( val < 1.0 && res[2] == '0' ) + res[2] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_seconds ( u_usec_t val ) +{ + return !val + ? " -" + : val < USEC_PER_SEC + ? PrintCircBuf("%8llu",val) + : val < 10*USEC_PER_SEC + ? PrintCircBuf("%llu.%06llu",val/USEC_PER_SEC,val%USEC_PER_SEC) + : PrintCircBuf("%4llu.%03llu",val/USEC_PER_SEC,val%USEC_PER_SEC/1000); +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_wait_val ( double val ) +{ + static const double collist[] = { 100, 150, 200, 250, 300, 350 }; + if ( val > collist[0] ) + { + if ( val >= 999.0 ) + return PrintCircBuf(" %s%6.0f%s",colout->fatal,val,colout->reset); + + ccp col = val < collist[2] + ? ( val < collist[1] ? colout->mark : colout->info ) + : val < collist[4] + ? ( val < collist[3] ? colout->hint : colout->warn ) + : ( val < collist[5] ? colout->err : colout->bad ); + + return PrintCircBuf("%s%7.2f%s",col,val,colout->reset); + } + + if ( val <= 0 ) + return " -"; + + char * res = PrintCircBuf("%7.2f",val); + if ( val < 1.0 && res[3] == '0' ) + res[3] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_wait ( u32 n_wait, u_usec_t elapsed_usec ) +{ + return elapsed_usec + ? print_cpu_wait_val( (double)( USEC_PER_SEC * n_wait ) / elapsed_usec ) + : " ×"; +} + +//----------------------------------------------------------------------------- + +static void print_cpu_debug + ( int idx, u_usec_t *min, u_usec_t elapsed1, u_usec_t elapsed2 ) +{ + DASSERT(min); + + if ( idx >= CPU_USAGE_FIRST_ADD && *min ) + { + printf(" %2u %s %s%4llu%%%s %s%4llu%%%s\n", + idx, + PrintTimerUSec6(0,0,*min,DC_SFORM_ALIGN|DC_SFORM_DASH), + elapsed1 < *min ? "" : elapsed1 < *min*2 ? colout->hint : colout->warn, + 100 * elapsed1 / *min, colout->reset, + elapsed2 < *min ? colout->info : "", 100 * elapsed2 / *min, colout->reset ); + *min = CPU_USAGE_NEXT_MIN(*min); + } + else + printf(" %2u\n",idx); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ListCpuUsage +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // ignored! + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + const u_usec_t limit_usec = limit_nsec > 0 ? limit_nsec/NSEC_PER_USEC : S_USEC_MAX; + + + //--- print header + + const ccp empty_line = pm.compact ? "" : "\n"; + printf("%s%s CPU Usage (%u record%s%s)%s\n", + empty_line, colout->caption, + cpu_usage.par.used, cpu_usage.par.used == 1 ? "" : "s", + cpu_usage.par.enabled ? "" : ", disabled", colout->reset ); + + if (!cpu_usage.par.used) + return; + + + //--- print table + + CpuUsageEntry_t sum = {0}; + u_sec_t reftime = 0; + u_usec_t min = CPU_USAGE_NEXT_MIN(USEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + printf( + "%s ╔════════╦═════════════════════════════════════════╦════════════════╦════════════════╗\n" + "%s ║ ║ Summed Values ║ top 1s ║ top 5s ║\n" + "%s ║ range ║ period │ user%% │system%%│total%% │ wait/s ║total%% │ wait/s ║total%% │ wait/s ║\n" + "%s ╠════════╬════════╪═══════╪═══════╪═══════╪════════╬═══════╪════════╬═══════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info; + const CpuUsageEntry_t *e = cpu_usage.entry; + for ( int i = 0; i < cpu_usage.par.used; i++, e++ ) + { + AddCpuEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_usec >= limit_usec; + if ( sum.elapsed_usec < interval->usec ) + { + is_last = i+1 == cpu_usage.par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_usec < interval->usec ) + name = "*"; + else + { + while ( sum.elapsed_usec > interval->usec ) + interval++; + name = interval[-1].name; + } + + const double elapsed = 100.0 / sum.elapsed_usec; + printf(" %s║%s %-6.6s %s║%s %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( sum.utime_usec + sum.stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + printf("%s %s│%s%s %s║%s%s %s│%s%s %s║%s" + ,print_cpu_percent(sum.top1s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(sum.topw1s) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.top5s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(sum.topw5s) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + printf( + "%s ╚════════╩════════╧═══════╧═══════╧═══════╧════════╩" + "═══════╧════════╩═══════╧════════╝%s%s%s", + colout->heading ,colout->reset, pm.eol, empty_line ); + } + else if ( print_mode == LUC_PM_RAW ) + { + printf( + "%s ╔═══════════╦══════════════════════════════════════════════════╦══════════════════════════════════════════════════╗\n" + "%s ║ ║ Single Record (µs) ║ Summed Values (µs) ║\n" + "%s ║ real time ║ period │ user │ system │ total │ wait/s ║ period │ user │ system │ total │ wait/s ║\n" + "%s ╠═══════════╬════════╪══════════╪══════════╪══════════╪════════╬════════╪══════════╪══════════╪══════════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info+1; + for ( int i = 0; i < cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + { + const CpuUsageEntry_t *e = cpu_usage.entry+i; + AddCpuEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_usec >= interval->usec || i == CPU_USAGE_FIRST_ADD )) + { + printf( + "%s ╟───────────╫────────┼──────────┼──────────┼──────────┼────────╫" + "────────┼──────────┼──────────┼──────────┼────────╢%s\n", + colout->heading, colout->reset ); + while ( sum.elapsed_usec >= interval->usec ) + interval++; + } + + printf(" %s║%s%s %s║%s %s %s│%s %s %s│%s %s %s│%s %s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,e->elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_seconds(e->utime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds(e->stime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds( e->utime_usec + e->stime_usec ) + ,colout->heading, colout->reset + ,print_cpu_wait(e->n_wait,e->elapsed_usec) + ,colout->heading, colout->reset + ); + + printf(" %s %s│%s %s %s│%s %s %s│%s %s %s│%s%s %s║%s" + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_seconds(sum.utime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds(sum.stime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds( sum.utime_usec + sum.stime_usec ) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + if (pm.debug) + print_cpu_debug(i,&min,e->elapsed_usec,sum.elapsed_usec); + else + putchar('\n'); + } + + printf( + "%s ╚═══════════╩════════╧══════════╧══════════╧══════════╧════════╩" + "════════╧══════════╧══════════╧══════════╧════════╝%s\n%s", + colout->heading, colout->reset, empty_line ); + } + else + { + printf( + "%s ╔═══════════╦═════════════════════════════════════════╦════════════════╦════════════════╦═════════════════════════════════════════╗\n" + "%s ║ ║ Single Record ║ top 1s ║ top 5s ║ Summed Values ║\n" + "%s ║ real time ║ period │ user%% │system%%│total%% │ wait/s ║total%% │ wait/s ║total%% │ wait/s ║ period │ user%% │system%%│total%% │ wait/s ║\n" + "%s ╠═══════════╬════════╪═══════╪═══════╪═══════╪════════╬═══════╪════════╬═══════╪════════╬════════╪═══════╪═══════╪═══════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info+1; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + #else + for ( int i = 0; i < cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + #endif + { + const CpuUsageEntry_t *e = cpu_usage.entry+i; + AddCpuEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_usec >= interval->usec || i == CPU_USAGE_FIRST_ADD )) + { + printf( + "%s ╟───────────╫────────┼───────┼───────┼───────┼────────╫" + "───────┼────────╫───────┼────────╫" + "────────┼───────┼───────┼───────┼────────╢%s\n", + colout->heading, colout->reset ); + while ( sum.elapsed_usec >= interval->usec ) + interval++; + } + + double elapsed = 100.0 / e->elapsed_usec; + printf(" %s║%s%s %s║%s %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,e->elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(e->utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(e->stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( e->utime_usec + e->stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(e->n_wait,e->elapsed_usec) + ,colout->heading, colout->reset + ); + + printf("%s %s│%s%s %s║%s%s %s│%s%s %s║%s" + ,print_cpu_percent(e->top1s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(e->topw1s) + ,colout->heading, colout->reset + ,print_cpu_percent(e->top5s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(e->topw5s) + ,colout->heading, colout->reset + ); + + elapsed = 100.0 / sum.elapsed_usec; + printf(" %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( sum.utime_usec + sum.stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + if (pm.debug) + print_cpu_debug(i,&min,e->elapsed_usec,sum.elapsed_usec); + else + putchar('\n'); + } + + printf( + "%s ╚═══════════╩════════╧═══════╧═══════╧═══════╧════════╩" + "═══════╧════════╩═══════╧════════╩" + "════════╧═══════╧═══════╧═══════╧════════╝%s%s", + colout->heading, colout->reset, empty_line ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_CpuUsage[] = +{ + #undef SRT_NAME + #define SRT_NAME CpuUsage_t + + DEF_SRT_COMMENT("cpu_usage"), + DEF_SRT_SEPARATOR(), + + DEF_SRT_INT( par.enabled, "cpu-enabled" ), + DEF_SRT_UINT( par.used, "cpu-used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].elapsed_usec, "cpu-elapsed" ), + DEF_SRT_UINT( entry[0].utime_usec, "cpu-utime" ), + DEF_SRT_UINT( entry[0].stime_usec, "cpu-stime" ), + DEF_SRT_UINT( entry[0].time_sec, "cpu-rtime" ), + DEF_SRT_FLOAT( entry[0].top1s, "cpu-top1s" ), + DEF_SRT_FLOAT( entry[0].top5s, "cpu-top5s" ), + DEF_SRT_UINT( entry[0].n_wait, "cpu-nwait" ), + DEF_SRT_FLOAT( entry[0].topw1s, "cpu-topw1s" ), + DEF_SRT_FLOAT( entry[0].topw5s, "cpu-topw5s" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +//----------------------------------------------------------------------------- +// [[UpdateCpuUsage]] + +void UpdateCpuUsageByUCI ( const UsageCtrl_t *uci, u_nsec_t now_nsec ) +{ + UpdateCpuUsage(); +} + +//----------------------------------------------------------------------------- + +void UpdateCpuUsage() +{ + if (!cpu_usage.par.enabled) + { + cpu_usage.ref.elapsed_usec = 0; + cpu_usage.ref.n_wait = 0; + return; + } + cpu_usage.par.is_active = true; + + CpuUsageEntry_t cur = {0}; + cur.elapsed_usec = GetTimerUSec(); + + if ( cur.elapsed_usec >= cpu_usage.ref.elapsed_usec + USEC_PER_SEC ) + { + // needed for the case that CPU_USAGE_ENTRIES was reduced after restart! + if ( cpu_usage.par.used > CPU_USAGE_ENTRIES ) + cpu_usage.par.used = CPU_USAGE_ENTRIES; + + struct rusage ru; + if (!getrusage(RUSAGE_SELF,&ru)) + { + cur.time_sec = GetTimeSec(false); + cur.utime_usec = ru.ru_utime.tv_sec * USEC_PER_SEC + ru.ru_utime.tv_usec; + cur.stime_usec = ru.ru_stime.tv_sec * USEC_PER_SEC + ru.ru_stime.tv_usec; + + if (cpu_usage.ref.elapsed_usec) + { + //--- find index and move data + + int index; + if ( cpu_usage.par.used < CPU_USAGE_FIRST_ADD ) + index = cpu_usage.par.used + 1; + else + { + u_usec_t min = CPU_USAGE_NEXT_MIN(USEC_PER_SEC); + for ( index = CPU_USAGE_FIRST_ADD; + index < cpu_usage.par.used; + index++, min = CPU_USAGE_NEXT_MIN(min) + ) + { + CpuUsageEntry_t *e = cpu_usage.entry + index; + if ( e->elapsed_usec < min ) + { + AddCpuEntry(e,e-1); + index--; + break; + } + } + if ( ++index > CPU_USAGE_ENTRIES ) + index = CPU_USAGE_ENTRIES; + } + + if ( index > 1 ) + memmove( cpu_usage.entry+1, cpu_usage.entry, + (index-1) * sizeof(*cpu_usage.entry) ); + + if ( cpu_usage.par.used < index ) + cpu_usage.par.used = index; + + + //--- update first entry + + CpuUsageEntry_t *e = cpu_usage.entry; + *e = cpu_usage.ref; + e->elapsed_usec = cur.elapsed_usec - cpu_usage.ref.elapsed_usec; + e->utime_usec = cur.utime_usec - cpu_usage.ref.utime_usec; + e->stime_usec = cur.stime_usec - cpu_usage.ref.stime_usec; + + + //--- update top values (1s) + + if (e->elapsed_usec) + { + e->top1s = 100.0 * ( e->utime_usec + e->stime_usec ) / e->elapsed_usec; + e->topw1s = (double)( USEC_PER_SEC * e->n_wait ) / e->elapsed_usec; + } + + + //--- update top values (5s) + + u_usec_t elapsed5 = 0, total5 = 0, wait5 = 0; + CpuUsageEntry_t *e5 = cpu_usage.entry; + for ( int i = 0; i < cpu_usage.par.used && elapsed5 < 5*USEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_usec; + total5 += e5->utime_usec + e5->stime_usec; + wait5 += e5->n_wait; + } + + if (elapsed5) + { + if ( wait5 == e->n_wait && total5 == e->utime_usec + e->stime_usec ) + elapsed5 = 5*NSEC_PER_SEC; + e->top5s = 100.0 * total5 / elapsed5; + e->topw5s = (double)( USEC_PER_SEC * wait5 ) / elapsed5; + } + } + + cpu_usage.ref = cur; + } + } +} + +//----------------------------------------------------------------------------- + +void UpdateCpuUsageIncrement() +{ + UpdateCpuUsage(); + cpu_usage.ref.n_wait++; +} + +//----------------------------------------------------------------------------- + +void AddCpuEntry ( CpuUsageEntry_t *dest, const CpuUsageEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->elapsed_usec += add->elapsed_usec; + dest->utime_usec += add->utime_usec; + dest->stime_usec += add->stime_usec; + dest->n_wait += add->n_wait; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top1s < add->top1s ) + dest->top1s = add->top1s; + if ( dest->top5s < add->top5s ) + dest->top5s = add->top5s; + + if ( dest->topw1s < add->topw1s ) + dest->topw1s = add->topw1s; + if ( dest->topw5s < add->topw5s ) + dest->topw5s = add->topw5s; +} + +//----------------------------------------------------------------------------- + +CpuUsageEntry_t GetCpuUsage ( s_usec_t usec ) +{ + CpuUsageEntry_t res = {0}; + for ( int i = 0; i < cpu_usage.par.used && usec > 0; i++ ) + { + const CpuUsageEntry_t *e = cpu_usage.entry + i; + usec -= e->elapsed_usec; + AddCpuEntry(&res,e); + } + return res; +} + +//----------------------------------------------------------------------------- + +uint GetCpuUsagePercent ( s_usec_t usec ) +{ + CpuUsageEntry_t e = GetCpuUsage(usec); + return GetCpuEntryPercent(&e); +} + +/////////////////////////////////////////////////////////////////////////////// + +double get_rusage_percent (void) +{ + static u_nsec_t prev_time = 0; + static u_usec_t prev_rusage = 0; + static double prev_percent = 0.0; + + const u_nsec_t cur_time = GetTimerNSec(); + if ( cur_time < prev_time + 10*NSEC_PER_MSEC ) + return prev_percent; + + u_usec_t cur_rusage = prev_rusage; + struct rusage ru; + if (!getrusage(RUSAGE_SELF,&ru)) + { + cur_rusage = ru.ru_utime.tv_sec * USEC_PER_SEC + ru.ru_utime.tv_usec + + ru.ru_stime.tv_sec * USEC_PER_SEC + ru.ru_stime.tv_usec; + } + + double percent = 0.0; + if ( prev_time && cur_time > prev_time ) + percent = (double)( 100 * NSEC_PER_USEC * ( cur_rusage - prev_rusage )) + / ( cur_time - prev_time ); + + prev_time = cur_time; + prev_rusage = cur_rusage; + + return prev_percent = percent; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// urandom support /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -3718,7 +6177,7 @@ const u32 RANDOM32_C_ADD = 2 * 197731421; // 2 Primzahlen const u32 RANDOM32_COUNT_BASE = 4294967; // Primzahl == ~UINT_MAX32/1000; static int random32_a_index = -1; // Index in die a-Tabelle -static u32 random32_count = 1; // Abwärts-Zähler bis zum Wechsel von a,c +static u32 random32_count = 1; // Abwärts-Zähler bis zum Wechsel von a,c static u32 random32_a, random32_c, random32_X; // Die letzten Werte @@ -3750,7 +6209,7 @@ u32 MyRandom ( u32 max ) if (!--random32_count) { - // Neue Berechnung von random32_a und random32_c fällig + // Neue Berechnung von random32_a und random32_c fällig if ( random32_a_index < 0 ) { @@ -4295,8 +6754,23 @@ char * ScanS32 while ( *src > 0 && *src <= ' ' ) src++; } + #if 0 // 0b and 0d can be hex numbers => so this doeasn't work + uint base = default_base; + if ( src[0] == '0' ) + { + switch(tolower((int)src[1])) + { + case 'x': base = 16; break; + case 'd': base = 10; src+=2; break; + case 'o': base = 8; src+=2; break; + case 'b': base = 2; src+=2; break; + } + } + #else const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' ) ? 16 : default_base; + #endif + char *end; const s32 num = strtoul( src, &end, base ); if ( (ccp)end > src ) @@ -5734,14 +8208,27 @@ char * ScanSizeRange u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { + if (!opt) + opt = RAOPT__DEFAULT; + int count = 0; - double d1, d2; + double d1 = 0, d2; + + bool have_d1 = false; + if ( opt & RAOPT_ASSUME_0 ) + { + have_d1 = *source == ':' && opt & RAOPT_COLON + || *source == '#' && opt & RAOPT_HASH + || *source == ',' && opt & RAOPT_COMMA; + } - char *end = ScanSize(&d1,source,default_factor,default_factor_add,force_base); - if ( end == source ) + char *end = have_d1 ? (char*)source + : ScanSize(&d1,source,default_factor,default_factor_add,force_base); + if ( !have_d1 && end == source ) { d1 = d2 = 0.0; count = 0; @@ -5750,13 +8237,33 @@ char * ScanSizeRange { count++; d2 = d1; - if ( *end == '#' || *end == ':' ) + + const bool have_range = *end == ':' && opt & RAOPT_COLON + || *end == '-' && opt & RAOPT_MINUS; + const bool have_size = *end == '#' && opt & RAOPT_HASH + || *end == ',' && opt & RAOPT_COMMA; + + if ( have_range || have_size ) { + if ( d1 < 0 && max_value > 0 && opt & RAOPT_NEG ) + { + d1 += max_value; + if ( d1 < 0 ) + d1 = 0; + } + source = end; end = ScanSize(&d2,source+1,default_factor,default_factor_add,force_base); + if ( have_range && d2 < 0 && max_value > 0 && opt & RAOPT_NEG ) + { + d2 += max_value; + if ( d2 < 0 ) + d2 = 0; + } + if ( end == source+1 ) { - if ( *source == ':' && max_value > 0.0 ) + if ( have_range && max_value > 0.0 ) { count++; d2 = max_value; @@ -5772,11 +8279,14 @@ char * ScanSizeRange else { count++; - if ( *source == '#' ) + if ( have_size ) d2 += d1-1; } if ( d2 < d1 ) + { + count = 0; d2 = d1; + } } } @@ -5802,16 +8312,21 @@ char * ScanSizeRangeU32 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { double d1, d2; char * end = ScanSizeRange(stat,&d1,&d2,source, default_factor,default_factor_add,force_base, - max_value>0 ? max_value : U32_MAX ); + max_value>0 ? max_value : U32_MAX, opt ); if ( d1 < 0 || d1 > U32_MAX || d2 < 0 || d2 > U32_MAX ) + { end = (char*)source; + if (stat) + *stat = 0; + } else { if (num1) @@ -5826,6 +8341,46 @@ char * ScanSizeRangeU32 /////////////////////////////////////////////////////////////////////////////// +char * ScanSizeRangeS32 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s32 *num1, // not NULL: store 'from' result + s32 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : S32_MAX, opt ); + + if ( d1 < S32_MIN || d1 > S32_MAX || d2 < S32_MIN || d2 > U32_MAX ) + { + end = (char*)source; + if (stat) + *stat = 0; + } + else + { + if (num1) + *num1 = (s32)d1; + if (num2) + *num2 = (s32)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// + char * ScanSizeRangeU64 ( int *stat, // if not NULL: store result @@ -5837,16 +8392,21 @@ char * ScanSizeRangeU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { double d1, d2; char * end = ScanSizeRange(stat,&d1,&d2,source, default_factor,default_factor_add,force_base, - max_value>0 ? max_value : U64_MAX ); + max_value>0 ? max_value : U64_MAX, opt ); if ( d1 < 0 || d1 > U64_MAX || d2 < 0 || d2 > U64_MAX ) + { end = (char*)source; + if (stat) + *stat = 0; + } else { if (num1) @@ -5860,6 +8420,46 @@ char * ScanSizeRangeU64 } /////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeS64 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s64 *num1, // not NULL: store 'from' result + s64 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : S64_MAX, opt ); + + if ( d1 < S64_MIN || d1 > S64_MAX || d2 < S64_MIN || d2 > U64_MAX ) + { + end = (char*)source; + if (stat) + *stat = 0; + } + else + { + if (num1) + *num1 = (s64)d1; + if (num2) + *num2 = (s64)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// char * ScanSizeRangeList @@ -5873,7 +8473,8 @@ char * ScanSizeRangeList u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5888,7 +8489,8 @@ char * ScanSizeRangeList int stat; ptr = ScanSizeRange( &stat, num, num+1, ptr, - default_factor, default_factor_add, force_base, max_value ); + default_factor, default_factor_add, + force_base, max_value, opt ); if (!stat) break; num += 2; @@ -5929,7 +8531,8 @@ char * ScanSizeRangeListU32 u32 default_factor, // use this factor if number hasn't one u32 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5940,7 +8543,7 @@ char * ScanSizeRangeListU32 char *end = ScanSizeRangeList( n_range, d, max_range, source, default_factor, default_factor_add, - force_base, max_value ); + force_base, max_value, opt ); uint i; for ( i = 0; i < 2*max_range; i++ ) @@ -5964,7 +8567,8 @@ char * ScanSizeRangeListU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5975,7 +8579,7 @@ char * ScanSizeRangeListU64 char *end = ScanSizeRangeList( n_range, d, max_range, source, default_factor, default_factor_add, - force_base, max_value ); + force_base, max_value, opt ); uint i; for ( i = 0; i < 2*max_range; i++ ) @@ -6021,7 +8625,7 @@ enumError ScanSizeOpt err = ERR_SYNTAX; if (print_err) ERROR0(ERR_SYNTAX, - "Illegal number for option --%s: %s\n", + "Invalid number for option --%s: %s\n", opt_name, source ); } else if ( min > 0 && d < min ) @@ -6219,6 +8823,7 @@ char * ScanSIFactor ) { DASSERT(source); + double i = 0.0; switch (*(uchar*)source++) { case 'y': default_factor = 1e-24; break; @@ -6227,26 +8832,27 @@ char * ScanSIFactor case 'f': default_factor = 1e-15; break; case 'p': default_factor = 1e-12; break; case 'n': default_factor = 1e-9; break; - case 0xb5: // µ as ANSI + case 0xb5: // µ as ASCII case 'u': default_factor = 1e-6; break; case 'm': default_factor = 1e-3; break; case 'c': default_factor = 1e-2; break; case 'd': default_factor = 1e-1; break; case 'h': default_factor = 1e2; break; - case 'k': default_factor = 1e3; break; - case 'M': default_factor = 1e6; break; - case 'G': default_factor = 1e9; break; - case 'T': default_factor = 1e12; break; - case 'P': default_factor = 1e15; break; - case 'E': default_factor = 1e18; break; - case 'Z': default_factor = 1e21; break; - case 'Y': default_factor = 1e24; break; + case 'k': + case 'K': default_factor = 1e3; i = 1024.0; break; + case 'M': default_factor = 1e6; i = 1048576.0; break; + case 'G': default_factor = 1e9; i = 1073741824.0; break; + case 'T': default_factor = 1e12; i = 1099511627776.0; break; + case 'P': default_factor = 1e15; i = 1125899906842624.0; break; + case 'E': default_factor = 1e18; i = 1152921504606846976.0; break; + case 'Z': default_factor = 1e21; i = 1180591620717411303424.0; break; + case 'Y': default_factor = 1e24; i = 1208925819614629174706176.0; break; case 0xc2: if ( (uchar)*source == 0xb5 ) { - // µ as UTF-8 + // µ as UTF-8 source++; default_factor = 1e-6; break; @@ -6258,6 +8864,13 @@ char * ScanSIFactor default: source--; } + + if ( *source == 'i' && i ) + { + source++; + default_factor = i; + } + if (num) *num = default_factor; return (char*)source; @@ -6270,13 +8883,13 @@ double GetDurationFactor ( char ch ) switch (ch) { case 's': return 1.0; break; - case 'm': return 60.0; break; - case 'h': return 3600.0; break; + case 'm': return SEC_PER_MIN; break; + case 'h': return SEC_PER_HOUR; break; case 't': - case 'd': return 86400.0; break; - case 'w': return 7*86400.0; break; + case 'd': return SEC_PER_DAY; break; + case 'w': return SEC_PER_WEEK; break; case 'j': - case 'y': return 365*86400.0; break; + case 'y': return SEC_PER_YEAR; break; default: return 0.0; } } @@ -6285,7 +8898,10 @@ double GetDurationFactor ( char ch ) char * ScanDuration ( - double *num, // not NULL: store result + // Returns a pointer to the the first not used character. + // On error, it is 'source' and '*seconds' is set to 0.0. + + double *seconds, // not NULL: store result (number of seconds) ccp source, // source text double default_factor, // default factor if no SI unit found ScanDuration_t mode @@ -6293,8 +8909,9 @@ char * ScanDuration { if (!source) return 0; + ccp src, done; + done = src = source; - ccp src = source; if ( mode & SDUMD_SKIP_BLANKS ) while ( *src == ' ' || *src == '\t' ) src++; @@ -6318,7 +8935,7 @@ char * ScanDuration } else sum += val; - src = end; + done = src = end; valid = true; if ( mode & SDUMD_ALLOW_SPACE_SEP ) @@ -6333,9 +8950,9 @@ char * ScanDuration } mode |= SDUMD_SKIP_BLANKS; } - if (num) - *num = sum; - return (char*)( valid ? src : source ); + if (seconds) + *seconds = sum; + return (char*)( valid ? done : source ); } @@ -6379,8 +8996,11 @@ char * ScanDuration double si_factor; end = ScanSIFactor(&si_factor,src,1.0); + char factor = *end; + if ( mode & SDUMD_ALLOW_CAPITALS ) + factor = tolower((int)factor); + double unit = GetDurationFactor(factor); - double unit = GetDurationFactor(*end); if (unit) { val *= unit * si_factor; @@ -6388,7 +9008,10 @@ char * ScanDuration } else { - unit = GetDurationFactor(*src); + char factor = *src; + if ( mode & SDUMD_ALLOW_CAPITALS ) + factor = tolower((int)factor); + unit = GetDurationFactor(factor); if (unit) { val *= unit; @@ -6401,24 +9024,199 @@ char * ScanDuration } } } - if (num) - *num = val; + if (seconds) + *seconds = val; return (char*)src; } - if (num) - *num = 0.0; + if (seconds) + *seconds = 0.0; return (char*)source; } +/////////////////////////////////////////////////////////////////////////////// + +double str2duration ( ccp src, char **endptr, double default_factor ) +{ + double dur = 0.0; + char *end = ScanDuration(&dur,src,default_factor,SDUMD_M_DEFAULT); + if (endptr) + *endptr = end; + return dur; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanIP4 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ipv4_class_t ScanIP4 +( + ipv4_t *ret_ip4, // not NULL: return numeric IPv4 here + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +) +{ + //--- param check + + if (!addr) + { + error: + if (ret_ip4) + *ret_ip4 = 0; + return CIP4_ERROR; + } + + if ( addr_len < 0 ) + addr_len = strlen(addr); + + ccp addr_end = addr + addr_len; + + + //--- skip leading blanks + + while ( addr < addr_end && isblank((int)*addr) ) + addr++; + + + //--- scan input + + int count = 0; + bool trailing_pt = false; + uint num[4] = {0,0,0,0}; + while ( addr < addr_end ) + { + if ( *addr < '0' || *addr > '9' ) + break; + + addr = ScanNumber ( num+count++, addr, addr_end, *addr == '0' ? 8 : 10, 3 ); + if ( count == 4 || *addr != '.' ) + { + trailing_pt = false; + break; + } + addr++; + trailing_pt = true; + } + + //printf("N=%u .=%u [%u,%u,%u,%u] addr=%s\n",count,trailing_pt,num[0],num[1],num[2],num[3],addr); + + if ( !count || addr < addr_end ) + goto error; + + + //--- calculate IPv4 based on class-X + + ipv4_class_t res = CIP4_INVALID; + u32 ipv4 = 0; + + switch ( trailing_pt ? 4 : count ) + { + case 1: + ipv4 = num[0]; + res = CIP4_NUMERIC; + break; + + case 2: + if ( num[0] <= 0xff && num[1] <= 0xffffff ) + { + ipv4 = num[0] << 24 | num[1]; + res = CIP4_CLASS_A; + } + break; + + case 3: + if ( num[0] <= 0xff && num[1] <= 0xff && num[2] <= 0xffff ) + { + ipv4 = num[0] << 24 | num[1] << 16 | num[2]; + res = CIP4_CLASS_B; + } + break; + + case 4: + if ( num[0] <= 0xff && num[1] <= 0xff && num[2] <= 0xff && num[3] <= 0xff ) + { + ipv4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3]; + res = CIP4_CLASS_C; + } + break; + } + + + //--- return status + + if (ret_ip4) + *ret_ip4 = ipv4; + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * NormalizeIP4 +( + // Return a pointer to dest if valid IPv4, + // Otherwise return 0 and buf is not modifieid. + + char *buf, // result buffer, can be 'addr' + // if NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + bool c_notation, // TRUE: force lass-C notation (a.b.c.d) + + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +) +{ + ipv4_t ip4; + ipv4_class_t stat = ScanIP4(&ip4,addr,-1); + if ( stat <= CIP4_INVALID ) + return 0; + + return PrintIP4ByMode( buf, buf_size, ip4, c_notation ? CIP4_CLASS_C : stat, -1 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int dclib_aton ( ccp addr, struct in_addr *inp ) +{ + ipv4_t ip4; + ipv4_class_t stat = ScanIP4(&ip4,addr,-1); + if (inp) + inp->s_addr = htonl(ip4); + return stat > CIP4_INVALID; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// PrintIP4*() /////////////// /////////////////////////////////////////////////////////////////////////////// +char * PrintIP4N +( + // print as unsigned number (CIP4_NUMERIC) + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_A_PORT+1 ); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%u:%u", ip4, port ); + else + snprintf(buf,buf_size,"%u",ip4); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + char * PrintIP4A ( - // print in A-notation + // print in A-notation (CIP4_CLASS_A) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -6446,7 +9244,7 @@ char * PrintIP4A char * PrintIP4B ( - // print in A-notation + // print in B-notation (CIP4_CLASS_B) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -6599,6 +9397,28 @@ char * PrintAlignedIP4 return buf; } +/////////////////////////////////////////////////////////////////////////////// + +char * PrintIP4ByMode +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + ipv4_class_t mode, // CIP4_NUMERIC | CIP4_CLASS_A | CIP4_CLASS_B: + // force numeric or class-A/B output; otherwise class-C output + s32 port // 0..0xffff: add ':port' +) +{ + switch (mode) + { + case CIP4_NUMERIC: return PrintIP4N(buf,buf_size,ip4,port); + case CIP4_CLASS_A: return PrintIP4A(buf,buf_size,ip4,port); + case CIP4_CLASS_B: return PrintIP4B(buf,buf_size,ip4,port); + default: return PrintIP4 (buf,buf_size,ip4,port); + } +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// CRC16 /////////////// @@ -6735,6 +9555,132 @@ char * ScanNiceID4 ( int * res, ccp arg ) return (char*)arg; }; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNiceID5 ( uint id ) +{ + if (!id) + return "-----"; + + char *buf = GetCircBuf(6); + DASSERT(buf); + + buf[0] = 'A' + id / 26000 % 26; + buf[1] = 'A' + id / 1000 % 26; + buf[2] = '0' + id / 100 % 10; + buf[3] = '0' + id / 10 % 10; + buf[4] = '0' + id % 10; + buf[5] = 0; + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNiceID5 ( int * res, ccp arg ) +{ + DASSERT(res); + *res = -1; + + if (!arg) + return 0; + + char ch = tolower((int)arg[0]); + if ( ch >= 'a' && ch <= 'z' ) + { + int num = ch - 'a'; + ch = tolower((int)arg[1]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = arg[2]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[3]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[4]; + if ( ch >= '0' && ch <= '9' ) + { + *res = 10 * num + ch - '0'; + return (char*)arg+5; + } + } + } + } + } + return (char*)arg; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNiceID6 ( uint id ) +{ + if (!id) + return "------"; + + char *buf = GetCircBuf(7); + DASSERT(buf); + + buf[0] = 'A' + id / 676000 % 26; + buf[1] = 'A' + id / 26000 % 26; + buf[2] = 'A' + id / 1000 % 26; + buf[3] = '0' + id / 100 % 10; + buf[4] = '0' + id / 10 % 10; + buf[5] = '0' + id % 10; + buf[6] = 0; + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNiceID6 ( int * res, ccp arg ) +{ + DASSERT(res); + *res = -1; + + if (!arg) + return 0; + + char ch = tolower((int)arg[0]); + if ( ch >= 'a' && ch <= 'z' ) + { + int num = ch - 'a'; + ch = tolower((int)arg[1]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = tolower((int)arg[2]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = arg[3]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[4]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[5]; + if ( ch >= '0' && ch <= '9' ) + { + *res = 10 * num + ch - '0'; + return (char*)arg+6; + } + } + } + } + } + } + return (char*)arg; +}; + // /////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// diff --git a/project/dclib/dclib-numeric.h b/project/dclib/dclib-numeric.h index dc4ccef..5ab36ef 100644 --- a/project/dclib/dclib-numeric.h +++ b/project/dclib/dclib-numeric.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -819,7 +819,7 @@ static inline s64 double2int64 ( double d ) { return (s64) trunc (d+0.5); } /////////////////////////////////////////////////////////////////////////////// -// all values are multiple of 26*26*100 = 67600 = 0x10810 +// all values are multiple of 26*26*100 = 67600 = 0x00010810 #define GOOD_MAX_NICE4_5 67600 // 1 * 67600 #define GOOD_MAX_NICE4_6 946400 // 14 * 67600 #define GOOD_MAX_NICE4_7 9937200 // 147 * 67600 @@ -830,6 +830,28 @@ static inline s64 double2int64 ( double d ) { return (s64) trunc (d+0.5); } ccp PrintNiceID4 ( uint nice_gid ); char * ScanNiceID4 ( int * res, ccp arg ); +/////////////////////////////////////////////////////////////////////////////// + +// all values are multiple of 26*26*1000 = 676000 = 0x000a50a0 +#define GOOD_MAX_NICE5_6 676000 // 1 * 676000 +#define GOOD_MAX_NICE5_7 9464000 // 14 * 676000 +#define GOOD_MAX_NICE5_8 99372000 // 147 * 676000 +#define GOOD_MAX_NICE5_9 999804000 // 1479 * 676000 +#define GOOD_MAX_NICE5 4294628000 // 6353 * 676000 = 0xfffad2a0 = -339296 + +ccp PrintNiceID5 ( uint nice_gid ); +char * ScanNiceID5 ( int * res, ccp arg ); + +/////////////////////////////////////////////////////////////////////////////// + +// all values are multiple of 26*26*26*1000 = 17576000 = 0x010c3040 +#define GOOD_MAX_NICE6_8 87880000 // 5 * 17576000 +#define GOOD_MAX_NICE6_9 984256000 // 56 * 17576000 +#define GOOD_MAX_NICE6 4288544000 // 244 * 17576000 = 0xff9dfd00 = -6423296 + +ccp PrintNiceID6 ( uint nice_gid ); +char * ScanNiceID6 ( int * res, ccp arg ); + // /////////////////////////////////////////////////////////////////////////////// /////////////// E N D /////////////// diff --git a/project/dclib/dclib-option.c b/project/dclib/dclib-option.c index 6af1589..fdcce94 100644 --- a/project/dclib/dclib-option.c +++ b/project/dclib/dclib-option.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -113,10 +113,10 @@ void AddGenericOpt { go->sopt_id[ch] = gp->id; *sopt_ptr++ = ch; - if ( gp->arg ) + if ( gp->arg == 1 || gp->arg == 2 ) { *sopt_ptr++ = ':'; - if ( gp->arg > 1 ) + if ( gp->arg == 2 ) *sopt_ptr++ = ':'; } } @@ -142,7 +142,7 @@ void AddGenericOpt struct option *opt = go->lopt + go->lopt_used++; opt->name = StrDupMemPool(&go->str_pool,name_buf); - opt->has_arg = gp->arg; + opt->has_arg = gp->arg < 2 ? gp->arg : 2; opt->flag = 0; opt->val = gp->id; @@ -446,7 +446,7 @@ GetGParam_t GetGParamDBL ( const GParam_t *par, double *return_val ) /////////////////////////////////////////////////////////////////////////////// -GetGParam_t GetGParamSTR ( const GParam_t *par, ccp *return_val ) +GetGParam_t GetGParamCCP ( const GParam_t *par, ccp *return_val ) { DASSERT( !par || par->vtype == GetGParamValueType(par->ptype) ); if ( par && par->vtype == GPT_STR ) @@ -573,13 +573,15 @@ ccp PrintGParam ( const GParam_t *par ) const GenericOptParm_t StandardGOptions[] = { - { GOPT_O_TEST_OPT, 0, "_opt" }, + { GOPT_O_TEST_OPT, 0, "@opt" }, { GOPT_O_HELP, 0, "h|help" }, { GOPT_O_CLEAR, 0, "c|clear" }, { GOPT_O_PAGER, 0, "p|pager" }, + { GOPT_O_WIDTH, 1, "w|width" }, { GOPT_O_QUIT, 0, "Q|quit" }, { GOPT_O_QUIET, 0, "q|quiet" }, - { GOPT_O_VERBOSE, 0, "v|verbose" }, + { GOPT_O_VERBOSE, 3, "v|verbose" }, + { GOPT_O_DEBUG, 3, "D|d|debug" }, {0,0,0} }; @@ -660,7 +662,7 @@ enumError ScanGOptionsHelper //--- check options - InitializeGOptions(gopt); + InitializeGOptions(gopt,gopt->max_width); int err = 0, test_opt = 0; enumError stat = ERR_OK; @@ -876,7 +878,7 @@ enumError ScanGOptionsHelper case GOPT_IDX_DURATION: par->ptype = GOPT_IDX_DURATION; - ScanDuration(&par->d,optarg,1,SDUMD_M_ALL); break; + ScanDuration(&par->d,optarg,1,SDUMD_M_DEFAULT); break; break; case GOPT_IDX_STRING: @@ -923,8 +925,28 @@ enumError ScanGOptionsHelper case GOPT_O_QUIET: gopt->verbose = gopt->verbose > -1 ? -1 : gopt->verbose - 1; break; + case GOPT_O_VERBOSE: - gopt->verbose = gopt->verbose < 0 ? 0 : gopt->verbose + 1; + if (optarg) + gopt->verbose = str2l(optarg,0,10); + else + gopt->verbose = gopt->verbose < 0 ? 0 : gopt->verbose + 1; + break; + + case GOPT_O_DEBUG: + if (optarg) + gopt->debug = str2l(optarg,0,10); + else + gopt->debug++; + break; + + case GOPT_O_WIDTH: + { + char *end; + int num = str2l(optarg,&end,10); + if ( num > 0 && !*end ) + gopt->width = num < 40 ? 40 : num <= gopt->max_width ? num : gopt->max_width; + } break; case GOPT_O_HELP: gopt->help++; scan_opt |= SGO_PAGER; break; @@ -954,10 +976,15 @@ enumError ScanGOptionsHelper if (test_opt) { - printf("\nOPTIONS [N=%u/%u]: %s\n",go->lopt_used,go->lopt_size,go->sopt); - const struct option *opt; - for ( opt = go->lopt; opt->name; opt++ ) - printf(" %04x %u %s\n", opt->val, opt->has_arg, opt->name ); + printf("\n## %sShort options:%s %s\n## %sLong options (%u/%u):%s\n", + colout->caption, colout->reset, go->sopt, + colout->caption, go->lopt_used, go->lopt_size, colout->reset ); + + for ( const struct option *opt = go->lopt; opt->name; opt++ ) + printf("## %6x %c %s\n", opt->val, + opt->has_arg ? '0'+opt->has_arg : '-', + opt->name ); + putchar('\n'); } char **dest = argv + optind; @@ -1001,7 +1028,7 @@ enumError ScanGOptionsHelper abort: ResetGenericOpt(go); if (stat) - InitializeGOptions(gopt); + InitializeGOptions(gopt,gopt->max_width); return stat; } diff --git a/project/dclib/dclib-option.h b/project/dclib/dclib-option.h index f16dd7b..98fdb9c 100644 --- a/project/dclib/dclib-option.h +++ b/project/dclib/dclib-option.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -46,8 +46,13 @@ struct TCPStream_t; typedef struct GenericOptParm_t { - u32 id; // id of the option, - u8 arg; // 0:no arg, 1:arg, 2:optional arg + u32 id; // id of the option + + u8 arg; // 0: no arguments + // 1: always argument, + // 2: optional argument + // 3: short-opt: no arg / long-opt: optional arg + ccp name; // pipe separated option names } GenericOptParm_t; @@ -85,7 +90,7 @@ void AddGenericOpt ( GenericOpt_t *go, // data structure const GenericOptParm_t *gp, // list to add, list end on name==NULL - bool rm_minus // remove '-' on options for alternatives + bool rm_minus // remove '-' of options for alternatives ); // @@ -100,7 +105,7 @@ typedef enum GOPT_t GOPT_RESERVED_END = 0x02000000, // end of reserved range GOPT_M_INDEX = 0x000000ff, // mask to get the index - GOPT_N_PARAM = 25, // max used index+1 = num of params + GOPT_N_PARAM = 60, // max used index+1 = num of params GOPT_M_VALUE = 0x0003ff00, // mask for GOPT_T_PLUS|GOPT_T_MINUS GOPT_S_VALUE = 8, // value is shifted by this num GOPT_M_TYPE = 0x00fc0000, // mask to isolate option type @@ -108,7 +113,7 @@ typedef enum GOPT_t GOPT_S_TYPE = 18, // type is shifted by this num - //--- parameters by index + //--- parameters by index (sn=signed num, un=unsigned num) GOPT_IDX_NULL = 0, // type undefined @@ -174,15 +179,17 @@ typedef enum GOPT_t //--- named options GOPT_O__BEGIN = 0x100, - GOPT_O_TEST_OPT, // test: print all defined options + GOPT_O_TEST_OPT, // test: print all defined options: --@opt GOPT_O_HELP, // force help GOPT_O_CLEAR, // clear screen -> ScanGOptionsHelperTN() GOPT_O_PAGER, // activate pager -> ScanGOptionsHelperTN() + GOPT_O_WIDTH, // limit terminal width -> ScanGOptionsHelperTN() GOPT_O_QUIT, // quit if command done -> ScanGOptionsHelperTN() GOPT_O_QUIET, // be quiet - GOPT_O_VERBOSE, // be verbose + GOPT_O_VERBOSE, // be verbose, or set by --verbose=# + GOPT_O_DEBUG, // increment debug level, or set by --debug=# GOPT_O__CONTINUE // continue with this value on more options } @@ -239,7 +246,7 @@ GParam_t; typedef enum GetGParam_t { - GGP_NONE, // no param set or param unusable, 'return_val' untouched + GGP_NONE = 0, // no param set or param unusable, 'return_val' is untouched GGP_CONVERT, // param set, but converted (different numeric type) GGP_SIGN, // param returned, but sign maybe converted GGP_OK // original or extended param returned @@ -253,7 +260,7 @@ GetGParam_t GetGParamUINT ( const GParam_t *par, uint *return_val ); GetGParam_t GetGParamS64 ( const GParam_t *par, s64 *return_val ); GetGParam_t GetGParamU64 ( const GParam_t *par, u64 *return_val ); GetGParam_t GetGParamDBL ( const GParam_t *par, double *return_val ); -GetGParam_t GetGParamSTR ( const GParam_t *par, ccp *return_val ); +GetGParam_t GetGParamCCP ( const GParam_t *par, ccp *return_val ); GetGParam_t GetGParamBIT ( const GParam_t *par, u32 *return_val, u32 *return_mask ); ccp PrintGParam ( const GParam_t *par ); @@ -276,10 +283,13 @@ typedef struct GOptions_t int help; // number of GOPT_O_HELP int clear; // number of GOPT_O_CLEAR - int pager; // number of GOPT_O_PAGER, - int quit; // number of GOPT_O_QUIT, - int verbose; // combi of GOPT_O_QUIET & GOPT_O_VERBOSE, + int pager; // number of GOPT_O_PAGER + int quit; // number of GOPT_O_QUIT + int debug; // number of GOPT_O_DEBUG + int verbose; // combi of GOPT_O_QUIET & GOPT_O_VERBOSE + int max_width; // max terminal width + int width; // terminal width //--- generic parameter support @@ -289,10 +299,14 @@ GOptions_t; //----------------------------------------------------------------------------- -static inline void InitializeGOptions ( GOptions_t *go ) +#define GOOD_INFO_MAX_WIDTH 120 + +static inline void InitializeGOptions ( GOptions_t *go, int max_width ) { DASSERT(go); memset(go,0,sizeof(*go)); + go->max_width = max_width; + go->width = max_width < GOOD_INFO_MAX_WIDTH ? max_width : GOOD_INFO_MAX_WIDTH; } static inline const GParam_t * GetGParam ( const GOptions_t *go, uint par_index ) diff --git a/project/dclib/dclib-regex.c b/project/dclib/dclib-regex.c index 5d3d0aa..f879566 100644 --- a/project/dclib/dclib-regex.c +++ b/project/dclib/dclib-regex.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -164,7 +164,7 @@ enumError ScanRegex ( Regex_t *re, bool init_re, ccp regex ) enumError err = ERR_OK; for(;;) { - while ( isspace(*regex) || *regex == ';' ) + while ( isspace((int)*regex) || *regex == ';' ) regex++; if (!*regex) break; diff --git a/project/dclib/dclib-regex.h b/project/dclib/dclib-regex.h index 0a31998..d314b63 100644 --- a/project/dclib/dclib-regex.h +++ b/project/dclib/dclib-regex.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-shift-jis.c b/project/dclib/dclib-shift-jis.c index fd7d692..affb167 100644 --- a/project/dclib/dclib-shift-jis.c +++ b/project/dclib/dclib-shift-jis.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-system.h b/project/dclib/dclib-system.h index 6829cb3..7da6e82 100644 --- a/project/dclib/dclib-system.h +++ b/project/dclib/dclib-system.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-tables.c b/project/dclib/dclib-tables.c index 508d1c0..629e805 100644 --- a/project/dclib/dclib-tables.c +++ b/project/dclib/dclib-tables.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -34,16 +34,34 @@ #define _GNU_SOURCE 1 -//#include <string.h> -//#include <unistd.h> -//#include <time.h> -//#include <stdio.h> -//#include <errno.h> -//#include <sys/time.h> +#include <time.h> +#include <utime.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/time.h> #include "dclib-basics.h" -//#include "dclib-debug.h" -//#include "dclib-utf8.h" +#include "dclib-utf8.h" +#include "dclib-file.h" +#include "dclib-option.h" +#include "dclib-network.h" +#include "dclib-regex.h" +#include "dclib-xdump.h" +#include "dclib-ui.h" + +#ifdef DCLIB_MYSQL + #include "dclib-mysql.h" +#endif + +#if DCLIB_TERMINAL + #include "dclib-terminal.h" +#endif + +#if DCLIB_THREAD + #include "dclib-thread.h" +#endif // /////////////////////////////////////////////////////////////////////////////// @@ -264,7 +282,7 @@ const u16 TableCP1252_80[0x20] = { 0x20ac, 0,0x201a,0x0192, 0x201e,0x2026,0x2020,0x2021, // 80..87 0x02c6,0x2030,0x0160,0x2039, 0x0152, 0,0x017d, 0, // 88..8f - 0,0x2018,0x2019,0x201c, 0x201d,0x2022,0x2013,0x2014, // 90..97 + 0,0x2018,0x2019,0x201c, 0x201d,0x2022,0x2013,0x2014, // 90..97 0x02dc,0x2122,0x0161,0x203a, 0x0153, 0,0x017e,0x0178, // 98..9f }; @@ -412,6 +430,472 @@ const u8 TableAlphabet64[48] = // /////////////////////////////////////////////////////////////////////////////// +/////////////// sizeof_info_t /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const sizeof_info_t sizeof_info_linux[] = +{ + SIZEOF_INFO_TITLE("Basic C types") + SIZEOF_INFO_ENTRY(char) + SIZEOF_INFO_ENTRY(wchar_t) + SIZEOF_INFO_ENTRY(short) + SIZEOF_INFO_ENTRY(int) + SIZEOF_INFO_ENTRY(long) + SIZEOF_INFO_ENTRY(long long) + SIZEOF_INFO_ENTRY(float) + SIZEOF_INFO_ENTRY(double) + SIZEOF_INFO_ENTRY(long double) + SIZEOF_INFO_ENTRY(void*) + + SIZEOF_INFO_TITLE("Linux C types") + SIZEOF_INFO_ENTRY(size_t) + SIZEOF_INFO_ENTRY(time_t) + SIZEOF_INFO_ENTRY(struct timeval) + SIZEOF_INFO_ENTRY(struct timespec) + SIZEOF_INFO_ENTRY(struct tm) + SIZEOF_INFO_ENTRY(struct timezone) + SIZEOF_INFO_ENTRY(struct utimbuf) + SIZEOF_INFO_ENTRY(struct stat) + SIZEOF_INFO_ENTRY(fd_set) + + SIZEOF_INFO_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +const sizeof_info_t sizeof_info_dclib[] = +{ + SIZEOF_INFO_TITLE("dcLib basic types") + SIZEOF_INFO_ENTRY(bool) + SIZEOF_INFO_ENTRY(u8) + SIZEOF_INFO_ENTRY(u16) + SIZEOF_INFO_ENTRY(u32) + SIZEOF_INFO_ENTRY(u64) + SIZEOF_INFO_ENTRY(mem_t) + SIZEOF_INFO_ENTRY(exmem_t) + + SIZEOF_INFO_TITLE("dcLib time & timer") + SIZEOF_INFO_ENTRY(u_sec_t) + SIZEOF_INFO_ENTRY(u_msec_t) + SIZEOF_INFO_ENTRY(u_usec_t) + SIZEOF_INFO_ENTRY(u_nsec_t) + SIZEOF_INFO_ENTRY(DayTime_t) + SIZEOF_INFO_ENTRY(CurrentTime_t) + + SIZEOF_INFO_TITLE("dcLib numeric & strings") + SIZEOF_INFO_ENTRY(float3) + SIZEOF_INFO_ENTRY(float3List_t) + SIZEOF_INFO_ENTRY(double3) + SIZEOF_INFO_ENTRY(double3List_t) + SIZEOF_INFO_ENTRY(float34) + SIZEOF_INFO_ENTRY(double34) + SIZEOF_INFO_ENTRY(MatrixD_t) + SIZEOF_INFO_ENTRY(RegexReplace_t) + SIZEOF_INFO_ENTRY(RegexElem_t) + SIZEOF_INFO_ENTRY(Regex_t) + SIZEOF_INFO_ENTRY(dcUnicodeTripel) + SIZEOF_INFO_ENTRY(Escape_t) + + SIZEOF_INFO_TITLE("dcLib buffers") + SIZEOF_INFO_ENTRY(sha1_hash_t) + SIZEOF_INFO_ENTRY(sha1_hex_t) + SIZEOF_INFO_ENTRY(sha1_id_t) + SIZEOF_INFO_ENTRY(uuid_text_t) + SIZEOF_INFO_ENTRY(CircBuf_t) + SIZEOF_INFO_ENTRY(FastBuf_t) + SIZEOF_INFO_ENTRY(ContainerData_t) + SIZEOF_INFO_ENTRY(Container_t) + SIZEOF_INFO_ENTRY(DataBuf_t) + SIZEOF_INFO_ENTRY(GrowBuffer_t) + + SIZEOF_INFO_TITLE("dcLib lists") + SIZEOF_INFO_ENTRY(mem_list_t) + SIZEOF_INFO_ENTRY(exmem_key_t) + SIZEOF_INFO_ENTRY(exmem_list_t) + SIZEOF_INFO_ENTRY(sizeof_info_t) + SIZEOF_INFO_ENTRY(PointerList_t) + SIZEOF_INFO_ENTRY(KeywordTab_t) + SIZEOF_INFO_ENTRY(StringField_t) + SIZEOF_INFO_ENTRY(ParamFieldItem_t) + SIZEOF_INFO_ENTRY(ParamField_t) + SIZEOF_INFO_ENTRY(SplitArg_t) + SIZEOF_INFO_ENTRY(ArgManager_t) + SIZEOF_INFO_ENTRY(CommandList_t) + SIZEOF_INFO_ENTRY(MemMapItem_t) + SIZEOF_INFO_ENTRY(MemMap_t) + SIZEOF_INFO_ENTRY(DynData_t) + SIZEOF_INFO_ENTRY(DynDataList_t) + SIZEOF_INFO_ENTRY(MemPoolChunk_t) + SIZEOF_INFO_ENTRY(MemPool_t) + SIZEOF_INFO_ENTRY(GenericOptParm_t) + SIZEOF_INFO_ENTRY(GenericOpt_t) + SIZEOF_INFO_ENTRY(GParam_t) + SIZEOF_INFO_ENTRY(GOptions_t) + SIZEOF_INFO_ENTRY(InfoOption_t) + SIZEOF_INFO_ENTRY(InfoCommand_t) + SIZEOF_INFO_ENTRY(InfoUI_t) + + SIZEOF_INFO_TITLE("dcLib file") + SIZEOF_INFO_ENTRY(LogFile_t) + SIZEOF_INFO_ENTRY(FileAttrib_t) + SIZEOF_INFO_ENTRY(File_t) + SIZEOF_INFO_ENTRY(MemFile_t) + SIZEOF_INFO_ENTRY(TraceLog_t) + #ifndef __APPLE__ + SIZEOF_INFO_ENTRY(LineBuffer_t) + #endif + SIZEOF_INFO_ENTRY(search_file_t) + SIZEOF_INFO_ENTRY(search_file_list_t) + SIZEOF_INFO_ENTRY(CatchOutput_t) + SIZEOF_INFO_ENTRY(SectionInfo_t) + SIZEOF_INFO_ENTRY(stat_file_count_t) + SIZEOF_INFO_ENTRY(PrintScript_t) + SIZEOF_INFO_ENTRY(RestoreState_t) + SIZEOF_INFO_ENTRY(RestoreStateTab_t) + SIZEOF_INFO_ENTRY(SaveRestoreType_t) + SIZEOF_INFO_ENTRY(SaveRestoreTab_t) + #ifdef DCLIB_MYSQL + SIZEOF_INFO_ENTRY(MySqlStatus_t) + SIZEOF_INFO_ENTRY(MySqlResult_t) + SIZEOF_INFO_ENTRY(MySql_t) + SIZEOF_INFO_ENTRY(MySqlServerStats_t) + #endif + + SIZEOF_INFO_TITLE("dcLib network") + SIZEOF_INFO_ENTRY(ipv4_t) + SIZEOF_INFO_ENTRY(ipv4x_t) + SIZEOF_INFO_ENTRY(ipv6_t) + SIZEOF_INFO_ENTRY(sockaddr_t) + SIZEOF_INFO_ENTRY(sockaddr_in4_t) + SIZEOF_INFO_ENTRY(sockaddr_in6_t) + SIZEOF_INFO_ENTRY(sockaddr_in46_t) + SIZEOF_INFO_ENTRY(sockaddr_un_t) + SIZEOF_INFO_ENTRY(sockaddr_dclib_t) + SIZEOF_INFO_ENTRY(sockaddr_info_t) + SIZEOF_INFO_ENTRY(socket_info_t) + SIZEOF_INFO_ENTRY(FDList_t) + SIZEOF_INFO_ENTRY(SplitIP_t) + SIZEOF_INFO_ENTRY(NamesIP_t) + SIZEOF_INFO_ENTRY(BinIP_t) + SIZEOF_INFO_ENTRY(BinIPItem_t) + SIZEOF_INFO_ENTRY(BinIPList_t) + SIZEOF_INFO_ENTRY(BinIPIterate_t) + SIZEOF_INFO_ENTRY(ManageIP_t) + SIZEOF_INFO_ENTRY(ResolveIP_t) + SIZEOF_INFO_ENTRY(NetworkHost_t) + SIZEOF_INFO_ENTRY(AllowIP4Item_t) + SIZEOF_INFO_ENTRY(AllowIP4_t) + SIZEOF_INFO_ENTRY(ether_head_t) + SIZEOF_INFO_ENTRY(ether_head_vlan_t) + SIZEOF_INFO_ENTRY(arp_head_t) + SIZEOF_INFO_ENTRY(ip4_head_t) + SIZEOF_INFO_ENTRY(udp_head_t) + SIZEOF_INFO_ENTRY(udp_packet_t) + SIZEOF_INFO_ENTRY(tcp_head_t) + SIZEOF_INFO_ENTRY(TransferStats_t) + SIZEOF_INFO_ENTRY(Socket_t) + SIZEOF_INFO_ENTRY(TCPStream_t) + SIZEOF_INFO_ENTRY(TCPHandler_t) + SIZEOF_INFO_ENTRY(CommandTCPInfo_t) + #if defined(SYSTEM_LINUX) || defined(__CYGWIN__) + SIZEOF_INFO_ENTRY(RouteIP4_t) + #endif + + #if DCLIB_TERMINAL + SIZEOF_INFO_TITLE("dcLib terminal & colors") + #else + SIZEOF_INFO_TITLE("dcLib colors") + #endif + SIZEOF_INFO_ENTRY(term_size_t) + SIZEOF_INFO_ENTRY(good_term_width_t) + SIZEOF_INFO_ENTRY(TermColorId_t) + SIZEOF_INFO_ENTRY(ColorSet_t) + SIZEOF_INFO_ENTRY(ColorView_t) + SIZEOF_INFO_ENTRY(SavedStdFiles_t) + #if DCLIB_TERMINAL + SIZEOF_INFO_ENTRY(History_t) + SIZEOF_INFO_ENTRY(TelnetRepeat_t) + SIZEOF_INFO_ENTRY(TelnetParam_t) + SIZEOF_INFO_ENTRY(TelnetTCPInfo_t) + SIZEOF_INFO_ENTRY(TelnetClient_t) + SIZEOF_INFO_ENTRY(TerminalKey_t) + SIZEOF_INFO_ENTRY(TerminalInfo_t) + SIZEOF_INFO_ENTRY(TerminalNameList_t) + SIZEOF_INFO_ENTRY(Keyboard_t) + #endif + + #if DCLIB_THREAD + SIZEOF_INFO_TITLE("dcLib threads") + SIZEOF_INFO_ENTRY(WatchdogThread_t) + #endif + + SIZEOF_INFO_TITLE("dcLib statistics") + SIZEOF_INFO_ENTRY(UsageParam_t) + SIZEOF_INFO_ENTRY(UsageCountEntry_t) + SIZEOF_INFO_ENTRY(UsageCount_t) + SIZEOF_INFO_ENTRY(UsageDurationEntry_t) + SIZEOF_INFO_ENTRY(UsageDuration_t) + SIZEOF_INFO_ENTRY(CpuUsageEntry_t) + SIZEOF_INFO_ENTRY(CpuUsage_t) + SIZEOF_INFO_ENTRY(UsageCtrl_t) + SIZEOF_INFO_ENTRY(usage_count_mgr) + SIZEOF_INFO_ENTRY(CpuStatus_t) + SIZEOF_INFO_ENTRY(MemoryStatus_t) + + SIZEOF_INFO_TITLE("dcLib misc") + SIZEOF_INFO_ENTRY(ProgInfo_t) + SIZEOF_INFO_ENTRY(IntervalInfo_t) + SIZEOF_INFO_ENTRY(MultiColumn_t) + SIZEOF_INFO_ENTRY(ScanAddr_t) + SIZEOF_INFO_ENTRY(ResizeElement_t) + SIZEOF_INFO_ENTRY(ResizeHelper_t) + SIZEOF_INFO_ENTRY(XDump_t) + + SIZEOF_INFO_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +const sizeof_info_t *sizeof_info_default[] = +{ + sizeof_info_linux, + sizeof_info_dclib, + 0 +}; + +PointerList_t SizeofInfoMgr = {0}; + +/////////////////////////////////////////////////////////////////////////////// + +static int compare_sizeof_size + ( const sizeof_info_t *a, const sizeof_info_t *b ) +{ + DASSERT( a && b ); + const int stat = a->size - b->size; + return stat ? stat : strcasecmp(a->name,b->name); +} + +//----------------------------------------------------------------------------- + +static int compare_sizeof_name + ( const sizeof_info_t *a, const sizeof_info_t *b ) +{ + DASSERT( a && b ); + return strcasecmp(a->name,b->name); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ListSizeofInfo +( + const PrintMode_t *p_pm, // NULL or print mode + const sizeof_info_t **si_list, // list of list of entries + ArgManager_t *p_filter, // NULL or filter arguments, LOUP_LOWER recommended + sizeof_info_order_t order // kind of order +) +{ + if ( !si_list || !*si_list ) + return; + + + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + if (!si_list) + { + si_list = GetSizeofInfoMgrList(); + if (!si_list) + si_list = sizeof_info_default; + } + + int n_tabs = 0, n_records = 0, n_elem = 0, n_cat = 0, str_size = 0;; + for ( const sizeof_info_t **si_ptr = si_list; *si_ptr; si_ptr++ ) + { + n_tabs++; + for ( const sizeof_info_t *si = *si_ptr; si->size != -9; si++ ) + { + n_records++; + if ( si->size >= 0 ) + n_elem++; + else if ( si->size == -1 ) + n_cat++; + if (si->name) + str_size += strlen(si->name) + 1; + } + } + + if ( pm.debug > 0 ) + fprintf(pm.flog,"\n%s> %u source list%s with %u record%s, %u categorie%s" + " and %u element%s, %zu bytes total%s%s", + pm.clog->info, + n_tabs, n_tabs == 1 ? "" : "s", + n_records, n_records == 1 ? "" : "s", + n_cat, n_cat == 1 ? "" : "s", + n_elem, n_elem == 1 ? "" : "s", + (n_records+n_tabs) * sizeof(sizeof_info_t) + str_size, + pm.clog->reset, pm.eol ); + + ArgManager_t my_filter = {0}, *filter = &my_filter; + + if (p_filter) + { + if ( p_filter->force_case == LOUP_LOWER ) + filter = p_filter; + else + { + for ( int i = 0; i < p_filter->argc; i++ ) + AppendArgManager(&my_filter,p_filter->argv[i],0,false); + + for ( int i = 0; i < my_filter.argc; i++ ) + { + char *arg = my_filter.argv[i]; + StringLowerS(arg,strlen(arg)+1,arg); + } + } + } + + sizeof_info_t *list = 0, *list_end = 0; + if (order) + list_end = list = MALLOC( n_elem * sizeof(*list) ); + + + //--- get field widths + + int max_size = 0; + for ( const sizeof_info_t **si_ptr = si_list; *si_ptr; si_ptr++ ) + { + for ( const sizeof_info_t *si = *si_ptr; si->size != -9; si++ ) + { + if ( max_size < si->size && CheckFilterArgManager(filter,si->name) ) + max_size = si->size; + } + } + + char buf[100]; + const int fw_hex = snprintf(buf,sizeof(buf),"%#x",max_size); + const int fw_dec = snprintf(buf,sizeof(buf),"%u",max_size); + + + //--- print or collect + + ccp head = 0; + int n_head = 0, n_sizeof = 0; + + for ( const sizeof_info_t **si_ptr = si_list; *si_ptr; si_ptr++ ) + { + for ( const sizeof_info_t *si = *si_ptr; si->size != -9; si++ ) + { + if ( si->size >= 0 ) + { + if (CheckFilterArgManager(filter,si->name)) + { + n_sizeof++; + if (order) + *list_end++ = *si; + else + { + if (head) + { + n_head++; + fprintf(pm.fout,"\n%s%s%s\n",pm.cout->caption,head,pm.cout->reset); + head = 0; + } + fprintf(pm.fout," %#*x = %*u : %s\n", + fw_hex, si->size, fw_dec, si->size, si->name ); + } + } + } + else if ( si->size == -1 ) + head = si->name; + else if ( si->size == -2 ) + putchar('\n'); + } + } + + + //--- print ordered list + + if (order) + { + putchar('\n'); + if ( n_sizeof > 1 ) + { + if ( order == SIZEOF_ORDER_NAME ) + qsort(list,n_sizeof,sizeof(*list),(qsort_func)compare_sizeof_name); + else + qsort(list,n_sizeof,sizeof(*list),(qsort_func)compare_sizeof_size); + } + + ccp prev = ""; + for ( const sizeof_info_t *ptr = list; ptr < list_end; ptr++ ) + { + if (!strcmp(prev,ptr->name)) + fprintf(pm.fout,"%s %#*x = %*u : %s%s\n", + pm.cout->warn, fw_hex, ptr->size, + fw_dec, ptr->size, ptr->name, pm.cout->reset ); + else + { + fprintf(pm.fout," %#*x = %*u : %s\n", + fw_hex, ptr->size, fw_dec, ptr->size, ptr->name ); + prev = ptr->name; + } + } + } + + + //--- terminate + + ResetArgManager(&my_filter); + + if ( pm.debug > 0 ) + { + putchar('\n'); + if (!n_sizeof) + fprintf(pm.flog,"%s> No elements printed.%s\n",pm.cout->info,pm.cout->reset); + else + { + fputs(pm.cout->info,stdout); + fputs("> ",stdout); + if (n_head) + fprintf(pm.flog,"%u head line%s and ", n_head, n_head == 1 ? "" : "s" ); + + if ( n_sizeof < n_elem ) + fprintf(pm.flog,"%u of %u elements printed.%s\n", + n_sizeof, n_elem, pm.cout->reset ); + else + fprintf(pm.flog,"%u element%s printed.%s\n", + n_sizeof, n_sizeof == 1 ? "" : "s", + pm.cout->reset ); + } + } + + putchar('\n'); + FREE(list); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// misc /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const IntervalInfo_t interval_info[] = +{ + { NSEC_PER_SEC, USEC_PER_SEC, "Second" }, + { NSEC_PER_MIN, USEC_PER_MIN, "Minute" }, + { NSEC_PER_HOUR, USEC_PER_HOUR, "Hour" }, + { NSEC_PER_DAY, USEC_PER_DAY, "Day" }, + { NSEC_PER_WEEK, USEC_PER_WEEK, "Week" }, + { NSEC_PER_MONTH, USEC_PER_MONTH, "Month" }, + { NSEC_PER_YEAR, USEC_PER_YEAR, "Year" }, + { M1(u_nsec_t), M1(u_usec_t), "*" }, +}; + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-types.h b/project/dclib/dclib-types.h index 41ffbf9..7818bc0 100644 --- a/project/dclib/dclib-types.h +++ b/project/dclib/dclib-types.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -41,6 +41,7 @@ #include <limits.h> #include <stdlib.h> #include <stdio.h> +#include <stdint.h> #include <ctype.h> #ifndef WIN_DCLIB @@ -122,7 +123,7 @@ __attribute__ ((packed)) dcEndian_t; #define PiB (1024ull*1024*1024*1024*1024) #define EiB (1024ull*1024*1024*1024*1024*1024) -// specal '-1' support +// special '-1' support #define M1(a) ( (typeof(a))~0 ) #define IS_M1(a) ( (a) == (typeof(a))~0 ) @@ -340,38 +341,44 @@ typedef float float32; // float as 32 bit binary data, endian is file dependent /////////////////////////////////////////////////////////////////////////////// -#define U8_MIN (0) -#define U8_MAX (0xff) -#define S8_MIN (-0x80) -#define S8_MAX (0x7f) +#define U8_MIN (0u) +#define U8_MAX UINT8_MAX +#define S8_MIN INT8_MIN +#define S8_MAX INT8_MAX -#define U16_MIN (0) -#define U16_MAX (0xffff) -#define S16_MIN (-0x8000) -#define S16_MAX (0x7fff) +#define U16_MIN (0u) +#define U16_MAX UINT16_MAX +#define S16_MIN INT16_MIN +#define S16_MAX INT16_MAX -#define U32_MIN (0u) -#define U32_MAX (0xffffffffu) -#define S32_MIN (-0x80000000) -#define S32_MAX (0x7fffffff) +#define U32_MIN (0u) +#define U32_MAX UINT32_MAX +#define S32_MIN INT32_MIN +#define S32_MAX INT32_MAX -#define U64_MIN (0ull) -#define U64_MAX (0xffffffffffffffffull) -#define S64_MIN (-0x8000000000000000ll) -#define S64_MAX (0x7fffffffffffffffll) +#define U64_MIN (0ull) +#define U64_MAX UINT64_MAX +#define S64_MIN INT64_MIN +#define S64_MAX INT64_MAX /////////////////////////////////////////////////////////////////////////////// #ifndef DCLIB_BASIC_TYPES #define DCLIB_BASIC_TYPES 1 - typedef unsigned char uchar; - typedef unsigned int uint; - typedef unsigned long ulong; - typedef const void * cvp; - typedef const char * ccp; - typedef const uchar * cucp; + typedef unsigned char uchar; + typedef unsigned int uint; + typedef unsigned long ulong; + typedef const void * cvp; + typedef const void ** cvpp; + typedef const char * ccp; + typedef const char ** ccpp; + typedef const uchar * cucp; + typedef const uchar ** cucpp; #endif +struct mem_t; +typedef struct mem_t mem_t; + // [[sha1_hash_t]] [[sha1_hex_t]] [[sha1_id_t]] typedef u8 sha1_hash_t[20]; typedef char sha1_hex_t[41]; // 40 chars + NULL @@ -386,6 +393,84 @@ typedef int (*qsort_r_func)( const void *, const void *, void * ); // /////////////////////////////////////////////////////////////////////////////// +/////////////// sockaddr + IPv* support /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sockaddr_t]] [[sockaddr_in_t]] +// [[sockaddr_in4_t]] [[sockaddr_in6_t]] [[sockaddr_un_t]] + +typedef struct sockaddr sockaddr_t; +typedef struct sockaddr_in sockaddr_in_t; +typedef struct sockaddr_in sockaddr_in4_t; +typedef struct sockaddr_in6 sockaddr_in6_t; +typedef struct sockaddr_un sockaddr_un_t; + +typedef struct sockaddr_in46_t sockaddr_in46_t; +typedef struct sockaddr_dclib_t sockaddr_dclib_t; + +/////////////////////////////////////////////////////////////////////////////// +// [[ipv4_t]] [[ipv4x_t]] [[ipv6_t]] + +typedef u32 ipv4_t; + +typedef struct ipv4x_t +{ + union + { + u8 v8[4]; + u16 v16[2]; + ipv4_t ip4; + }; +} +__attribute__ ((packed)) ipv4x_t; + +typedef struct ipv6_t +{ + union + { + u8 v8[16]; + u16 v16[8]; + u32 v32[4]; + u64 v64[2]; + }; +} +__attribute__ ((packed)) ipv6_t; + +//----------------------------------------------------------------------------- +// [[IpMode_t]] + +typedef enum IpMode_t +{ + // flags + IPVF_IPV6 = 1, // IPv6 only/first, otherwise IPv4 + IPVF_BOTH = 2, // try both variants (IPv4+IPv6) + + IPV4_ONLY = 0, // resolve only a IPv4 address + IPV6_ONLY = IPVF_IPV6, // resolve only a IPv6 address + IPV4_FIRST = IPVF_BOTH, // resolve a IPv4 first, and a IPv6 as fallback + IPV6_FIRST = IPVF_BOTH|IPVF_IPV6, // resolve a IPv6 first, and a IPv4 as fallback +} +IpMode_t; + +ccp GetIpModeName ( IpMode_t ipm ); + +//----------------------------------------------------------------------------- + +ipv4_t GetIP4Mask ( int bits ); +ipv6_t GetIP6Mask ( int bits ); + +int GetBitsByMask4 ( ipv4_t ip4_nbo ); +int GetBitsByMask6 ( ipv6_t ip6_nbo ); +int GetBitsBySA ( sockaddr_t *sa, int return_on_error ); + +static inline int GetBitsByIN ( sockaddr_in_t *sa, int return_on_error ) + { return GetBitsBySA((sockaddr_t*)sa,return_on_error); } +static inline int GetBitsByIN4 ( sockaddr_in4_t *sa, int return_on_error ) + { return GetBitsBySA((sockaddr_t*)sa,return_on_error); } +static inline int GetBitsByIN6 ( sockaddr_in6_t *sa, int return_on_error ) + { return GetBitsBySA((sockaddr_t*)sa,return_on_error); } + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// some structs /////////////// /////////////////////////////////////////////////////////////////////////////// // [[KeywordTab_t]] diff --git a/project/dclib/dclib-ui.c b/project/dclib/dclib-ui.c index ab75d67..b477d74 100644 --- a/project/dclib/dclib-ui.c +++ b/project/dclib/dclib-ui.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -256,6 +256,19 @@ enumError CheckEnvOptions ( ccp varname, check_opt_func func ) TRACE("env[%s] = %s\n",varname,env); + #if 1 // [[new]] with ArgManager_t + + ArgManager_t am = {0}; + AppendArgManager(&am,ProgInfo.progname,0,false); + ScanQuotedArgManager(&am,env,true); + #ifdef DEBUG + for ( int i = 0; i < am.argc; i++ ) + printf(" [%02u] |%s|\n",i,am.argv[i]); + #endif + enumError stat = func(am.argc,am.argv,true); + + #else // [[old]] + const int envlen = strlen(env); char * buf = MALLOC(envlen+1); char * dest = buf; @@ -274,7 +287,7 @@ enumError CheckEnvOptions ( ccp varname, check_opt_func func ) while ( *(u8*)src > ' ' ) *dest++ = *src++; *dest++ = 0; - ASSERT( dest <= buf+envlen+1 ); + DASSERT( dest <= buf+envlen+1 ); } TRACE("argc = %d\n",argc); @@ -290,18 +303,19 @@ enumError CheckEnvOptions ( ccp varname, check_opt_func func ) while (*dest) dest++; dest++; - ASSERT( dest <= buf+envlen+1 ); + DASSERT( dest <= buf+envlen+1 ); } enumError stat = func(argc,argv,true); + + #endif + + // don't free() because of possible pointers into argv[] + if (stat) fprintf(stderr, "Error while scanning the environment variable '%s'\n",varname); - // don't free() because it's possible that there are pointers to arguments - //FREE(argv); - //FREE(buf); - return stat; } @@ -580,7 +594,8 @@ static void print_help_options GetTextMode(colmode,TTM_RESET) ); } - const int fw = GetTermWidth(80,40) - 1; +// const int fw = GetTermWidth(80,40) - 1; + const int fw = GetGoodTermWidth(0,false) - 1; const InfoOption_t *io_beg = iu->opt_info + beg_index; const InfoOption_t *io_end = iu->opt_info + end_index; @@ -704,7 +719,8 @@ void PrintHelpCommands } fprintf(f,"\n%*s%sCommands:%s\n\n", indent,"", col_head, col_reset ); - const int fw = GetTermWidth(80,40) - 1; +// const int fw = GetTermWidth(80,40) - 1; + const int fw = GetGoodTermWidth(0,false) - 1; int fw1 = 0, fw2 = 0; const InfoCommand_t * ic; @@ -836,7 +852,8 @@ void PrintHelpCmd cmd = 0; const InfoCommand_t * ic = iu->cmd_info + cmd; - const int fw = GetTermWidth(80,40) - 1; +// const int fw = GetTermWidth(80,40) - 1; + const int fw = GetGoodTermWidth(0,false) - 1; uint col_len; ccp col_head, col_reset; diff --git a/project/dclib/dclib-ui.h b/project/dclib/dclib-ui.h index 42171d9..cc7f327 100644 --- a/project/dclib/dclib-ui.h +++ b/project/dclib/dclib-ui.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-utf8.c b/project/dclib/dclib-utf8.c index 29a7f81..1ad06ce 100644 --- a/project/dclib/dclib-utf8.c +++ b/project/dclib/dclib-utf8.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -108,7 +108,7 @@ int GetUTF8CharLength ( u32 code ) char * NextUTF8Char ( ccp ptr ) { - // go to next next UTF8 character + // goto next UTF8 character switch (CheckUTF8Mode(*ptr)) { @@ -144,7 +144,7 @@ char * NextUTF8Char ( ccp ptr ) char * NextUTF8CharE( ccp ptr, ccp end ) { - // go to next next UTF8 character + // goto next UTF8 character if ( ptr >= end ) return (char*)end; @@ -181,6 +181,20 @@ char * NextUTF8CharE( ccp ptr, ccp end ) /////////////////////////////////////////////////////////////////////////////// +char * NextEUTF8Char ( ccp ptr ) +{ + return NextUTF8Char(SkipEscapes(ptr)); +} + +//----------------------------------------------------------------------------- + +char * NextEUTF8CharE ( ccp ptr, ccp end ) +{ + return NextUTF8CharE(SkipEscapesE(ptr,end),end); +} + +/////////////////////////////////////////////////////////////////////////////// + char * PrevUTF8Char ( ccp str ) { // go to the previous UTC character @@ -265,6 +279,82 @@ char * PrevUTF8CharB ( ccp str, ccp begin ) /////////////////////////////////////////////////////////////////////////////// +char * SkipUTF8Char ( ccp str, int skip ) +{ + if (str) + { + if ( skip < 0 ) + skip += ScanUTF8Length(str); + while ( skip-- > 0 ) + { + ccp next = NextUTF8Char(str); + if ( str == next ) + break; + str = next; + } + } + return (char*)str; +} + +//----------------------------------------------------------------------------- + +char * SkipUTF8CharE ( ccp str, ccp end, int skip ) +{ + if (str) + { + if ( skip < 0 ) + skip += ScanUTF8LengthE(str,end); + while ( skip-- > 0 ) + { + ccp next = NextUTF8CharE(str,end); + if ( str == next ) + break; + str = next; + } + } + return (char*)str; +} + +//----------------------------------------------------------------------------- + +char * SkipEUTF8Char ( ccp str, int skip ) +{ + if (str) + { + if ( skip < 0 ) + skip += ScanEUTF8Length(str); + while ( skip-- > 0 ) + { + ccp next = NextEUTF8Char(str); + if ( str == next ) + break; + str = next; + } + } + return (char*)str; +} + +//----------------------------------------------------------------------------- + +char * SkipEUTF8CharE ( ccp str, ccp end, int skip ) +{ + if (str) + { + if ( skip < 0 ) + skip += ScanEUTF8LengthE(str,end); + while ( skip-- > 0 ) + { + ccp next = NextEUTF8CharE(str,end); + if ( str == next ) + break; + str = next; + } + } + return (char*)str; +} + +/////////////////////////////////////////////////////////////////////////////// + u32 GetUTF8Char ( ccp str ) { // fast scan of a UTF8 char -> ignore errors in continuation bytes @@ -698,12 +788,57 @@ u32 ScanUTF8AnsiCharE ( ccp * p_str, ccp end ) /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -int ScanUTF8Length ( ccp str, ccp end ) +int ScanUTF8Length ( ccp str ) { int count = 0; - ccp ptr = str; - if (end) + if (str) + { + ccp ptr = str; + for(;;) + { + const char ch = *ptr++; + if (!ch) + break; + switch (CheckUTF8Mode(ch)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + ptr++; + break; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + ptr += 2; + break; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + ptr += 3; + break; + + default: + break; + } + count++; + } + } + return count; +} + +//----------------------------------------------------------------------------- + +int ScanUTF8LengthE ( ccp str, ccp end ) +{ + if (!end) + return ScanUTF8Length(str); + + int count = 0; + if (str) { + ccp ptr = str; while ( ptr < end ) { const char ch = *ptr++; @@ -733,13 +868,78 @@ int ScanUTF8Length ( ccp str, ccp end ) count++; } } - else + return count; +} + +//----------------------------------------------------------------------------- + +int ScanEUTF8Length ( ccp str ) +{ + int count = 0; + if (str) { + ccp ptr = str; for(;;) { const char ch = *ptr++; if (!ch) break; + + if ( ch == '\e' ) + { + ptr = SkipEscapes(ptr-1); + continue; + } + + switch (CheckUTF8Mode(ch)) + { + case DC_UTF8_2CHAR: + if ( CheckUTF8Mode(*ptr) == DC_UTF8_CONT_ANY ) + ptr++; + break; + + case DC_UTF8_3CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY ) + ptr += 2; + break; + + case DC_UTF8_4CHAR: + if ( CheckUTF8Mode(ptr[0]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[1]) == DC_UTF8_CONT_ANY + && CheckUTF8Mode(ptr[2]) == DC_UTF8_CONT_ANY ) + ptr += 3; + break; + + default: + break; + } + count++; + } + } + return count; +} + +//----------------------------------------------------------------------------- + +int ScanEUTF8LengthE ( ccp str, ccp end ) +{ + if (!end) + return ScanEUTF8Length(str); + + int count = 0; + if (str) + { + ccp ptr = str; + while ( ptr < end ) + { + const char ch = *ptr++; + if ( ch == '\e' ) + { + ptr = SkipEscapesE(ptr-1,end); + continue; + } + switch (CheckUTF8Mode(ch)) { case DC_UTF8_2CHAR: @@ -775,7 +975,7 @@ int ScanUTF8Length ( ccp str, ccp end ) int CalcUTF8PrintFW ( ccp str, ccp end, uint wanted_fw ) { const uint bytecount = end ? end - str : strlen(str); - const uint utf8len = ScanUTF8Length(str,end); + const uint utf8len = ScanUTF8LengthE(str,end); return utf8len < wanted_fw ? wanted_fw - utf8len + bytecount : bytecount; @@ -853,6 +1053,74 @@ char * PrintUTF8CharToCircBuf ( u32 code ) return buf; } +/////////////////////////////////////////////////////////////////////////////// + +ccp AlignUTF8ToCircBuf ( ccp str, int fw, int prec ) +{ + uint len = ScanUTF8Length(str); + if ( prec >= 0 && len > prec ) + len = prec; + + const int align_left = fw < 0; + if ( align_left) + fw = -fw; + if ( fw < len ) + fw = len; + int spaces = fw - len; + + ccp end = SkipUTF8Char(str,len); + const int copy_len = end - str; + + char *buf = GetCircBuf(copy_len+spaces+1); + DASSERT(buf); + if (align_left) + { + memcpy(buf,str,copy_len); + memset(buf+copy_len,' ',spaces); + } + else + { + memset(buf,' ',spaces); + memcpy(buf+spaces,str,copy_len); + } + buf[copy_len+spaces] = 0; + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp AlignEUTF8ToCircBuf ( ccp str, int fw, int prec ) +{ + uint len = ScanEUTF8Length(str); + if ( prec >= 0 && len > prec ) + len = prec; + + const int align_left = fw < 0; + if ( align_left) + fw = -fw; + if ( fw < len ) + fw = len; + int spaces = fw - len; + + ccp end = SkipEUTF8Char(str,len); + const int copy_len = end - str; + + char *buf = GetCircBuf(copy_len+spaces+1); + DASSERT(buf); + if (align_left) + { + memcpy(buf,str,copy_len); + memset(buf+copy_len,' ',spaces); + } + else + { + memset(buf,' ',spaces); + memcpy(buf+spaces,str,copy_len); + } + buf[copy_len+spaces] = 0; + return buf; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/dclib-utf8.h b/project/dclib/dclib-utf8.h index adea8dc..c4d9380 100644 --- a/project/dclib/dclib-utf8.h +++ b/project/dclib/dclib-utf8.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -89,24 +89,56 @@ static inline dcUTF8Mode CheckUTF8Mode ( unsigned char ch ) { return (dcUTF8Mode)TableUTF8Mode[ch]; } int GetUTF8CharLength ( u32 code ); -char * NextUTF8Char ( ccp str ); +char * NextUTF8Char ( ccp str ); char * NextUTF8CharE ( ccp str, ccp end ); -char * PrevUTF8Char ( ccp str ); +char * PrevUTF8Char ( ccp str ); char * PrevUTF8CharB ( ccp str, ccp begin ); -u32 GetUTF8Char ( ccp str ); -u32 ScanUTF8Char ( ccp * str ); -u32 ScanUTF8CharE ( ccp * str, ccp end ); -u32 ScanUTF8CharInc ( ccp * str ); -u32 ScanUTF8CharIncE ( ccp * str, ccp end ); -u32 GetUTF8AnsiChar ( ccp str ); -u32 ScanUTF8AnsiChar ( ccp * str ); +char * SkipUTF8Char ( ccp str, int skip ); +char * SkipUTF8CharE ( ccp str, ccp end, int skip ); + +u32 GetUTF8Char ( ccp str ); +u32 ScanUTF8Char ( ccp * str ); +u32 ScanUTF8CharE ( ccp * str, ccp end ); +u32 ScanUTF8CharInc ( ccp * str ); +u32 ScanUTF8CharIncE ( ccp * str, ccp end ); +u32 GetUTF8AnsiChar ( ccp str ); +u32 ScanUTF8AnsiChar ( ccp * str ); u32 ScanUTF8AnsiCharE ( ccp * str, ccp end ); -int ScanUTF8Length ( ccp str, ccp end ); -int CalcUTF8PrintFW ( ccp str, ccp end, uint wanted_fw ); +int ScanUTF8Length ( ccp str ); +int ScanUTF8LengthE ( ccp str, ccp end ); +int CalcUTF8PrintFW ( ccp str, ccp end, uint wanted_fw ); char * PrintUTF8Char ( char * buf, u32 code ); char * PrintUTF8CharToCircBuf ( u32 code ); +ccp AlignUTF8ToCircBuf ( ccp str, int fw, int prec ); + +/////////////////////////////////////////////////////////////////////////////// +// special variants, that ignore known escape seqeuences + +char * NextEUTF8Char ( ccp str ); +char * NextEUTF8CharE ( ccp str, ccp end ); +char * SkipEUTF8Char ( ccp str, int skip ); +char * SkipEUTF8CharE ( ccp str, ccp end, int skip ); +int ScanEUTF8Length ( ccp str ); +int ScanEUTF8LengthE ( ccp str, ccp end ); + +ccp AlignEUTF8ToCircBuf ( ccp str, int fw, int prec ); + +/////////////////////////////////////////////////////////////////////////////// + +static inline int strlen8 ( ccp str ) + { return ScanUTF8Length(str); } + +static inline int strlen8e ( ccp str, ccp end ) + { return ScanUTF8LengthE(str,end); } + +static inline int strlene8 ( ccp str ) + { return ScanEUTF8Length(str); } + +static inline int strlene8e ( ccp str, ccp end ) + { return ScanEUTF8LengthE(str,end); } + /////////////////////////////////////////////////////////////////////////////// typedef struct dcUnicodeTripel diff --git a/project/dclib/dclib-vector.c b/project/dclib/dclib-vector.c index 233e7ef..af18888 100644 --- a/project/dclib/dclib-vector.c +++ b/project/dclib/dclib-vector.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-xdump.c b/project/dclib/dclib-xdump.c index df2f7f9..f4175b0 100644 --- a/project/dclib/dclib-xdump.c +++ b/project/dclib/dclib-xdump.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/dclib-xdump.h b/project/dclib/dclib-xdump.h index 06f602b..0cf9938 100644 --- a/project/dclib/dclib-xdump.h +++ b/project/dclib/dclib-xdump.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * diff --git a/project/dclib/lib-bmg.c b/project/dclib/lib-bmg.c index c1d24ce..0540243 100644 --- a/project/dclib/lib-bmg.c +++ b/project/dclib/lib-bmg.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -500,7 +500,7 @@ uint PrintString16BMG xd.indent = 2; xd.assumed_size = mem.len; XDiff( &xd, buf,dest-buf,true, mem.ptr,mem.len,true, 5,false ); - } + } ResetFastBuf(&fb.b); #endif @@ -542,7 +542,7 @@ uint PrintFastBuf16BMG char buf[1300]; char *dest = buf; - char *dest_term = dest + sizeof(buf) - 1; + char *dest_term = dest + sizeof(buf) - 1; char *dest_end = dest_term - 270; // escape sequence with 256 bytes possible char *last_u = 0; @@ -6442,6 +6442,36 @@ void DumpCtBMG ( FILE *f, int indent, ct_bmg_t *ctb ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// sizeof_info_t: bmg /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sizeof_info_bmg]] + +const sizeof_info_t sizeof_info_bmg[] = +{ + SIZEOF_INFO_TITLE("BMG") + SIZEOF_INFO_ENTRY(bmg_range_t) + SIZEOF_INFO_ENTRY(bmg_section_t) + SIZEOF_INFO_ENTRY(bmg_header_t) + SIZEOF_INFO_ENTRY(bmg_inf_item_t) + SIZEOF_INFO_ENTRY(bmg_inf_t) + SIZEOF_INFO_ENTRY(bmg_dat_t) + SIZEOF_INFO_ENTRY(bmg_mid_t) + SIZEOF_INFO_ENTRY(bmg_flw_t) + SIZEOF_INFO_ENTRY(bmg_fli_t) + SIZEOF_INFO_ENTRY(bmg_sect_info_t) + SIZEOF_INFO_ENTRY(bmg_sect_list_t) + SIZEOF_INFO_ENTRY(bmg_raw_section_t) + SIZEOF_INFO_ENTRY(bmg_item_t) + SIZEOF_INFO_ENTRY(bmg_t) + SIZEOF_INFO_ENTRY(bmg_create_t) + SIZEOF_INFO_ENTRY(bmg_scan_mid_t) + SIZEOF_INFO_ENTRY(ct_bmg_t) + + SIZEOF_INFO_TERM() +}; + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// E N D /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/lib-bmg.h b/project/dclib/lib-bmg.h index 46cccac..98d3d9e 100644 --- a/project/dclib/lib-bmg.h +++ b/project/dclib/lib-bmg.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -70,7 +70,6 @@ typedef u16 bmg_slot_t; // type of slot typedef struct bmg_t bmg_t; - // /////////////////////////////////////////////////////////////////////////////// /////////////// MKW specific consts /////////////// @@ -259,6 +258,8 @@ extern u8 bmg_def_attrib[BMG_ATTRIB_SIZE]; // 2: string param extern const KeywordTab_t PatchKeysBMG[]; +extern const sizeof_info_t sizeof_info_bmg[]; + // /////////////////////////////////////////////////////////////////////////////// /////////////// CT/LE support /////////////// @@ -920,7 +921,7 @@ typedef struct bmg_create_t bmg_t *bmg; // current bmg const endian_func_t // endian functions, never NULL *endian; // init by bmg->endian, overridden by opt_bmg_endian - + FastBuf_t inf; // INF1 data FastBuf_t dat; // DAT1 data diff --git a/project/dclib/lib-dol.c b/project/dclib/lib-dol.c index d671193..94b73bb 100644 --- a/project/dclib/lib-dol.c +++ b/project/dclib/lib-dol.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -1201,5 +1201,26 @@ void ResetWCH ( wch_control_t * wc ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// sizeof_info_t: dol /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sizeof_info_dol]] + +const sizeof_info_t sizeof_info_dol[] = +{ + SIZEOF_INFO_TITLE("DOL") + SIZEOF_INFO_ENTRY(dol_header_t) + SIZEOF_INFO_ENTRY(dol_sect_info_t) + SIZEOF_INFO_ENTRY(dol_sect_select_t) + SIZEOF_INFO_ENTRY(dol_sect_addr_t) + SIZEOF_INFO_ENTRY(gch_header_t) + SIZEOF_INFO_ENTRY(wch_segment_t) + SIZEOF_INFO_ENTRY(wch_header_t) + SIZEOF_INFO_ENTRY(wch_control_t) + + SIZEOF_INFO_TERM() +}; + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// E N D /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/lib-dol.h b/project/dclib/lib-dol.h index 0f03e25..c1d8b7b 100644 --- a/project/dclib/lib-dol.h +++ b/project/dclib/lib-dol.h @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library 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, * + * This library 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. * @@ -123,7 +123,7 @@ typedef struct dol_sect_addr_t { //--- source info u32 addr; // address of data - u32 size; // size of data + u32 size; // size of data //--- search info int section; // section index, <0: invalid @@ -149,6 +149,8 @@ static inline ccp GetDolSectionName ( uint section ) return section < DOL_NN_SECTIONS ? dol_section_name[section] : 0; } +extern const sizeof_info_t sizeof_info_dol[]; + /////////////////////////////////////////////////////////////////////////////// bool GetDolSectionInfo diff --git a/project/dclib/lib-mkw.c b/project/dclib/lib-mkw.c index bbfb316..538e24b 100644 --- a/project/dclib/lib-mkw.c +++ b/project/dclib/lib-mkw.c @@ -15,7 +15,7 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * @@ -38,6 +38,8 @@ #include "lib-mkw.h" #include "dclib-numeric.h" +#include "lib-mkw-def.c" + // /////////////////////////////////////////////////////////////////////////////// /////////////// assigned points: tables /////////////// @@ -973,7 +975,7 @@ int GetVrDiffByTab ( int diff ) // diff = VR(winner) - VR(loser) const s16 search = diff; int i = 0, j = sizeof(vr_diff_tab) / sizeof(*vr_diff_tab) - 1; - + while ( i < j ) { const int k = (i+j)/2; @@ -1103,5 +1105,246 @@ ccp GetMkwMusicName3 ( int mid ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// engines /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const char mkw_engine_info[MKW_ENGINE__N+1][7] = +{ + "50cc", + "100cc", + "150cc", + "mirror", + "200cc", + "?", +}; + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// slots, drivers and vehicles /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp GetSlotHex ( u8 slot ) +{ + if (IsMkwSlotValid(slot)) + { + char *dest = GetCircBuf(2); + dest[0] = LoDigits[slot]; + dest[1] = 0; + return dest; + } + return "-"; +} + +//----------------------------------------------------------------------------- + +ccp GetSlotNum ( u8 slot ) +{ + if (IsMkwSlotValid(slot)) + return PrintCircBuf("%2u",slot); + return " -"; +} + +/////////////////////////////////////////////////////////////////////////////// + +const mkw_driver_t mkw_driver_attrib[MKW_N_DRIVER+1] = +{ + { 1, 0, "Mari", "Mari", "Mario", "Mario" }, // M0 #0 + { 0, 0, "B.Pe", "B.Pe", "Baby Peach", "Baby Peach" }, // S2 + { 2, 0, "Walu", "Walu", "Waluigi", "Waluigi" }, // L1 + { 2, 0, "Bows", "Bows", "Bowser", "Bowser" }, // L3 + + { 0, 0, "B.Da", "B.Da", "Baby Daisy", "Baby Daisy" }, // S3 #4 + { 0, 0, "DBon", "KnTr", "Dry Bones", "Knochentrocken" }, // S7 + { 0, 0, "B.Ma", "B.Ma", "Baby Mario", "Baby Mario" }, // S0 + { 1, 0, "Luig", "Luig", "Luigi", "Luigi" }, // M1 + + { 0, 0, "Toad", "Toad", "Toad", "Toad" }, // S4 #8 + { 2, 0, "Donk", "Donk", "Donkey Kong", "Donkey Kong" }, // L2 + { 1, 0, "Yosh", "Yosh", "Yoshi", "Yoshi" }, // M4 + { 2, 0, "Wari", "Wari", "Wario", "Wario" }, // L0 + + { 0, 0, "B.Lu", "B.Lu", "Baby Luigi", "Baby Luigi" }, // S1 #12 + { 0, 0, "Tdte", "Tdte", "Toadette", "Toadette" }, // S5 + { 0, 0, "Koop", "Koop", "Koopa Troopa", "Koopa" }, // S6 + { 1, 0, "Dais", "Dais", "Daisy", "Daisy" }, // M3 + + { 1, 0, "Peac", "Peac", "Peach", "Peach" }, // M2 #16 + { 1, 0, "Bird", "Bird", "Birdo", "Birdo" }, // M5 + { 1, 0, "DidK", "DidK", "Diddy Kong", "Diddy Kong" }, // M6 + { 2, 0, "KBoo", "BuHu", "King Boo", "König Buu Huu" }, // L4 + + { 1, 0, "B.Jr", "B.Jr", "Bowser Jr.", "Bowser Jr." }, // M7 #20 + { 2, 0, "DBow", "KnBo", "Dry Bowser", "Knochen-Bowser" }, // L7 + { 2, 0, "Funk", "Funk", "Funky Kong", "Funky Kong" }, // L6 + { 2, 0, "Rosa", "Rosa", "Rosalina", "Rosalina" }, // L5 + + { 0, 1, "MAsm", "MAkm", "Mii A (small,male)", "Mii A (klein,männ)" }, // S8 #24 + { 0, 1, "MAsf", "MAkw", "Mii A (small,female)", "Mii A (klein,weib)" }, // S9 + { 0, 2, "MBsm", "MBkm", "Mii B (small,male)", "Mii B (klein,männ)" }, // S10 + { 0, 2, "MBsf", "MBkw", "Mii B (small,female)", "Mii B (klein,weib)" }, // S11 + { 0, 3, "MCsm", "MCkm", "Mii C (small,male)", "Mii C (klein,männ)" }, // 0x11c + { 0, 3, "MCsf", "MCkw", "Mii C (small,female)", "Mii C (klein,weib)" }, // 0x11d + + { 1, 1, "MAmm", "MAmm", "Mii A (medium,male)", "Mii A (mittel,männ)" },// M8 #30 + { 1, 1, "MAmf", "MAmw", "Mii A (medium,female)","Mii A (mittel,weib)" },// M9 + { 1, 2, "MBmm", "MBmm", "Mii B (medium,male)", "Mii B (mittel,männ)" },// M10 + { 1, 2, "MBmf", "MBmw", "Mii B (medium,female)","Mii B (mittel,weib)" },// M11 + { 1, 3, "MCmm", "MCmm", "Mii C (medium,male)", "Mii C (mittel,männ)" },// 0x122 + { 1, 3, "MCmf", "MCmw", "Mii C (medium,female)","Mii C (mittel,weib)" },// 0x123 + + { 2, 1, "MAlm", "MAgm", "Mii A (large,male)", "Mii A (groß,männ)" }, // L8 #36 + { 2, 1, "MAlf", "MAgw", "Mii A (large,female)", "Mii A (groß,weib)" }, // L9 + { 2, 2, "MBlm", "MBgm", "Mii B (large,male)", "Mii B (groß,männ)" }, // L10 + { 2, 2, "MBlf", "MBgw", "Mii B (large,female)", "Mii B (groß,weib)" }, // L11 + { 2, 3, "MClm", "MCgm", "Mii C (large,male)", "Mii C (groß,männ)" }, // 0x128 + { 2, 3, "MClf", "MCgw", "Mii C (large,female)", "Mii C (groß,weib)" }, // 0x129 + + {0,0,"","",0,0} // TERM #42 +}; + +//----------------------------------------------------------------------------- + +ccp GetDriverNum ( u8 driver ) +{ + switch (driver) + { + case 48: return " ?"; + case 254: return " ."; + case 255: return " -"; + } + + return PrintCircBuf("%3u",driver); +} + +//----------------------------------------------------------------------------- + +ccp GetDriverName4 ( u8 driver ) +{ + if ( driver < MKW_N_DRIVER ) + { + ccp res = mkw_driver_attrib[driver].id_en; + if (*res) + return res; + } + + switch (driver) + { + case 48: return " ?? "; + case 254: return " .. "; + case 255: return " -- "; + } + + return PrintCircBuf("%3u.",driver); +} + +/////////////////////////////////////////////////////////////////////////////// + +const mkw_vehicle_t mkw_vehicle_attrib[MKW_N_VEHICLE+1] = +{ + { 0,0,1, "Ka-S", "MinK", "Standard Kart S", "Mini-Kart" }, // SK0 #0 + { 1,0,1, "Ka-M", "MedK", "Standard Kart M", "Medi-Kart" }, // MK0 + { 2,0,1, "Ka-L", "MaxK", "Standard Kart L", "Maxi-Kart" }, // LK0 + { 0,0,0, "Boos", "Baby", "Booster Seat", "Baby-Booster" }, // SK1 + { 1,0,0, "Drag", "Nost", "Classic Dragster","Nostalgia 1" }, // MK1 + { 2,0,0, "OffR", "OffR", "Offroader", "Offroader" }, // LK1 + + { 0,0,0, "Mini", "MinB", "Mini Beast", "Minibiest" }, // SK2 #6 + { 1,0,0, "Wing", "Wind", "Wild Wing", "Windschnitte" }, // MK2 + { 2,0,0, "Flam", "Feue", "Flame Flyer", "Feuerschleuder" }, // LK2 + { 0,0,0, "Chep", "BobC", "Cheep Charger", "Bob-Cheep" }, // SK3 + { 1,0,0, "Bloo", "Bloo", "Super Blooper", "Blooper-Bolide" }, // MK3 + { 2,0,0, "Pira", "PPir", "Piranha Prowler", "Pistenpiranha" }, // LK3 + + { 0,0,0, "Tita", "Monz", "Tiny Titan", "Monztruck" }, // SK4 #12 + { 1,0,0, "Dayt", "Cabr", "Daytripper", "Cabriosa" }, // MK4 + { 2,0,0, "JetS", "Ster", "Jetsetter", "Sterngleiter" }, // LK4 + { 0,0,0, "Falc", "Falc", "Blue Falcon", "Blue Falcon" }, // SK5 + { 1,0,0, "Spri", "Glor", "Sprinter", "Glory" }, // MK5 + { 2,0,0, "Hony", "Turb", "Honeycoupe", "Turbetto" }, // LK5 + + { 0,1,1, "Bi-S", "MinB", "Standard Bike S", "Mini-Bike" }, // SB0 #18 + { 1,1,1, "Bi-M", "MedB", "Standard Bike M", "Medi-Bike" }, // MB0 + { 2,1,1, "Bi-L", "MaxB", "Standard Bike L", "Maxi-Bike" }, // LB0 + { 0,2,0, "Bull", "Wili", "Bullet Bike", "Willi-Bike" }, // SB1 + { 1,2,0, "Mach", "Mach", "Mach Bike", "Mach-Bike" }, // MB1 + { 2,2,0, "Flam", "BowB", "Flame Runner", "Bowser-Bike" }, // LB1 + + { 0,1,0, "BitB", "Blit", "Bit Bike", "Blitzer" }, // SB2 #24 + { 1,1,0, "Suga", "Bonb", "Sugarscoot", "Bonbon-Bike" }, // MB2 + { 2,1,0, "WarB", "WarB", "Wario Bike", "Wario-Bike" }, // LB2 + { 0,2,0, "Quac", "Quak", "Quacker", "Quakquak" }, // SB3 + { 1,1,0, "ZipZ", "Rapi", "Zip Zip", "Rapido" }, // MB3 + { 2,1,0, "Shoo", "Schn", "Shooting Star", "Schnuppe" }, // LB3 + + { 0,2,0, "MKru", "Kame", "Magikruiser", "Kameknaller" }, // SB4 #30 + { 1,2,0, "Snea", "Pist", "Sneakster", "Pistensturm" }, // MB4 + { 2,2,0, "Spea", "Torp", "Spear", "Torped" }, // LB4 + { 0,2,0, "JetB", "JetA", "Jet Bubble", "Jet-A" }, // SB5 + { 1,2,0, "Dolp", "Flip", "Dolphin Dasher", "Flippster" }, // MB5 + { 2,1,0, "Phan", "Phan", "Phantom", "Phantom" }, // LB5 + + {0,0,0,"","",0,0} // TERM #36 +}; + +//----------------------------------------------------------------------------- + +ccp GetVehicleNum ( u8 vehicle ) +{ + switch (vehicle) + { + case 36: return " ?"; + case 254: return " ."; + case 255: return " -"; + } + + return PrintCircBuf("%3u",vehicle); +} + +//----------------------------------------------------------------------------- + +ccp GetVehicleName4 ( u8 vehicle ) +{ + if ( vehicle < MKW_N_VEHICLE ) + return mkw_vehicle_attrib[vehicle].id_en; + + switch (vehicle) + { + case 36: return " ?? "; + case 254: return " .. "; + case 255: return " -- "; + } + + return PrintCircBuf("%3u.",vehicle); +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// sizeof_info_t: mkw /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[sizeof_info_mkw]] + +const sizeof_info_t sizeof_info_mkw[] = +{ + SIZEOF_INFO_TITLE("Mario Kart Wii") + + SIZEOF_INFO_ENTRY(mkw_std_pbits1_t) + SIZEOF_INFO_ENTRY(mkw_std_pbits2_t) + SIZEOF_INFO_ENTRY(mkw_ex_pbits1_t) + SIZEOF_INFO_ENTRY(mkw_ex_pbits2_t) + SIZEOF_INFO_ENTRY(mkw_pbits1_t) + SIZEOF_INFO_ENTRY(mkw_pbits2_t) + SIZEOF_INFO_ENTRY(MkwPointInfo_t) + SIZEOF_INFO_ENTRY(mkw_engine_t) + SIZEOF_INFO_ENTRY(mkw_engine_info) + SIZEOF_INFO_ENTRY(mkw_driver_t) + SIZEOF_INFO_ENTRY(mkw_driver_attrib) + SIZEOF_INFO_ENTRY(mkw_vehicle_t) + SIZEOF_INFO_ENTRY(mkw_vehicle_attrib) + + SIZEOF_INFO_TERM() +}; + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// /////////////////////////////////////////////////////////////////////////////// diff --git a/project/dclib/lib-mkw.h b/project/dclib/lib-mkw.h index 16f7f86..f0df9b5 100644 --- a/project/dclib/lib-mkw.h +++ b/project/dclib/lib-mkw.h @@ -15,7 +15,7 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * @@ -37,11 +37,11 @@ #define SZS_LIB_MKWII_H 1 #define _GNU_SOURCE 1 -#include "dclib-basics.h" +#include "lib-mkw-def.h" // /////////////////////////////////////////////////////////////////////////////// -/////////////// CTW definitions / ////////////// +/////////////// CTW definitions /////////////// /////////////////////////////////////////////////////////////////////////////// #define CTW_MAX_TRACK_ID 999999 @@ -51,6 +51,10 @@ /////////////// base definitions /////////////// /////////////////////////////////////////////////////////////////////////////// +extern const sizeof_info_t sizeof_info_mkw[]; + +/////////////////////////////////////////////////////////////////////////////// + enum mkw_consts_t // some constants { //--- num of players @@ -119,10 +123,10 @@ static inline bool IsMkwCustom ( uint tid ) #endif // unsigned types, that can hold 1 or 2 bits for each player -typedef u16 mkw_std_pbits1_t; // 12 bits needed -typedef u32 mkw_std_pbits2_t; // 24 bits needed -typedef u32 mkw_ex_pbits1_t; // 24 bits needed -typedef u64 mkw_ex_pbits2_t; // 48 bits needed +typedef u16 mkw_std_pbits1_t; // 12 bits needed +typedef u32 mkw_std_pbits2_t; // 24 bits needed +typedef u32 mkw_ex_pbits1_t; // 24 bits needed +typedef u64 mkw_ex_pbits2_t; // 48 bits needed #if SUPPORT_24P typedef u32 mkw_pbits1_t; @@ -138,6 +142,8 @@ typedef u64 mkw_ex_pbits2_t; // 48 bits needed #define MKW_EX_MAX_PLAYER2 ( MKW_EX_MAX_PLAYER * MKW_EX_MAX_PLAYER ) #define MKW_MAX_PLAYER2 ( MKW_MAX_PLAYER * MKW_MAX_PLAYER ) +#define MAX_ROOM_MEMBERS MKW_MAX_PLAYER + // /////////////////////////////////////////////////////////////////////////////// /////////////// assigned points for versus and rooms /////////////// @@ -156,7 +162,7 @@ typedef u64 mkw_ex_pbits2_t; // 48 bits needed typedef enum MkwPointID { //--- tables - MPI_UNKNOWN, // no table assigend + MPI_UNKNOWN, // no table assigned MPI_NINTENDO, // original table MPI_LINEAR, // 4 linear tables @@ -198,7 +204,7 @@ MkwPointInfo_t; //----------------------------------------------------------------------------- -const MkwPointInfo_t MkwPointInfo[MPI__N+1]; +extern const MkwPointInfo_t MkwPointInfo[MPI__N+1]; // [[24P--]] all tables @@ -301,6 +307,90 @@ ccp GetMkwMusicName3 ( int mid ); // /////////////////////////////////////////////////////////////////////////////// +/////////////// engines /////////////// +/////////////////////////////////////////////////////////////////////////////// + +typedef enum mkw_engine_t +{ + MKW_ENGINE_50CC, + MKW_ENGINE_100CC, + MKW_ENGINE_150CC, + MKW_ENGINE_MIRROR, + MKW_ENGINE_200CC, + MKW_ENGINE__N +} +__attribute__ ((packed)) mkw_engine_t; + +extern const char mkw_engine_info[MKW_ENGINE__N+1][7]; + +static inline ccp GetMkwEngineInfo ( mkw_engine_t engine ) +{ + return (uint)engine < MKW_ENGINE__N ? mkw_engine_info[engine] : "?"; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// slots, drivers and vehicles /////////////// +/////////////////////////////////////////////////////////////////////////////// +// [[mkw_driver_t]] + +typedef struct mkw_driver_t +{ + u8 weight; // 0:small, 1:medium, 2:large + u8 is_mii; // 0:no Mii, 1:A, 2:B, 3:B + char id_en[5]; + char id_de[5]; + ccp name_en; + ccp name_de; +} +mkw_driver_t; + +#define MKW_N_DRIVER 42 +extern const mkw_driver_t mkw_driver_attrib[MKW_N_DRIVER+1]; + +//----------------------------------------------------------------------------- +// [[mkw_vehicle_t]] + +typedef struct mkw_vehicle_t +{ + u8 weight; // 0:small, 1:medium, 2:large + u8 is_bike; // 0:kart, 1:out-bike, 2:in-bike + u8 battle; // 0:not used in battle, 1:used in battle + char id_en[5]; + char id_de[5]; + ccp name_en; + ccp name_de; +} +mkw_vehicle_t; + +#define MKW_N_VEHICLE 36 +extern const mkw_vehicle_t mkw_vehicle_attrib[MKW_N_VEHICLE+1]; + +//----------------------------------------------------------------------------- + +static inline bool IsMkwSlotValid ( uint slot ) + { return slot < MAX_ROOM_MEMBERS; } + +static inline int GetDriverInt ( u8 driver ) + { return driver >= 254 ? (int)driver - 256 : driver; } + +static inline int GetVehicleInt ( u8 vehicle ) + { return vehicle >= 254 ? (int)vehicle - 256 : vehicle; } + +//----------------------------------------------------------------------------- + +// for optimization: `colset´ is always valid and never NULL! + +ccp GetSlotHex ( u8 slot ); +ccp GetSlotNum ( u8 slot ); +ccp GetDriverNum ( u8 driver ); +ccp GetVehicleNum ( u8 vehicle ); + +ccp GetDriverName4 ( u8 driver ); +ccp GetVehicleName4 ( u8 vehicle ); + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// /////////////////////////////////////////////////////////////////////////////// |
