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.
naka/src/display.c

180 lines
5.8 KiB

//! 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));
}
}