aboutsummaryrefslogtreecommitdiff
path: root/project/dclib
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--project/dclib/Makefile.inc49
-rw-r--r--project/dclib/dclib-basics.c1650
-rw-r--r--project/dclib/dclib-basics.h1731
-rw-r--r--project/dclib/dclib-cli.c248
-rw-r--r--project/dclib/dclib-color.c599
-rw-r--r--project/dclib/dclib-color.h308
-rw-r--r--project/dclib/dclib-debug.c19
-rw-r--r--project/dclib/dclib-debug.h48
-rw-r--r--project/dclib/dclib-file.c798
-rw-r--r--project/dclib/dclib-file.h164
-rw-r--r--project/dclib/dclib-gen-ui.h6
-rw-r--r--project/dclib/dclib-gen-ui.inc6
-rw-r--r--project/dclib/dclib-mysql.c195
-rw-r--r--project/dclib/dclib-mysql.h38
-rw-r--r--project/dclib/dclib-network-linux.c8
-rw-r--r--project/dclib/dclib-network-linux.h6
-rw-r--r--project/dclib/dclib-network.c1453
-rw-r--r--project/dclib/dclib-network.h414
-rw-r--r--project/dclib/dclib-numeric.c3194
-rw-r--r--project/dclib/dclib-numeric.h30
-rw-r--r--project/dclib/dclib-option.c61
-rw-r--r--project/dclib/dclib-option.h46
-rw-r--r--project/dclib/dclib-regex.c8
-rw-r--r--project/dclib/dclib-regex.h6
-rw-r--r--project/dclib/dclib-shift-jis.c6
-rw-r--r--project/dclib/dclib-system.h6
-rw-r--r--project/dclib/dclib-tables.c508
-rw-r--r--project/dclib/dclib-types.h137
-rw-r--r--project/dclib/dclib-ui.c41
-rw-r--r--project/dclib/dclib-ui.h6
-rw-r--r--project/dclib/dclib-utf8.c288
-rw-r--r--project/dclib/dclib-utf8.h60
-rw-r--r--project/dclib/dclib-vector.c6
-rw-r--r--project/dclib/dclib-xdump.c6
-rw-r--r--project/dclib/dclib-xdump.h6
-rw-r--r--project/dclib/lib-bmg.c40
-rw-r--r--project/dclib/lib-bmg.h11
-rw-r--r--project/dclib/lib-dol.c27
-rw-r--r--project/dclib/lib-dol.h10
-rw-r--r--project/dclib/lib-mkw.c247
-rw-r--r--project/dclib/lib-mkw.h108
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 ///////////////
///////////////////////////////////////////////////////////////////////////////