#include<kherrinternal.h>
#include<assert.h>
#include<stdarg.h>
CRITICAL_SECTION cs_error;
DWORD tls_error = 0;
kherr_context * ctx_free_list = NULL;
kherr_context * ctx_root_list = NULL;
kherr_context * ctx_error_list = NULL;
kherr_event * evt_free_list = NULL;
kherr_handler_node * ctx_handlers = NULL;
khm_size n_ctx_handlers;
khm_size nc_ctx_handlers;
kherr_serial ctx_serial = 0;
#ifdef DEBUG
#define DEBUG_CONTEXT
KHMEXP void
kherr_debug_printf(wchar_t * fmt, ...)
{
va_list vl;
wchar_t buf[1024];
va_start(vl, fmt);
StringCbVPrintf(buf, sizeof(buf), fmt, vl);
OutputDebugString(buf);
va_end(vl);
}
#endif
KHMEXP void KHMAPI
kherr_add_ctx_handler(kherr_ctx_handler h,
khm_int32 filter,
kherr_serial serial)
{
khm_size idx;
assert(h);
EnterCriticalSection(&cs_error);
if( ctx_handlers == NULL) {
nc_ctx_handlers = CTX_ALLOC_INCR;
n_ctx_handlers = 0;
ctx_handlers = PMALLOC(sizeof(*ctx_handlers) * nc_ctx_handlers);
} else if (n_ctx_handlers == nc_ctx_handlers) {
khm_size new_nc;
kherr_handler_node * new_ctxs;
new_nc = nc_ctx_handlers + CTX_ALLOC_INCR;
new_ctxs = PMALLOC(sizeof(*new_ctxs) * new_nc);
memcpy(new_ctxs, ctx_handlers, n_ctx_handlers * sizeof(*new_ctxs));
PFREE(ctx_handlers);
ctx_handlers = new_ctxs;
nc_ctx_handlers = new_nc;
}
if (filter == 0)
filter = KHERR_CTX_BEGIN |
KHERR_CTX_DESCRIBE |
KHERR_CTX_END |
KHERR_CTX_ERROR |
KHERR_CTX_NEWCHILD |
KHERR_CTX_FOLDCHILD;
if (filter & KHERR_CTX_EVTCOMMIT) {
idx = 0;
memmove(&ctx_handlers[1], &ctx_handlers[0],
n_ctx_handlers * sizeof(ctx_handlers[0]));
} else {
idx = n_ctx_handlers;
}
ctx_handlers[idx].h = h;
ctx_handlers[idx].filter = filter;
ctx_handlers[idx].serial = serial;
n_ctx_handlers++;
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_remove_ctx_handler(kherr_ctx_handler h,
kherr_serial serial)
{
khm_size i;
EnterCriticalSection(&cs_error);
for (i=0 ; i < n_ctx_handlers; i++) {
if (ctx_handlers[i].h == h &&
ctx_handlers[i].serial == serial) {
break;
}
}
if ( i < n_ctx_handlers ) {
n_ctx_handlers --;
for (; i < n_ctx_handlers; i++) {
ctx_handlers[i] = ctx_handlers[i + 1];
}
}
LeaveCriticalSection(&cs_error);
}
static void
notify_ctx_event(enum kherr_ctx_event e, kherr_context * c)
{
khm_size i;
kherr_ctx_handler h;
for (i=0; i<n_ctx_handlers; i++) {
if (ctx_handlers[i].h && (ctx_handlers[i].filter & e) &&
(ctx_handlers[i].serial == 0 ||
ctx_handlers[i].serial == c->serial)) {
if (IsBadCodePtr((FARPROC) ctx_handlers[i].h)) {
ctx_handlers[i].h = NULL;
} else {
h = ctx_handlers[i].h;
(*h)(e,c);
if (h != ctx_handlers[i].h)
i--;
}
} else if (e == KHERR_CTX_EVTCOMMIT &&
!(ctx_handlers[i].filter & KHERR_CTX_EVTCOMMIT)) {
break;
}
}
}
void
attach_this_thread(void)
{
kherr_thread * t;
t = (kherr_thread *) TlsGetValue(tls_error);
if (t)
return;
t = PMALLOC(sizeof(kherr_thread) +
sizeof(kherr_context *) * THREAD_STACK_SIZE);
t->nc_ctx = THREAD_STACK_SIZE;
t->n_ctx = 0;
t->ctx = (kherr_context **) &t[1];
TlsSetValue(tls_error, t);
}
void
detach_this_thread(void)
{
kherr_thread * t;
khm_size i;
t = (kherr_thread *) TlsGetValue(tls_error);
if (t) {
for(i=0; i < t->n_ctx; i++) {
kherr_release_context(t->ctx[i]);
}
PFREE(t);
TlsSetValue(tls_error, 0);
}
}
static kherr_context *
peek_context(void)
{
kherr_thread * t;
t = (kherr_thread *) TlsGetValue(tls_error);
if (t) {
if (t->n_ctx > 0) {
kherr_context * c;
c = t->ctx[t->n_ctx - 1];
assert(c == NULL || IS_KHERR_CTX(c));
return c;
} else {
return NULL;
}
} else
return NULL;
}
static void
push_context(kherr_context * c)
{
kherr_thread * t;
t = (kherr_thread *) TlsGetValue(tls_error);
if (!t) {
attach_this_thread();
t = (kherr_thread *) TlsGetValue(tls_error);
assert(t);
}
if (t->n_ctx == t->nc_ctx) {
khm_size nc_new;
khm_size cb_new;
kherr_thread * nt;
nc_new = t->nc_ctx + THREAD_STACK_SIZE;
cb_new = sizeof(kherr_thread) +
sizeof(kherr_context *) * nc_new;
nt = PMALLOC(cb_new);
memcpy(nt, t, sizeof(kherr_thread) +
sizeof(kherr_context *) * t->n_ctx);
nt->ctx = (kherr_context **) &nt[1];
nt->nc_ctx = nc_new;
PFREE(t);
t = nt;
TlsSetValue(tls_error, t);
}
assert(t->n_ctx < t->nc_ctx);
t->ctx[t->n_ctx++] = c;
kherr_hold_context(c);
}
static kherr_context *
pop_context(void)
{
kherr_thread * t;
kherr_context * c;
t = (kherr_thread *) TlsGetValue(tls_error);
if (t) {
if (t->n_ctx > 0) {
c = t->ctx[--(t->n_ctx)];
assert(IS_KHERR_CTX(c));
return c;
} else
return NULL;
} else {
return NULL;
}
}
static kherr_event *
get_empty_event(void)
{
kherr_event * e;
EnterCriticalSection(&cs_error);
if(evt_free_list) {
LPOP(&evt_free_list, &e);
} else {
e = PMALLOC(sizeof(*e));
}
LeaveCriticalSection(&cs_error);
ZeroMemory(e, sizeof(*e));
e->severity = KHERR_NONE;
e->magic = KHERR_EVENT_MAGIC;
return e;
}
static void
free_event_params(kherr_event * e)
{
assert(IS_KHERR_EVENT(e));
if(parm_type(e->p1) == KEPT_STRINGT) {
assert((void *) parm_data(e->p1));
PFREE((void*) parm_data(e->p1));
}
ZeroMemory(&e->p1, sizeof(e->p1));
if(parm_type(e->p2) == KEPT_STRINGT) {
assert((void *) parm_data(e->p2));
PFREE((void*) parm_data(e->p2));
}
ZeroMemory(&e->p2, sizeof(e->p2));
if(parm_type(e->p3) == KEPT_STRINGT) {
assert((void *) parm_data(e->p3));
PFREE((void*) parm_data(e->p3));
}
ZeroMemory(&e->p3, sizeof(e->p3));
if(parm_type(e->p4) == KEPT_STRINGT) {
assert((void *) parm_data(e->p4));
PFREE((void*) parm_data(e->p4));
}
ZeroMemory(&e->p4, sizeof(e->p4));
}
static void
free_event(kherr_event * e)
{
EnterCriticalSection(&cs_error);
assert(IS_KHERR_EVENT(e));
#ifdef DEBUG
assert(LNEXT(e) == NULL);
assert(LPREV(e) == NULL);
#endif
#ifdef DEBUG_CONTEXT
if (!(e->flags & KHERR_RF_STR_RESOLVED))
resolve_event_strings(e);
if (e->short_desc && e->long_desc) {
kherr_debug_printf(L"E:%s (%s)\n", e->short_desc, e->long_desc);
} else if (e->short_desc) {
kherr_debug_printf(L"E:%s\n", e->short_desc);
} else if (e->long_desc) {
kherr_debug_printf(L"E:%s\n", e->long_desc);
} else {
kherr_debug_printf(L"E:[No description for event 0x%p]\n", e);
}
if (e->suggestion)
kherr_debug_printf(L" Suggest:[%s]\n", e->suggestion);
if (e->facility)
kherr_debug_printf(L" Facility:[%s]\n", e->facility);
#endif
if(e->flags & KHERR_RF_FREE_SHORT_DESC) {
assert(e->short_desc);
PFREE((void *) e->short_desc);
}
if(e->flags & KHERR_RF_FREE_LONG_DESC) {
assert(e->long_desc);
PFREE((void *) e->long_desc);
}
if(e->flags & KHERR_RF_FREE_SUGGEST) {
assert(e->suggestion);
PFREE((void *) e->suggestion);
}
free_event_params(e);
ZeroMemory(e, sizeof(e));
LPUSH(&evt_free_list, e);
LeaveCriticalSection(&cs_error);
}
static kherr_context *
get_empty_context(void)
{
kherr_context * c;
EnterCriticalSection(&cs_error);
if(ctx_free_list)
LPOP(&ctx_free_list, &c);
else {
c = PMALLOC(sizeof(kherr_context));
}
ZeroMemory(c,sizeof(*c));
c->severity = KHERR_NONE;
c->flags = KHERR_CF_UNBOUND;
c->magic = KHERR_CONTEXT_MAGIC;
c->serial = ++ctx_serial;
LPUSH(&ctx_root_list, c);
LeaveCriticalSection(&cs_error);
return c;
}
static void
free_context(kherr_context * c)
{
kherr_context * ch;
kherr_event * e;
assert(IS_KHERR_CTX(c));
#ifdef DEBUG_CONTEXT
kherr_debug_printf(L"Freeing context 0x%x\n", c);
#endif
EnterCriticalSection(&cs_error);
if (c->desc_event)
free_event(c->desc_event);
c->desc_event = NULL;
TPOPCHILD(c, &ch);
while(ch) {
free_context(ch);
TPOPCHILD(c, &ch);
}
QGET(c, &e);
while(e) {
free_event(e);
QGET(c, &e);
}
c->serial = 0;
LPUSH(&ctx_free_list,c);
LeaveCriticalSection(&cs_error);
#ifdef DEBUG_CONTEXT
kherr_debug_printf(L"Done with context 0x%x\n", c);
#endif
}
static void
add_event(kherr_context * c, kherr_event * e)
{
kherr_event * te;
assert(IS_KHERR_CTX(c));
assert(IS_KHERR_EVENT(e));
#ifdef DEBUG
assert(LPREV(e) == NULL && LNEXT(e) == NULL);
#endif
EnterCriticalSection(&cs_error);
te = QBOTTOM(c);
if (te && !(te->flags & KHERR_RF_COMMIT)) {
notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);
te->flags |= KHERR_RF_COMMIT;
}
QPUT(c,e);
if(c->severity > e->severity) {
if (e->severity <= KHERR_ERROR)
notify_ctx_event(KHERR_CTX_ERROR, c);
c->severity = e->severity;
c->err_event = e;
c->flags &= ~KHERR_CF_DIRTY;
}
LeaveCriticalSection(&cs_error);
}
static void
pick_err_event(kherr_context * c)
{
kherr_event * e;
kherr_event * ce = NULL;
enum kherr_severity s;
s = KHERR_RESERVED_BANK;
EnterCriticalSection(&cs_error);
e = QTOP(c);
while(e) {
if(!(e->flags & KHERR_RF_INERT) &&
s >= e->severity) {
ce = e;
s = e->severity;
}
e = QNEXT(e);
}
if(ce) {
c->err_event = ce;
c->severity = ce->severity;
} else {
c->err_event = NULL;
c->severity = KHERR_NONE;
}
c->flags &= ~KHERR_CF_DIRTY;
LeaveCriticalSection(&cs_error);
}
static void
va_arg_from_param(va_list * parm, kherr_param p)
{
int t = parm_type(p);
khm_int32 * pi;
wchar_t ** ps;
void ** pptr;
khm_int64 * pli;
if (t != KEPT_NONE) {
switch (t) {
case KEPT_INT32:
case KEPT_UINT32:
pi = (khm_int32 *)(*parm);
va_arg(*parm, khm_int32);
*pi = (khm_int32) parm_data(p);
break;
case KEPT_STRINGC:
case KEPT_STRINGT:
ps = (wchar_t **) (*parm);
va_arg(*parm, wchar_t *);
*ps = (wchar_t *) parm_data(p);
break;
case KEPT_PTR:
pptr = (void **) (*parm);
va_arg(*parm, void *);
*pptr = (void *) parm_data(p);
break;
case KEPT_INT64:
case KEPT_UINT64:
pli = (khm_int64 *) (*parm);
va_arg(*parm, khm_int64);
*pli = (khm_int64) parm_data(p);
break;
#ifdef DEBUG
default:
assert(FALSE);
#endif
}
}
}
static void
va_args_from_event(va_list args, kherr_event * e, khm_size cb)
{
ZeroMemory(args, cb);
va_arg_from_param(&args, e->p1);
va_arg_from_param(&args, e->p2);
va_arg_from_param(&args, e->p3);
va_arg_from_param(&args, e->p4);
}
static void
resolve_string_resource(kherr_event * e,
const wchar_t ** str,
va_list vl,
khm_int32 if_flag,
khm_int32 or_flag)
{
wchar_t tfmt[KHERR_MAXCCH_STRING];
wchar_t tbuf[KHERR_MAXCCH_STRING];
size_t chars = 0;
size_t bytes = 0;
if(e->flags & if_flag) {
if(e->h_module != NULL)
chars = LoadString(e->h_module, (UINT)(INT_PTR) *str,
tfmt, ARRAYLENGTH(tbuf));
if(e->h_module == NULL || chars == 0)
*str = NULL;
else {
wchar_t * s;
chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING, tfmt,
0, 0, tbuf, ARRAYLENGTH(tbuf), &vl);
if (chars == 0) {
*str = NULL;
} else {
bytes = (chars + 1) * sizeof(wchar_t);
s = PMALLOC(bytes);
assert(s);
StringCbCopy(s, bytes, tbuf);
*str = s;
e->flags |= or_flag;
}
}
e->flags &= ~if_flag;
}
}
static void
resolve_msg_resource(kherr_event * e,
const wchar_t ** str,
va_list vl,
khm_int32 if_flag,
khm_int32 or_flag)
{
wchar_t tbuf[KHERR_MAXCCH_STRING];
size_t chars = 0;
size_t bytes = 0;
if(e->flags & if_flag) {
if(e->h_module != NULL) {
chars = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
(LPCVOID) e->h_module,
(DWORD)(DWORD_PTR) *str,
0,
tbuf,
ARRAYLENGTH(tbuf),
&vl);
}
if(e->h_module == NULL || chars == 0) {
*str = NULL;
} else {
wchar_t * s;
if (tbuf[chars-1] == L'\n')
tbuf[--chars] = L'\0';
if (tbuf[chars-1] == L'\r')
tbuf[--chars] = L'\0';
bytes = (chars + 1) * sizeof(wchar_t);
s = PMALLOC(bytes);
assert(s);
StringCbCopy(s, bytes, tbuf);
*str = s;
e->flags |= or_flag;
}
e->flags &= ~if_flag;
}
}
static void
resolve_string(kherr_event * e,
const wchar_t ** str,
va_list vl,
khm_int32 mask,
khm_int32 free_if,
khm_int32 or_flag)
{
wchar_t tbuf[KHERR_MAXCCH_STRING];
size_t chars;
size_t bytes;
if (((e->flags & mask) == 0 ||
(e->flags & mask) == free_if) &&
*str != NULL) {
chars = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
(LPCVOID) *str,
0,
0,
tbuf,
ARRAYLENGTH(tbuf),
&vl);
if ((e->flags & mask) == free_if) {
PFREE((void *) *str);
}
e->flags &= ~mask;
if (chars == 0) {
*str = 0;
} else {
wchar_t * s;
bytes = (chars + 1) * sizeof(wchar_t);
s = PMALLOC(bytes);
assert(s);
StringCbCopy(s, bytes, tbuf);
*str = s;
e->flags |= or_flag;
}
}
}
void
resolve_event_strings(kherr_event * e)
{
DWORD_PTR args[8];
va_list vl = (va_list) args;
va_args_from_event(vl, e, sizeof(args));
resolve_string(e, &e->short_desc, vl,
KHERR_RFMASK_SHORT_DESC,
KHERR_RF_FREE_SHORT_DESC,
KHERR_RF_FREE_SHORT_DESC);
resolve_string(e, &e->long_desc, vl,
KHERR_RFMASK_LONG_DESC,
KHERR_RF_FREE_LONG_DESC,
KHERR_RF_FREE_LONG_DESC);
resolve_string(e, &e->suggestion, vl,
KHERR_RFMASK_SUGGEST,
KHERR_RF_FREE_SUGGEST,
KHERR_RF_FREE_SUGGEST);
resolve_string_resource(e, &e->short_desc, vl,
KHERR_RF_RES_SHORT_DESC,
KHERR_RF_FREE_SHORT_DESC);
resolve_string_resource(e, &e->long_desc, vl,
KHERR_RF_RES_LONG_DESC,
KHERR_RF_FREE_LONG_DESC);
resolve_string_resource(e, &e->suggestion, vl,
KHERR_RF_RES_SUGGEST,
KHERR_RF_FREE_SUGGEST);
resolve_msg_resource(e, &e->short_desc, vl,
KHERR_RF_MSG_SHORT_DESC,
KHERR_RF_FREE_SHORT_DESC);
resolve_msg_resource(e, &e->long_desc, vl,
KHERR_RF_MSG_LONG_DESC,
KHERR_RF_FREE_LONG_DESC);
resolve_msg_resource(e, &e->suggestion, vl,
KHERR_RF_MSG_SUGGEST,
KHERR_RF_FREE_SUGGEST);
free_event_params(e);
e->h_module = NULL;
e->flags |= KHERR_RF_STR_RESOLVED;
}
KHMEXP void KHMAPI
kherr_evaluate_event(kherr_event * e)
{
if (!IS_KHERR_EVENT(e))
return;
EnterCriticalSection(&cs_error);
resolve_event_strings(e);
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_evaluate_last_event(void)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(!IS_KHERR_EVENT(e))
goto _exit;
resolve_event_strings(e);
_exit:
LeaveCriticalSection(&cs_error);
}
KHMEXP kherr_event * __cdecl
kherr_reportf(const wchar_t * long_desc_fmt, ...)
{
va_list vl;
wchar_t buf[1024];
kherr_event * e;
va_start(vl, long_desc_fmt);
StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);
#ifdef DEBUG
OutputDebugString(buf);
#endif
va_end(vl);
e = kherr_report(KHERR_DEBUG_1,
NULL, NULL, NULL, buf, NULL, 0,
KHERR_SUGGEST_NONE, _vnull(), _vnull(), _vnull(), _vnull(),
KHERR_RF_CSTR_LONG_DESC
#ifdef _WIN32
,NULL
#endif
);
if (IS_KHERR_EVENT(e)) {
kherr_evaluate_event(e);
}
return e;
}
KHMEXP kherr_event * __cdecl
kherr_reportf_ex(enum kherr_severity severity,
const wchar_t * facility,
khm_int32 facility_id,
#ifdef _WIN32
HMODULE hModule,
#endif
const wchar_t * long_desc_fmt, ...)
{
va_list vl;
wchar_t buf[1024];
kherr_event * e;
va_start(vl, long_desc_fmt);
StringCbVPrintf(buf, sizeof(buf), long_desc_fmt, vl);
#ifdef DEBUG
OutputDebugString(buf);
#endif
va_end(vl);
e = kherr_report(severity, NULL, facility, NULL, buf, NULL, facility_id,
KHERR_SUGGEST_NONE,
_vnull(),
_vnull(),
_vnull(),
_vnull(), KHERR_RF_CSTR_LONG_DESC
#ifdef _WIN32
,hModule
#endif
);
if (IS_KHERR_EVENT(e)) {
kherr_evaluate_event(e);
}
return e;
}
KHMEXP kherr_event * KHMAPI
kherr_report(enum kherr_severity severity,
const wchar_t * short_desc,
const wchar_t * facility,
const wchar_t * location,
const wchar_t * long_desc,
const wchar_t * suggestion,
khm_int32 facility_id,
enum kherr_suggestion suggestion_id,
kherr_param p1,
kherr_param p2,
kherr_param p3,
kherr_param p4,
khm_int32 flags
#ifdef _WIN32
,HMODULE h_module
#endif
)
{
kherr_context * c;
kherr_event * e;
khm_boolean invalid = FALSE;
if (!IS_POW2(flags & KHERR_RFMASK_SHORT_DESC) ||
!IS_POW2(flags & KHERR_RFMASK_LONG_DESC) ||
!IS_POW2(flags & KHERR_RFMASK_SUGGEST))
invalid = TRUE;
e = get_empty_event();
e->thread_id = GetCurrentThreadId();
e->time_ticks = GetTickCount();
GetSystemTimeAsFileTime(&e->time_ft);
e->severity = severity;
e->short_desc = short_desc;
e->facility = facility;
e->location = location;
e->long_desc = long_desc;
e->suggestion = suggestion;
e->facility_id = facility_id;
e->suggestion_id = suggestion_id;
e->p1 = p1;
e->p2 = p2;
e->p3 = p3;
e->p4 = p4;
e->flags = flags;
#ifdef _WIN32
e->h_module = h_module;
#endif
EnterCriticalSection(&cs_error);
c = peek_context();
if(!c || invalid) {
free_event(e);
e = NULL;
} else {
add_event(c,e);
}
LeaveCriticalSection(&cs_error);
return e;
}
KHMEXP void KHMAPI
kherr_suggest(wchar_t * suggestion,
enum kherr_suggestion suggestion_id,
khm_int32 flags)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
if (flags != KHERR_RF_CSTR_SUGGEST &&
flags != KHERR_RF_RES_SUGGEST &&
flags != KHERR_RF_MSG_SUGGEST &&
flags != KHERR_RF_FREE_SUGGEST)
return;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(!IS_KHERR_EVENT(e))
goto _exit;
if ((flags == KHERR_RF_RES_SUGGEST ||
flags == KHERR_RF_MSG_SUGGEST) &&
(e->flags & KHERR_RF_STR_RESOLVED))
goto _exit;
e->suggestion = suggestion;
e->suggestion_id = suggestion_id;
e->flags |= flags;
_exit:
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_location(wchar_t * location)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(!IS_KHERR_EVENT(e))
goto _exit;
e->location = location;
_exit:
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_facility(wchar_t * facility,
khm_int32 facility_id)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(!IS_KHERR_EVENT(e))
goto _exit;
e->facility = facility;
e->facility_id = facility_id;
_exit:
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_set_desc_event(void)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(!IS_KHERR_EVENT(e) || c->desc_event)
goto _exit;
QDEL(c,e);
c->desc_event = e;
e->severity = KHERR_NONE;
resolve_event_strings(e);
notify_ctx_event(KHERR_CTX_DESCRIBE, c);
_exit:
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_del_last_event(void)
{
kherr_context * c;
kherr_event * e;
DWORD tid;
c = peek_context();
if(!IS_KHERR_CTX(c))
return;
tid = GetCurrentThreadId();
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
while (e != NULL && e->thread_id != tid)
e = QPREV(e);
if(IS_KHERR_EVENT(e)) {
QDEL(c, e);
if(c->err_event == e) {
pick_err_event(c);
}
free_event(e);
}
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_push_context(kherr_context * c)
{
kherr_context * p = NULL;
int new_context = FALSE;
if (!IS_KHERR_CTX(c))
return;
EnterCriticalSection(&cs_error);
p = peek_context();
if(IS_KHERR_CTX(p) && (c->flags & KHERR_CF_UNBOUND)) {
LDELETE(&ctx_root_list, c);
TADDCHILD(p,c);
c->flags &= ~KHERR_CF_UNBOUND;
kherr_hold_context(p);
new_context = TRUE;
}
push_context(c);
if (new_context && IS_KHERR_CTX(p)) {
notify_ctx_event(KHERR_CTX_BEGIN, c);
notify_ctx_event(KHERR_CTX_NEWCHILD, p);
}
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_push_new_context(khm_int32 flags)
{
kherr_context * p = NULL;
kherr_context * c;
flags &= KHERR_CFMASK_INITIAL;
EnterCriticalSection(&cs_error);
p = peek_context();
c = get_empty_context();
if(IS_KHERR_CTX(p)) {
LDELETE(&ctx_root_list, c);
TADDCHILD(p,c);
c->flags &= ~KHERR_CF_UNBOUND;
kherr_hold_context(p);
}
c->flags |= flags;
push_context(c);
notify_ctx_event(KHERR_CTX_BEGIN, c);
if (IS_KHERR_CTX(p)) {
notify_ctx_event(KHERR_CTX_NEWCHILD, p);
}
LeaveCriticalSection(&cs_error);
}
static kherr_param
dup_parm(kherr_param p)
{
if(parm_type(p) == KEPT_STRINGT) {
wchar_t * d = PWCSDUP((wchar_t *)parm_data(p));
return kherr_val(KEPT_STRINGT, (khm_ui_8) d);
} else
return p;
}
static kherr_event *
fold_context(kherr_context * c)
{
kherr_event * e;
kherr_event * g;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
if(!c->err_event || (c->flags & KHERR_CF_DIRTY)) {
pick_err_event(c);
}
if(c->err_event) {
g = c->err_event;
e = get_empty_event();
*e = *g;
g->short_desc = NULL;
g->long_desc = NULL;
g->suggestion = NULL;
g->flags &=
~(KHERR_RF_FREE_SHORT_DESC |
KHERR_RF_FREE_LONG_DESC |
KHERR_RF_FREE_SUGGEST);
LINIT(e);
e->p1 = dup_parm(g->p1);
e->p2 = dup_parm(g->p2);
e->p3 = dup_parm(g->p3);
e->p4 = dup_parm(g->p4);
} else {
e = c->desc_event;
c->desc_event = NULL;
}
if (IS_KHERR_EVENT(e))
e->flags |= KHERR_RF_CONTEXT_FOLD;
LeaveCriticalSection(&cs_error);
return e;
}
KHMEXP void KHMAPI
kherr_hold_context(kherr_context * c)
{
if(!IS_KHERR_CTX(c))
return;
EnterCriticalSection(&cs_error);
c->refcount++;
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_release_context(kherr_context * c)
{
if (!IS_KHERR_CTX(c))
return;
EnterCriticalSection(&cs_error);
c->refcount--;
if (c->refcount == 0) {
kherr_event * e;
kherr_context * p;
e = QBOTTOM(c);
if (IS_KHERR_EVENT(e) && !(e->flags & KHERR_RF_COMMIT)) {
notify_ctx_event(KHERR_CTX_EVTCOMMIT, c);
e->flags |= KHERR_RF_COMMIT;
}
notify_ctx_event(KHERR_CTX_END, c);
p = TPARENT(c);
if (IS_KHERR_CTX(p)) {
e = fold_context(c);
if (e)
add_event(p, e);
TDELCHILD(p, c);
notify_ctx_event(KHERR_CTX_FOLDCHILD, p);
kherr_release_context(p);
} else {
LDELETE(&ctx_root_list, c);
}
free_context(c);
}
LeaveCriticalSection(&cs_error);
}
KHMEXP void KHMAPI
kherr_pop_context(void)
{
kherr_context * c;
EnterCriticalSection(&cs_error);
c = pop_context();
if(IS_KHERR_CTX(c)) {
kherr_release_context(c);
}
LeaveCriticalSection(&cs_error);
}
KHMEXP kherr_context * KHMAPI
kherr_peek_context(void)
{
kherr_context * c;
c = peek_context();
if (IS_KHERR_CTX(c))
kherr_hold_context(c);
return c;
}
KHMEXP khm_boolean KHMAPI
kherr_is_error(void)
{
kherr_context * c = peek_context();
return kherr_is_error_i(c);
}
KHMEXP khm_boolean KHMAPI
kherr_is_error_i(kherr_context * c)
{
if(IS_KHERR_CTX(c) && c->severity <= KHERR_ERROR)
return TRUE;
else
return FALSE;
}
KHMEXP void KHMAPI
kherr_clear_error(void)
{
kherr_context * c = peek_context();
if (IS_KHERR_CTX(c))
kherr_clear_error_i(c);
}
KHMEXP void KHMAPI
kherr_clear_error_i(kherr_context * c)
{
kherr_event * e;
if (IS_KHERR_CTX(c)) {
EnterCriticalSection(&cs_error);
e = QTOP(c);
while(e) {
assert(IS_KHERR_EVENT(e));
e->flags |= KHERR_RF_INERT;
e = QNEXT(e);
}
c->severity = KHERR_NONE;
c->err_event = NULL;
c->flags &= ~KHERR_CF_DIRTY;
LeaveCriticalSection(&cs_error);
}
}
#define CTX_USES_OWN_PROGRESS(c) ((c)->progress_num != 0 || (c)->progress_denom != 0 || ((c)->flags & KHERR_CF_OWN_PROGRESS))
KHMEXP void KHMAPI
kherr_set_progress(khm_ui_4 num, khm_ui_4 denom)
{
kherr_context * c = peek_context();
if(IS_KHERR_CTX(c)) {
EnterCriticalSection(&cs_error);
if (num > denom)
num = denom;
if (c->progress_denom != denom ||
c->progress_num != denom) {
kherr_context * p;
c->progress_denom = denom;
c->progress_num = num;
notify_ctx_event(KHERR_CTX_PROGRESS, c);
for (p = TPARENT(c);
IS_KHERR_CTX(p) && !CTX_USES_OWN_PROGRESS(p);
p = TPARENT(p)) {
notify_ctx_event(KHERR_CTX_PROGRESS, p);
}
}
LeaveCriticalSection(&cs_error);
}
}
KHMEXP void KHMAPI
kherr_get_progress(khm_ui_4 * num, khm_ui_4 * denom)
{
kherr_context * c = peek_context();
kherr_get_progress_i(c,num,denom);
}
static void
get_progress(kherr_context * c, khm_ui_4 * pnum, khm_ui_4 * pdenom)
{
if (CTX_USES_OWN_PROGRESS(c)) {
*pnum = c->progress_num;
*pdenom = c->progress_denom;
} else {
khm_ui_4 num = 0;
khm_ui_4 denom = 0;
kherr_context * cc;
for (cc = TFIRSTCHILD(c);
cc;
cc = LNEXT(cc)) {
khm_ui_4 cnum, cdenom;
assert(IS_KHERR_CTX(cc));
get_progress(cc, &cnum, &cdenom);
if (cdenom == 0) {
cnum = 0;
} else {
if (cnum > cdenom)
cnum = cdenom;
if (cdenom != 256) {
cnum = (cnum * 256) / cdenom;
cdenom = 256;
}
}
num += cnum;
denom += cdenom;
}
*pnum = num;
*pdenom = denom;
}
}
KHMEXP void KHMAPI
kherr_get_progress_i(kherr_context * c,
khm_ui_4 * num,
khm_ui_4 * denom)
{
if (num == NULL || denom == NULL)
return;
if(IS_KHERR_CTX(c)) {
EnterCriticalSection(&cs_error);
get_progress(c, num, denom);
LeaveCriticalSection(&cs_error);
} else {
*num = 0;
*denom = 0;
}
}
KHMEXP kherr_event * KHMAPI
kherr_get_first_event(kherr_context * c)
{
kherr_event * e;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
e = QTOP(c);
LeaveCriticalSection(&cs_error);
assert(e == NULL || IS_KHERR_EVENT(e));
return e;
}
KHMEXP kherr_event * KHMAPI
kherr_get_next_event(kherr_event * e)
{
kherr_event * ee;
if (!IS_KHERR_EVENT(e))
return NULL;
EnterCriticalSection(&cs_error);
ee = QNEXT(e);
LeaveCriticalSection(&cs_error);
assert(ee == NULL || IS_KHERR_EVENT(ee));
return ee;
}
KHMEXP kherr_event * KHMAPI
kherr_get_prev_event(kherr_event * e)
{
kherr_event * ee;
if (!IS_KHERR_EVENT(e))
return NULL;
EnterCriticalSection(&cs_error);
ee = QPREV(e);
LeaveCriticalSection(&cs_error);
assert(ee == NULL || IS_KHERR_EVENT(ee));
return ee;
}
KHMEXP kherr_event * KHMAPI
kherr_get_last_event(kherr_context * c)
{
kherr_event * e;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
e = QBOTTOM(c);
LeaveCriticalSection(&cs_error);
assert(e == NULL || IS_KHERR_EVENT(e));
return e;
}
KHMEXP kherr_context * KHMAPI
kherr_get_first_context(kherr_context * c)
{
kherr_context * cc;
if (c != NULL && !IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
if (IS_KHERR_CTX(c)) {
cc = TFIRSTCHILD(c);
if (cc)
kherr_hold_context(cc);
} else {
cc = ctx_root_list;
if (cc)
kherr_hold_context(cc);
}
LeaveCriticalSection(&cs_error);
assert(cc == NULL || IS_KHERR_CTX(cc));
return cc;
}
KHMEXP kherr_context * KHMAPI
kherr_get_next_context(kherr_context * c)
{
kherr_context * cc;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
cc = LNEXT(c);
if (cc)
kherr_hold_context(cc);
LeaveCriticalSection(&cs_error);
assert(cc == NULL || IS_KHERR_CTX(cc));
return cc;
}
KHMEXP kherr_event * KHMAPI
kherr_get_err_event(kherr_context * c)
{
kherr_event * e;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
if(!c->err_event) {
pick_err_event(c);
}
e = c->err_event;
LeaveCriticalSection(&cs_error);
assert(e == NULL || IS_KHERR_EVENT(e));
return e;
}
KHMEXP kherr_event * KHMAPI
kherr_get_desc_event(kherr_context * c)
{
kherr_event * e;
if (!IS_KHERR_CTX(c))
return NULL;
EnterCriticalSection(&cs_error);
e = c->desc_event;
LeaveCriticalSection(&cs_error);
assert(e == NULL || IS_KHERR_EVENT(e));
return e;
}
KHMEXP kherr_param
kherr_dup_string(const wchar_t * s)
{
wchar_t * dest;
size_t cb_s;
if (s == NULL)
return _vnull();
if (FAILED(StringCbLength(s, KHERR_MAXCB_STRING, &cb_s)))
cb_s = KHERR_MAXCB_STRING;
else
cb_s += sizeof(wchar_t);
dest = PMALLOC(cb_s);
assert(dest != NULL);
dest[0] = L'\0';
StringCbCopy(dest, cb_s, s);
return _tstr(dest);
}