You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
6.7 KiB
204 lines
6.7 KiB
// *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, "\nLogging 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, "\nUsage: %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;
|
|
}
|
|
|