// *naka* - find a file within another file
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/mman.h>
# include <time.h>
# include <ints.h>
# include <macros.h>
# include <version.h>
# include <project.h>
# include <tests.h>
# include <slice.h>
# include <map.h>
# include <comp.h>
# include <display.h>
static const char * _compdate_readable ( )
{
static const time_t comp = PROG_COMPILED_TIMESTAMP ;
# define DATE_MAX 20
static _Thread_local char date [ DATE_MAX ] = { 0 } ;
debug_assert ( sizeof ( date ) = = DATE_MAX ) ;
struct tm * time = gmtime ( & comp ) ;
if ( ! date [ 0 ] ) {
usize w = strftime ( date , sizeof ( date ) , " %Y-%m-%d %H:%M:%S " , time ) ;
DBG ( ifU ( ! w | | w > = DATE_MAX ) FATAL ( " Increase DATE_MAX, %lu is too low (strftime(): %lu) " , DATE_MAX , w ) ) ;
TRACE ( " size of hr time: %lu / %lu " , w , sizeof ( date ) ) ;
}
return date ;
# undef DATE_MAX
}
static void pi_print_trace_infos ( FILE * out )
{
const char * name ;
enum trace_level cmax = trace_max_level ( ) ;
for ( int i = 0 ; i < ( int ) _TRACE_LEVEL_NUM ; i + + )
{
enum trace_level lv = ( enum trace_level ) i ;
name = trace_name_of ( lv ) ;
if ( name ) {
debug_assert ( AS ( lv , int ) < AS ( _TRACE_LEVEL_NUM , int ) & & AS ( lv , int ) > = 0 ) ;
const char * desc = lv [ _trace_level_descriptions ] ;
assert ( desc ) ;
fprintf ( out , " -> level %d: \" %s \" - %s%s \n " , i , name , desc , cmax = = lv ? " (currently set) " : " " ) ;
}
}
enum trace_level def = _TRACE_LEVEL_DEFAULT ;
name = trace_name_of ( def ) ;
debug_assert ( name ) ;
fprintf ( out , " (default level: %d, \" %s \" ) \n " , def , name ) ;
fprintf ( out , " Set `LOG_LEVEL=<name string>` to control logging verbosity level at runtime. The name string is case-sensitive. \n " ) ;
}
static void prog_info ( FILE * out )
{
fprintf ( out , PROG_NAME " v%s - " PROG_DESCRIPTION
# ifdef DEBUG
" (debug build) "
# endif
" \n written by %s with <3 (compiled at %s UTC. "
# ifdef DEBUG
" %lu uts) "
# else
" ) "
# endif
" \n license %s. \n " ,
v_ctoss ( v_rawtoc ( PROG_VERSION ) ) ,
PROG_AUTHOUR ,
_compdate_readable ( ) ,
# ifdef DEBUG
PROG_COMPILED_TIMESTAMP ,
# endif
PROG_LICENSE ) ;
fprintf ( out , " \n Logging levels: (set with env-var `%s') \n " , _TRACE_CONTROL_ENV_NAME ) ;
pi_print_trace_infos ( out ) ;
}
void usage ( FILE * out , int argc , char * * argv )
{
IGNORE ( argc ) ;
prog_info ( out ) ;
fprintf ( out , " \n Usage: %s <needle> <haystack(s)...> \n " , argv [ 0 ] ? : PROG_NAME ) ;
fprintf ( out , " Usage: %s --help \n " , argv [ 0 ] ? : PROG_NAME ) ;
}
// err: 0 for normal exit and print to stdout.
// err: nonzero for abnormal exit and print to stderr.
//
// noinline: We don't want this hoisted.
noinline noreturn void usage_then_exit ( int err , int argc , char * * argv )
{
usage ( err ? stderr : stdout , argc , argv ) ;
exit ( err ) ;
}
static int map_haystacks ( const char * const * h , map_t maps [ pOUT ] )
{
const char * path ;
usize d = 0 ;
while ( ( path = * h + + ) )
{
map_t * pOUT c = maps + + ;
INFO ( " Mapping haystack file `%s' (to map addr %p) " , path , c ) ;
debug_assert ( c ) ;
if ( ! map_handle_err ( map_file ( path , false , 0 , 0 , c ) ) ) {
//Handle unmapping previous haystacks before return
// Prep reset maps to before `c` for unwind
maps - = 1 ;
ERROR ( " Failed to map file `%s', rolling back %lu maps. (erp: %p, prev: %p) " , path , d , c , maps ) ;
while ( d - - > 0 )
ifU ( ! map_handle_err ( map_free ( * ( - - maps ) ) ) ) WARN ( " Failed to free map at %p (in %lu unwind) " , maps , d ) ;
return 0 ;
}
d + = 1 ;
}
return 1 ;
}
int main ( int argc , char * * argv )
{
if ( ! trace_init ( ) ) {
ERROR ( " Unknown trace value for %s: `%s' " , _TRACE_CONTROL_ENV_NAME , getenv ( _TRACE_CONTROL_ENV_NAME ) ? : " <null> " ) ;
}
TRACE ( " main start with %d (pn: %s, a1: %s), log trace level (ctrl envar `%s'): %d " , argc , argv [ 0 ] , argv [ 1 ] ? : " <null> " , _TRACE_CONTROL_ENV_NAME , ( int ) trace_max_level ( ) ) ;
if ( ! argv [ 1 ] )
inv_args :
usage_then_exit ( PROG_RET_ARGS , argc , argv ) ;
else if ( strcmp ( argv [ 1 ] , " --help " ) = = 0 )
usage_then_exit ( 0 , argc , argv ) ;
else if ( ! argv [ 2 ] )
goto inv_args ;
int m_rval = 0 ;
map_t needle ;
INFO ( " Mapping needle file `%s' " , argv [ 1 ] ) ;
if ( ! map_handle_err ( map_file ( argv [ 1 ] , false , 0 , 0 , & needle ) ) ) return PROG_RET_MAP_NEEDLE_FAILED ;
if ( ! map_handle_err ( map_preload ( & needle , false ) ) ) WARN ( " Failed to advise kernel about memory access: needle " ) ;
// Remove exe name from argv: now is (needle, haystacks...)
argv + = 1 ;
// Setup haystack maps
usize hsn = AS ( argc - 2 , usize ) ;
map_t haystacks [ hsn ] ;
TRACE ( " Attempting to map %lu haystacks " , hsn ) ;
if ( ! map_haystacks ( ( const char * * ) ( argv + 1 ) , haystacks ) ) {
// Unmap needle before return.
ifU ( ! map_handle_err ( map_free ( needle ) ) ) WARN ( " Failed to unmap needle before exiting after failed haystack maps " ) ;
return PROG_RET_MAP_HAYSTACK_FAILED ;
}
# ifdef _FEATURE_PARALLEL // Multi-threaded
TODO ( " Multithreaded processing currently unimplemented. " ) ;
//TODO: Setup thread-pool.
//TODO: Dispatch haystack maps to threadpool.
//TODO: Use either `cmp_find()` or `cmp_find_many()` to find the `needle` (mapped above) within those haystacks.
//TODO: Within the threadpool: output information regarding each match/nonmatch.
//TODO: Join the threadpool and consolidate results.
//TODO: Iterate through the haystack numbers match results, return the first non-match haystack number through `PROG_RET_MATCH_HAYSTACK_N_FAILED(n)` (haystack numbers are always nonzero). If all were matched, return 0
# else // Single-threaded
INFO ( " Computing result for %lu haystacks... " , hsn ) ;
comp_multires_t * full_result = comp_match_all ( & needle , hsn , haystacks , false ) ;
// Number of results actually computed by `comp_match_all()` may differ from `hsn` if `abort_on_fail` is true (currently its hardcoded to false but that may change.)
usize cres = full_result - > num_results ;
INFO ( " Computed result: Suc. Matched %lu / %lu hs. Full match: %s (first failure: %d) " , cres , hsn , full_result - > all_matched ? " yes " : " no " , full_result - > idx_first_failure ) ;
//TODO: Print out the aggregate (full_result) results (cres) in a reasonable format.
struct comp_single_result * current = full_result - > aggr ;
for ( usize i = 0 ; i < cres ; i + + )
{
slice_t cslice ;
const void * base = current - > mapping - > origin ;
bool matched = current - > matched ;
if ( ! comp_slice_of ( & needle , current + + , & cslice ) ) {
printf ( " fail: %lu \n " , i + 1 ) ;
} else {
TRACE ( " Displaying result %lu ( " SLICE_FORMAT " ) (base %p) " , i , SLICE_FORMAT_ARGS ( & cslice ) , base ) ;
display_result ( stdout , ( dispin_t ) { . base = base , . cslice = cslice , . index = i , . matched = matched } , DISPK_NORMAL , 0 ) ;
}
}
free ( full_result ) ;
# endif
for ( int i = 0 ; i < ( int ) hsn ; i + + ) {
INFO ( " Unmapping haystack haystack file `%s' (#%d) (map addr %p) " , argv [ i + 1 ] , i + 1 , haystacks + i ) ;
ifU ( ! map_handle_err ( map_free ( haystacks [ i ] ) ) ) m_rval = ! m_rval ? PROG_RET_UNMAP_HAYSTACK_N_FAILED ( i + 1 ) : m_rval ;
}
TRACE ( " Unmap haystacks ended with main rval: %d " , m_rval ) ;
INFO ( " Unmapping needle file `%s' " , * argv ) ;
ifU ( ! map_handle_err ( map_free ( needle ) ) ) m_rval = ! m_rval ? PROG_RET_UNMAP_NEEDLE_FAILED : m_rval ;
TRACE ( " main end: rval %d " , m_rval ) ;
return m_rval ;
}