//! Displaying match data
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <macros.h>
# include <comp.h>
# include <display.h>
# define FLAGSET(f, flag) (( (f) & (flag) ) == (flag))
# define DFLAGSET(f, flagname) FLAGSET(f, DISPF_SHOW_ ## flagname)
# define CMP(x,cmp,y) ( (x) cmp (y) ? (x) : (y) )
# define CMPv(x,cmp,y) ({ let _x = (x); let _y = (y); CMP(_x, cmp, _y); })
# define MIN(x, y) CMP((x), <, (y))
# define MINv(x, y) CMPv((x), <, (y))
# define MAX(x, y) CMP((x), >, (y))
# define MAXv(x, y) CMPv((x), >, (y))
// Returns number of bytes written to `dest`. Otherwise same as `strncpy()`.
static usize strncpy_n ( char * pOUT dest , const char * restrict src , usize n )
{
usize i ;
for ( i = 0 ; i < n ; i + + )
if ( ! ( * dest + + = * src + + ) ) return i ;
dest [ i ] = 0 ;
return i ;
}
// Will not write past `dn` bytes of `dest`, will not read past `sn` bytes of `src`. Otherwise, same as `strncpy_n()`.
static usize s_strncpy_n ( usize sn , usize dn ; char dest [ static pOUT dn ] , usize dn , const char src [ static restrict sn ] , usize sn )
{
usize i ;
usize n = MIN ( sn , dn ) ;
for ( i = 0 ; i < n ; i + + )
if ( ! ( * dest + + = * src + + ) ) return i ;
if ( i < dn ) dest [ i ] = 0 ;
return i ;
}
// Will not write past `dn` bytes of `dest`, will not read past `sn` bytes of `src`. Otherwise, same as `strncat_n()`.
static inline usize s_strncat_n ( usize sn , usize dn ; char dest [ static pOUT dn ] , usize dn , const char src [ static restrict sn ] , usize sn )
{
usize dl = MINv ( strlen ( dest ) , dn ) ;
return s_strncpy_n ( dest + dl , dn - dl , src , sn ) ;
}
// Returns number of bytes written to (dest+strlen(dest)). Otherwise same as `strncat()`.
static inline usize strncat_n ( char * pOUT dest , const char * restrict src , usize n )
{
usize dl = strlen ( dest ) ;
return strncpy_n ( dest + dl , src , n ) ;
}
//TODO: This isn't working at all...
# ifdef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
_dead
# endif
static usize _display_get_fmt_n ( dispflags_t flags , bool matched , usize _n , char str [ pOUT _n ] )
{
register usize n = _n , fw = 0 ;
const char * fmts [ ] = {
DFLAGSET ( flags , FAILURES )
? " failure: "
: " " ,
" match: " ,
" %lu: " ,
SLICE_FORMAT ,
" (len %lu) " ,
( const char * ) NULL ,
} ;
usize w = s_strncpy_n ( str , n , fmts [ ! ! matched ] , n ) ; // SAFETY: Will never read past `fmts[x]` nul-terminator, so setting n as both dest and src length here is safe.
//TODO: Fix this... It isn't working...
# define ADDSTR(s) do { \
debug_assert ( ( s ) ) ; \
w = MINv ( s_strncat_n ( str , n , ( s ) , n ) , n ) ; \
/* Remove written from `n` */ \
n - = w ; \
/* Check if we have written into the limit (if so, w will be equal to n above, since w is high-bounded to n by `MINv()` above. So if n is 0, we have hit the limit. */ \
ifU ( ! n ) { \
/*XXX: Should this be `str[w-1]?` If this assert fails then yes it should. */ \
debug_assert ( w < _n ) ; \
str [ w ] = 0 ; \
return fw + w ; \
} \
/* Increase `str` pointer by written */ \
str + = w ; \
/* Increase return value by written. */ \
fw + = w ; \
} while ( 0 )
if ( DFLAGSET ( flags , NUMBER ) ) ADDSTR ( fmts [ 2 ] ) ;
if ( DFLAGSET ( flags , SLICE ) ) ADDSTR ( fmts [ 3 ] ) ;
if ( DFLAGSET ( flags , LENGTH ) ) ADDSTR ( fmts [ 4 ] ) ;
ADDSTR ( " \n " ) ;
TRACE ( " Output string written: %lu, (rlen %lu) " , fw , n ) ;
# undef ADDSTR
return fw ;
}
//XXX: This works, but seems inherantly unsafe. Rewrite to use the above `strn*_n()` functions for better length tracking.
# ifndef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
_dead deprecated_message ( " this is unsafe, use `_display_get_fmt_n` " )
# endif
static usize _display_get_fmt0 ( dispflags_t flags , bool matched , usize _len , char str [ pOUT _len ] )
{
register usize len = _len ;
static const char * fmts [ ] = { " match: " , " %lu: " , SLICE_FORMAT , " (len %lu) " , " failure: " , ( const char * ) NULL } ;
# define ADDSTR(s) do { debug_assert((s)); strncat(str, (s), len); w = strlen((s)); w = w > len ? len : w; ifU(len<=w) { str[len-1] = 0; return w; } len -= w; str += w; fw+=w; } while(0)
usize fw ;
usize w = strlen ( strncpy ( str , matched ? fmts [ 0 ]
: DFLAGSET ( flags , FAILURES ) ? fmts [ 4 ]
: " " , len ) ) ; if ( len < = w | | w = = 0 ) { str [ len - 1 ] = 0 ; return 0 ; } len - = w ; str + = w ; fw = w ;
if ( DFLAGSET ( flags , NUMBER ) ) ADDSTR ( fmts [ 1 ] ) ;
if ( DFLAGSET ( flags , SLICE ) ) ADDSTR ( fmts [ 2 ] ) ;
if ( DFLAGSET ( flags , LENGTH ) ) ADDSTR ( fmts [ 3 ] ) ;
ADDSTR ( " \n " ) ;
TRACE ( " Output string written: %lu, (rlen %lu) " , fw , len ) ;
return fw ;
# undef ADDSTR
}
# ifdef _DEBUG_USE_UNSAFE_DISPLAY_FMT_ARGS
# define _display_get_fmt(...) _display_get_fmt0(__VA_ARGS__)
# else
# define _display_get_fmt(...) _display_get_fmt_n(__VA_ARGS__)
# endif
static void _display_normal ( FILE * output , dispin_t * pIN input , dispflags_t flags )
{
bool matched = input - > matched ;
slice_t cslice = input - > cslice ;
const void * base = input - > base ;
usize i = input - > index ;
# ifdef _DEBUG_MANCALC_OFFSETS
struct {
usize start , end ;
} pos = {
. start = AS ( cslice . ptr - base , usize ) ,
} ;
pos . end = pos . start + cslice . len ;
# else
let pos = slice_sted ( ( slice_t [ ] ) { SLICE ( AS ( cslice . ptr - base , usize ) , cslice . len ) } ) ;
# endif
INFO ( " Mapped region slice for %lu, pre-transform (at %p): " SLICE_FORMAT " , len: %lu " , i + 1 , base , SLICE_FORMAT_ARGS ( & cslice ) , cslice . len ) ;
// --- get format string from `flags` ---
char fmt [ 200 ] ; //TODO: Find the realistic max for this.
usize fmtw = _display_get_fmt ( flags , matched , sizeof ( fmt ) - 1 , fmt ) ;
assert ( fmtw + 1 < sizeof ( fmt ) ) ;
fmt [ fmtw + 1 ] = 0 ;
// ---
TRACE ( " Format string get: `%s' (%lu) " , fmt , fmtw ) ;
fprintf ( output , fmt , i + 1 , pos . start , pos . end , pos . end - pos . start ) ;
}
void display_result ( FILE * output , dispin_t input , dispkind_t how , dispflags_t flags )
{
flags = flags ? : DISPLAY_FLAGS_DEFAULT ;
output = output ? : stdout ;
TRACE ( " Outputting as %d to %p with flags 0x%08lx " , ( int ) how , output , ( usize ) flags ) ;
switch ( how )
{
case DISPK_NORMAL :
_display_normal ( output , & input , flags ) ;
break ;
case DISPK_CSV :
case DISPK_BINARY :
case DISPK_INI :
TODO ( " display kind %d is unimplemented " , ( int ) how ) ;
default : COLD_EXPR ( FATAL ( " unknown display kind: %d " , ( int ) how ) ) ;
}
}