diff options
| author | Wiimm <wiimm@wiimm.de> | 2022-05-13 19:22:13 +0200 |
|---|---|---|
| committer | Wiimm <wiimm@wiimm.de> | 2022-05-13 19:22:13 +0200 |
| commit | 568365f5bdd4dc71a156ca963d89ac002f405a34 (patch) | |
| tree | 730d0311536d8fd46a56b74e2c8cac028e778243 /project/dclib/dclib-basics.c | |
| parent | 9ae8841ec4720e74173e2f72fac33f0848bbf435 (diff) | |
SZS v2.27a
Diffstat (limited to 'project/dclib/dclib-basics.c')
| -rw-r--r-- | project/dclib/dclib-basics.c | 1650 |
1 files changed, 1631 insertions, 19 deletions
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; |
