text:
    const REBOL_HOST_LIB *Host_Lib = nullptr;
    EXTERN_C REBOL_HOST_LIB Host_Lib_Init;
#endif

// !!! Review how much checking one wants to do when calling API routines,
// and what the balance should be of debug vs. release.  Right now, this helps
// in particular notice if the core tries to use an API function before the
// proper moment in the boot.
//
inline static void Enter_Api(void) {
    if (not Host_Lib)
        panic ("rebStartup() not called before API call");
}

//=//// SERIES-BACKED ALLOCATORS //////////////////////////////////////////=//
//
// These are replacements for malloc(), realloc(), and free() which use a
// byte-sized REBSER as the backing store for the data.
//
// One benefit of using a series is that it offers more options for automatic
// memory management (such as being freed in case of a fail(), vs. leaked as
// a malloc() would, or perhaps being GC'd when a particular FRAME! ends).
//
// It also has the benefit of helping interface with client code that has
// been stylized to use malloc()-ish hooks to produce data, when the eventual
// target of that data is a Rebol series.  It does this without exposing
// REBSER* internals to the external API, by allowing one to "rebRepossess()"
// the underlying series as a BINARY! REBVAL*.
//

//
//  rebMalloc: RL_API
//
// * Unlike plain malloc(), this will fail() instead of return null if an
//   allocation cannot be fulfilled.
//
// * Like plain malloc(), if size is zero, the implementation just has to
//   return something that free() will take.  A backing series is added in
//   this case vs. returning null, in order to avoid null handling in other
//   routines (e.g. rebRepossess() or handle lifetime control functions).
//
// * Because of the above points, null is *never* returned.
//
// * It tries to be like malloc() by giving back a pointer "suitably aligned
//   for the size of any fundamental type".  See notes on ALIGN_SIZE.
//
// !!! rebAlignedMalloc() could exist to take an alignment, which could save
// on wasted bytes when ALIGN_SIZE > sizeof(REBSER*)...or work with "weird"
// large fundamental types that need more alignment than ALIGN_SIZE.
//
void *RL_rebMalloc(size_t size)
{
    Enter_Api();

    REBSER *s = Make_Series_Core(
        ALIGN_SIZE // stores REBSER* (must be at least big enough for void*)
            + size // for the actual data capacity (may be 0...see notes)
            + 1, // for termination (even BINARY! has this, review necessity)
        sizeof(REBYTE), // rebRepossess() only creates binary series ATM
        SERIES_FLAG_DONT_RELOCATE // direct data pointer is being handed back!
    );

    REBYTE *ptr = BIN_HEAD(s) + ALIGN_SIZE;

    REBSER **ps = (cast(REBSER**, ptr) - 1);
    *ps = s; // save self in bytes *right before* data
    POISON_MEMORY(ps, sizeof(REBSER*)); // let ASAN catch underruns

    // !!! The data is uninitialized, and if it is turned into a BINARY! via
    // rebRepossess() before all bytes are assigned initialized, it could be
    // worse than just random data...MOLDing such a binary and reading those
    // bytes could be bad (due to, for instance, "trap representations"):
    //
    // https://stackoverflow.com/a/37184840
    //
    // It may be that rebMalloc() and rebRealloc() should initialize with 0
    // in the release build to defend against that, but doing so in the debug
    // build would keep address sanitizer from noticing when memory was not
    // initialized.
    //
    TERM_BIN_LEN(s, ALIGN_SIZE + size);

    return ptr;
}

//
//  rebRealloc: RL_API
//
// * Like plain realloc(), null is legal for ptr (despite the fact that
//   rebMalloc() never returns null, this can still be useful)
//
// * Like plain realloc(), it preserves the lesser of the old data range or
//   the new data range, and memory usage drops if new_size is smaller:
//
// https://stackoverflow.com/a/9575348
//
// * Unlike plain realloc() (but like rebMalloc()), this fails instead of
//   returning null, hence it is safe to say `ptr = rebRealloc(ptr, new_size)`
//
// * A 0 size is considered illegal.  This is consistent with the C11 standard
//   for realloc(), but not with malloc() or rebMalloc()...which allow it.
//
void *RL_rebRealloc(void *ptr, size_t new_size)
{
    Enter_Api();

    assert(new_size > 0); // realloc() deprecated this as of C11 DR 400

    if (not ptr) // C realloc() accepts null
        return rebMalloc(new_size);

    REBSER **ps = cast(REBSER**, ptr) - 1;
    UNPOISON_MEMORY(ps, sizeof(REBSER*)); // need to underrun to fetch `s`

    REBSER *s = *ps;

    REBCNT old_size = BIN_LEN(s) - ALIGN_SIZE;

    // !!! It's less efficient to create a new series with another call to
    // rebMalloc(), but simpler for the time being.  Switch to do this with
    // the same series node.
    //
    void *reallocated = rebMalloc(new_size);
    memcpy(reallocated, ptr, old_size < new_size ? old_size : new_size);
    Free_Unmanaged_Series(s);

    return reallocated;
}

//
//  rebFree: RL_API
//
// * As with free(), null is accepted as a no-op.
//
void RL_rebFree(void *ptr)
{
    Enter_Api();

    if (not ptr)
        return;

    REBSER **ps = cast(REBSER**, ptr) - 1;
    UNPOISON_MEMORY(ps, sizeof(REBSER*)); // need to underrun to fetch `s`

    REBSER *s = *ps;
    if (s->header.bits & NODE_FLAG_CELL) {
        rebJUMPS (
            "panic [",
                "{rebFree() mismatched with allocator!}"
                "{Did you mean to use free() instead of rebFree()?}",
            "]",
            rebEnd()
        );
    }

    assert(BYTE_SIZE(s));

    Free_Unmanaged_Series(s);
}

//
//  rebRepossess: RL_API
//
// Alternative to rebFree() is to take over the underlying series as a
// BINARY!.  The old void* should not be used after the transition, as this
// operation makes the series underlying the memory subject to relocation.
//
// If the passed in size is less than the size with which the series was
// allocated, the overage will be treated as unused series capacity.
//
// Note that all rebRepossess()'d data will be terminated by an 0x00 byte
// after the end of its capacity.
//
// !!! All bytes in the allocation are expected to be initialized by this
// point, as failure to do so will mean reads crash the interpreter.  See
// remarks in rebMalloc() about the issue, and possibly doing zero fills.
//
// !!! It might seem tempting to use (BIN_LEN(s) - ALIGN_SIZE).  However,
// some routines make allocations bigger than they ultimately need and do not
// realloc() before converting the memory to a series...rebInflate() and
// rebDeflate() do this.  So a version passing the size will be necessary,
// and since C does not have the size exposed in malloc() and you track it
// yourself, it seems fair to *always* ask the caller to pass in a size.
//
REBVAL *RL_rebRepossess(void *ptr, REBCNT size)
{
    Enter_Api();

    REBSER **ps = cast(REBSER**, ptr) - 1;
    UNPOISON_MEMORY(ps, sizeof(REBSER*)); // need to underrun to fetch `s`

    REBSER *s = *ps;
    assert(not IS_SERIES_MANAGED(s));

    if (size > BIN_LEN(s) - ALIGN_SIZE)
        fail ("Attempt to rebRepossess() more than rebMalloc() capacity");

    assert(GET_SER_FLAG(s, SERIES_FLAG_DONT_RELOCATE));
    CLEAR_SER_FLAG(s, SERIES_FLAG_DONT_RELOCATE);

    if (GET_SER_INFO(s, SERIES_INFO_HAS_DYNAMIC)) {
        //
        // Dynamic series have the concept of a "bias", which is unused
        // allocated capacity at the head of a series.  Bump the "bias" to
        // treat the embedded REBSER* (aligned to REBI64) as unused capacity.
        //
        SER_SET_BIAS(s, ALIGN_SIZE);
        s->content.dynamic.data += ALIGN_SIZE;
        s->content.dynamic.rest -= ALIGN_SIZE;
    }
    else {
        // Data is in REBSER node itself, no bias.  Just slide the bytes down.
        //
        memmove( // src overlaps destination, can't use memcpy()
            BIN_HEAD(s),
            BIN_HEAD(s) + ALIGN_SIZE,
            size
        );
    }

    TERM_BIN_LEN(s, size);
    return Init_Binary(Alloc_Value(), s);
}

//
//  Startup_Api: C
//
// RL_API routines may be used by extensions (which are invoked by a fully
// initialized Rebol core) or by normal linkage (such as from within the core
// itself).  A call to rebStartup() won't be needed in the former case.  So
// setup code that is needed to interact with the API needs to be done by the
// core independently.
//
void Startup_Api(void)
{
}

//
//  Shutdown_Api: C
//
// See remarks on Startup_Api() for the difference between this idea and
// rebShutdown.
//
void Shutdown_Api(void)
{
    assert(Host_Lib);
    Host_Lib = nullptr;
}

//
//  rebVersion: RL_API
//
// Obtain the current Rebol version information.  Takes a byte array to
// hold the version info:
//
//      vers[0]: (input) length of the expected version information
//      vers[1]: version
//      vers[2]: revision
//      vers[3]: update
//      vers[4]: system
//      vers[5]: variation
//
// !!! In the original RL_API, this function was to be called before any other
// initialization to determine version compatiblity with the caller.  With the
// massive changes in Ren-C and the lack of RL_API clients, this check is low
// priority...but something like it will be needed.
//
// This is how it was originally done:
//
//      REBYTE vers[8];
//      vers[0] = 5; // len
//      RL_Version(&vers[0]);
//
//      if (vers[1] != RL_VER or vers[2] != RL_REV)
//          rebPanic ("Incompatible reb-lib DLL");
//
void RL_rebVersion(REBYTE vers[])
{
    if (vers[5] != 5)
        panic ("rebVersion() requires 1 + 5 byte structure");

    vers[1] = REBOL_VER;
    vers[2] = REBOL_REV;
    vers[3] = REBOL_UPD;
    vers[4] = REBOL_SYS;
    vers[5] = REBOL_VAR;
}

//
//  rebStartup: RL_API
//
// This function will allocate and initialize all memory structures used by
// the REBOL interpreter. This is an extensive process that takes time.
//
// `lib` is the host lib table (OS_XXX functions) which Rebol core does not
// take for granted--and assumes a host must provide to operate.  An example
// of this would be that getting the current UTC date and time varies from OS
// to OS, so for the NOW native to be implemented it has to call something
// outside of standard C...e.g. OS_GET_TIME().  So even though NOW is in the
// core, it will be incomplete without having that function supplied.
//
// !!! Increased modularization of the core, and new approaches, are making
// this concept obsolete.  For instance, the NOW native might not even live
// in the core, but be supplied by a "Timer Extension" which is considered to
// be sandboxed and non-core enough that having platform-specific code in it
// is not a problem.  Also, hooks can be supplied in the form of natives that
// are later HIJACK'd by some hosts (see PANIC and FAIL), as a way of
// injecting richer platform-or-scenario-specific code into a more limited
// default host operation.  It is expected that the OS_XXX functions will
// eventually disappear completely.
//
void RL_rebStartup(const void *lib)
{
    if (Host_Lib)
        panic ("rebStartup() called when it's already started");

    Host_Lib = cast(const REBOL_HOST_LIB*, lib);

    if (Host_Lib->size < HOST_LIB_SIZE)
        panic ("Host-lib wrong size");

    if (((HOST_LIB_VER << 16) + HOST_LIB_SUM) != Host_Lib->ver_sum)
        panic ("Host-lib wrong version/checksum");

    Startup_Core();
}

//
//  rebInit: RL_API
//
// Initialize the REBOL interpreter with Host_Lib_Init
//
void RL_rebInit(void)
{
    rebStartup(&Host_Lib_Init);
}

//
//  rebShutdown: RL_API
//
// Shut down a Rebol interpreter initialized with rebStartup().
//
// The `clean` parameter tells whether you want Rebol to release all of its
// memory accrued since initialization.  If you pass false, then it will
// only do the minimum needed for data integrity (it assumes you are planning
// to exit the process, and hence the OS will automatically reclaim all
// memory/handles/etc.)
//
// For rigor, the debug build *always* runs a "clean" shutdown.
//
void RL_rebShutdown(REBOOL clean)
{
    Enter_Api();

    // At time of writing, nothing Shutdown_Core() does pertains to
    // committing unfinished data to disk.  So really there is
    // nothing to do in the case of an "unclean" shutdown...yet.

  #if defined(NDEBUG)
    if (not clean)
        return; // Only do the work above this line in an unclean shutdown
  #else
    UNUSED(clean);
  #endif

    // Run a clean shutdown anyway in debug builds--even if the
    // caller didn't need it--to see if it triggers any alerts.
    //
    Shutdown_Core();
}

//
//  rebRun: RL_API
//
// C variadic function which calls the evaluator on multiple pointers.
// Each pointer may either be a REBVAL* or a UTF-8 string which will be
// scanned to reflect one or more values in the sequence.
//
// All REBVAL* are spliced in inert by default, as if they were an evaluative
// product already.  Use rebEval() to "retrigger" them (which wraps them in
// a singular REBARR*, another type of detectable pointer.)
//
REBVAL *RL_rebRun(const void *p, ...)
{
    Enter_Api();

    va_list va;
    va_start(va, p);

    REBVAL *result = Alloc_Value();
    REBIXO indexor = Do_Va_Core(
        result,
        p, // opt_first (preloads value)
        &va,
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );
    va_end(va);

    if (indexor == THROWN_FLAG) {
        DECLARE_LOCAL (uncaught);
        Move_Value(uncaught, result);
        rebRelease(result); // ...or could GC after fail at some point

        fail (Error_No_Catch_For_Throw(uncaught));
    }

    if (not IS_VOID(result))
        return result;

    // To API clients, null means void.  This provides convenience for testing
    // a result (`if (val)`), doesn't require a rebRelease(), and interacts
    // well with the TRY/OPT mechanic from inside of Rebol statements.

    rebRelease(result);
    return nullptr;
}

//
//  rebElide: RL_API
//
// Variant of rebRun() which assumes you don't need the result.  This saves on
// allocating an API handle, or the caller needing to manage its lifetime.
//
void RL_rebElide(const void *p, ...)
{
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (elided);
    REBIXO indexor = Do_Va_Core(
        elided,
        p, // opt_first (preloads value)
        &va,  // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(elided));
}

//
//  rebJUMPS: RL_API [
//      #noreturn
//  ]
//
// rebJUMPS() is like rebElide, but has the noreturn attribute, so that
// compiler warnings can be enabled and checked.  You can thus use it with
// things like `rebJUMPS ("fail", ...)` or `rebJUMPS ("throw", ...)`.
//
// Capitalizing it helps draw attention, and it is suggested that a space be
// used between it and the arguments to draw further attention.
//
// !!! The name is not ideal, but other possibilites aren't great:
//
//    rebDeadEnd(...) -- doesn't sound like it should take arguments
//    rebNoReturn(...) -- whose return?
//    rebStop(...) -- STOP is rather final sounding, the code keeps going
//
void RL_rebJUMPS(const void *p, va_list *vaptr)
{
    Enter_Api();

    DECLARE_LOCAL (elided);
    REBIXO indexor = Do_Va_Core(
        elided,
        p, // opt_first (preloads value)
        vaptr, // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(elided));

    panic ("rebJUMPS() was used to run code, but it didn't FAIL/QUIT/THROW!");
}

//
//  rebRunInline: RL_API
//
// Non-variadic function which takes a single argument which must be a single
// value that is a BLOCK! or GROUP!.  The goal is that it not add an extra
// stack level the way calling DO would.  This is important for instance in
// the console, so that BACKTRACE does not look up and see a Rebol function
// like DO on the stack.
//
// !!! This may be replaceable with `rebRun(rebInline(v), END);` or something
// similar.
//
REBVAL *RL_rebRunInline(const REBVAL *array)
{
    Enter_Api();

    if (not IS_BLOCK(array) and not IS_GROUP(array))
        fail ("rebRunInline() only supports BLOCK! and GROUP!");

    DECLARE_LOCAL (group);
    Move_Value(group, array);
    VAL_SET_TYPE_BITS(group, REB_GROUP);

    return rebRun(rebEval(NAT_VALUE(eval)), group, END);
}

//
//  rebEval: RL_API
//
// When rebRun() receives a REBVAL*, the default is to assume it should be
// spliced into the input stream as if it had already been evaluated.  It's
// only segments of code supplied via UTF-8 strings, that are live and can
// execute functions.
//
// This instruction is used with rebRun() in order to mark a value as being
// evaluated.  So `rebRun(rebEval(some_word), ...)` will execute that word
// if it's bound to an ACTION! and dereference if it's a variable.
//
const void *RL_rebEval(const REBVAL *v)
{
    Enter_Api();

    if (IS_VOID(v))
        fail ("Cannot pass voids to rebEval()");

    // !!! The presence of the VALUE_FLAG_EVAL_FLIP is a pretty good
    // indication that it's an eval instruction.  So it's not necessary to
    // fill in the ->link or ->misc fields.  But if there were more
    // instructions like this, there'd probably need to be a misc->opcode or
    // something to distinguish them.
    //
    REBARR *instruction = Alloc_Singular_Array();
    RELVAL *single = ARR_SINGLE(instruction);
    Move_Value(single, v);
    SET_VAL_FLAG(single, VALUE_FLAG_EVAL_FLIP);

    // !!! The intent for the long term is that these rebEval() instructions
    // not tax the garbage collector and be freed as they are encountered
    // while traversing the va_list.  Right now an assert would trip if we
    // tried that.  It's a good assert in general, so rather than subvert it
    // the instructions are just GC managed for now.
    //
    MANAGE_ARRAY(instruction);
    return instruction;
}

//
//  rebUneval: RL_API
//
// voids are represented as null, and they are not legal to splice into
// blocks.  So the rebUneval() expression works around it by splicing in a
// GROUP!, and if it's not void then it puts an QUOTE and the value inside.
//
//    void => `()`
//    non-void => `(quote ...)`
//
// There's a parallel Rebol action! that does this called UNEVAL, which is
// for use with REDUCE and COMPOSE/ONLY.  However, rather than return REBVAL*
// directly, this acts as an "instruction" that can be passed to the rebRun()
// variadic stream.  This leaves the implementation method more open, and
// has the benefit of not requiring a rebRelease().
//
const void *RL_rebUneval(const REBVAL *v)
{
    Enter_Api();

    // !!! See notes in rebEval() about adding opcodes.  No particular need
    // for one right now, just put in the value.

    REBARR *instruction = Alloc_Singular_Array();
    RELVAL *single = ARR_SINGLE(instruction);
    if (not v)
        Init_Group(single, EMPTY_ARRAY);
    else {
        REBARR *a = Make_Array(2);
        SET_SER_INFO(a, SERIES_INFO_HOLD);
        Move_Value(Alloc_Tail_Array(a), NAT_VALUE(quote));
        Move_Value(Alloc_Tail_Array(a), v);

        Init_Group(single, a);
    }

    // !!! See notes in rebEval() about why these are currently GC'd
    //
    MANAGE_ARRAY(instruction);
    return instruction;
}

//
//  rebR: RL_API
//
// Convenience tool for making "auto-release" form of values.  They will only
// exist for one API call.  They will be automatically rebRelease()'d when
// they are seen (or even if they are not seen, if there is a failure on that
// call it will still process the va_list in order to release these handles)
//
// Though what is returned is a REBVAL*, it is returned as a const void*,
// in order to discourage using these anywhere than as an argument to a
// variadic API like rebRun().
//
const void *RL_rebR(REBVAL *v)
{
    if (not Is_Api_Value(v))
        fail ("Cannot apply rebR() to non-API value");

    REBARR *a = Singular_From_Cell(v);
    if (GET_SER_INFO(a, SERIES_INFO_API_RELEASE))
        fail ("Cannot apply rebR() more than once to the same API value");

    SET_SER_INFO(a, SERIES_INFO_API_RELEASE);
    return v;
}

//
//  rebBlank: RL_API
//
REBVAL *RL_rebBlank(void)
{
    Enter_Api();
    return Init_Blank(Alloc_Value());
}

//
//  rebLogic: RL_API
//
// !!! Uses libRed convention that it takes a long where 0 is false and all
// other values are true, for the moment.  REBOOL is standardized to only hold
// 0 or 1 inside the core, so taking a foreign REBOOL is risky and would
// require normalization anyway.
//
REBVAL *RL_rebLogic(long logic)
{
    Enter_Api();
    return Init_Logic(Alloc_Value(), did logic);
}

//
//  rebChar: RL_API
//
REBVAL *RL_rebChar(REBUNI codepoint)
{
    Enter_Api();
    return Init_Char(Alloc_Value(), codepoint);
}

//
//  rebInteger: RL_API
//
// !!! Should there be rebSigned() and rebUnsigned(), in order to catch cases
// of using out of range values?
//
REBVAL *RL_rebInteger(REBI64 i)
{
    Enter_Api();
    return Init_Integer(Alloc_Value(), i);
}

//
//  rebI: RL_API
//
// Convenience form of `rebR(rebInteger(i))`.
//
const void *RL_rebI(REBI64 i)
{
    Enter_Api();

    return rebR(rebInteger(i));
}

//
//  rebDecimal: RL_API
//
REBVAL *RL_rebDecimal(REBDEC dec)
{
    Enter_Api();
    return Init_Decimal(Alloc_Value(), dec);
}

//
//  rebTimeHMS: RL_API
//
REBVAL *RL_rebTimeHMS(
    unsigned int hour,
    unsigned int minute,
    unsigned int second
){
    Enter_Api();

    REBVAL *result = Alloc_Value();
    RESET_VAL_HEADER(result, REB_TIME);
    VAL_NANO(result) = SECS_TO_NANO(hour * 3600 + minute * 60 + second);
    return result;
}

//
//  rebTimeNano: RL_API
//
REBVAL *RL_rebTimeNano(long nanoseconds) {
    Enter_Api();

    REBVAL *result = Alloc_Value();
    RESET_VAL_HEADER(result, REB_TIME);
    VAL_NANO(result) = nanoseconds;
    return result;
}

//
//  rebDateYMD: RL_API
//
REBVAL *RL_rebDateYMD(
    unsigned int year,
    unsigned int month,
    unsigned int day
){
    Enter_Api();

    REBVAL *result = Alloc_Value();
    RESET_VAL_HEADER(result, REB_DATE); // no time or time zone flags
    VAL_YEAR(result) = year;
    VAL_MONTH(result) = month;
    VAL_DAY(result) = day;
    return result;
}

//
//  rebDateTime: RL_API
//
REBVAL *RL_rebDateTime(const REBVAL *date, const REBVAL *time)
{
    Enter_Api();

    if (not IS_DATE(date))
        fail ("rebDateTime() date parameter must be DATE!");

    if (not IS_TIME(time))
        fail ("rebDateTime() time parameter must be TIME!");

    // if we had a timezone, we'd need to set DATE_FLAG_HAS_ZONE and
    // then INIT_VAL_ZONE().  But since DATE_FLAG_HAS_ZONE is not set,
    // the timezone bitfield in the date is ignored.

    REBVAL *result = Alloc_Value();
    RESET_VAL_HEADER(result, REB_DATE);
    SET_VAL_FLAG(result, DATE_FLAG_HAS_TIME);
    VAL_YEAR(result) = VAL_YEAR(date);
    VAL_MONTH(result) = VAL_MONTH(date);
    VAL_DAY(result) = VAL_DAY(date);
    VAL_NANO(result) = VAL_NANO(time);
    return result;
}

//
//  rebHalt: RL_API
//
// Signal that code evaluation needs to be interrupted.
//
// Returns:
//     nothing
// Notes:
//     This function sets a signal that is checked during evaluation
//     and will cause the interpreter to begin processing an escape
//     trap. Note that control must be passed back to REBOL for the
//     signal to be recognized and handled.
//
void RL_rebHalt(void)
{
    Enter_Api();

    SET_SIGNAL(SIG_HALT);
}

//
//  rebEvent: RL_API
//
// Appends an application event (e.g. GUI) to the event port.
//
// Returns:
//     Returns TRUE if queued, or FALSE if event queue is full.
// Arguments:
//     evt - A properly initialized event structure. The
//         contents of this structure are copied as part of
//         the function, allowing use of locals.
// Notes:
//     Sets a signal to get REBOL attention for WAIT and awake.
//     To avoid environment problems, this function only appends
//     to the event queue (no auto-expand). So if the queue is full
//
// !!! Note to whom it may concern: REBEVT would now be 100% compatible with
// a REB_EVENT REBVAL if there was a way of setting the header bits in the
// places that generate them.
//
int RL_rebEvent(REBEVT *evt)
{
    Enter_Api();

    REBVAL *event = Append_Event();     // sets signal

    if (event) {                        // null if no room left in series
        RESET_VAL_HEADER(event, REB_EVENT); // has more space, if needed
        event->extra.eventee = evt->eventee;
        event->payload.event.type = evt->type;
        event->payload.event.flags = evt->flags;
        event->payload.event.win = evt->win;
        event->payload.event.model = evt->model;
        event->payload.event.data = evt->data;
        return 1;
    }

    return 0;
}

//
//  rescue: native [
//
//  {Dummy ACTION! for rebRescue() API to hinge stray allocations to}
//
//  ]
//
REBNATIVE(rescue)
//
// !!! *VERY* temporary hack, because API handles must be owned by frames
// that can be reified.  The system currently does not allow the reification
// of non-ACTION! frames.
{
    UNUSED(frame_);
    return R_UNHANDLED;
}

//
//  rebRescue: RL_API
//
// This API abstracts the mechanics by which exception-handling is done.
// While code that knows specifically which form is used can take advantage of
// that knowledge and use the appropriate mechanism without this API, any
// code (such as core code) that wants to be agnostic to mechanism should
// use rebRescue() instead.
//
// There are three current mechanisms which can be built with.  One is to
// use setjmp()/longjmp(), which is extremely dodgy.  But it's what R3-Alpha
// used, and it's the only choice if one is sticking to ANSI C89-99:
//
// https://en.wikipedia.org/wiki/Setjmp.h#Exception_handling
//
// If one is willing to compile as C++ -and- link in the necessary support
// for exception handling, there are benefits to doing exception handling
// with throw/catch.  One advantage is performance: most compilers can avoid
// paying for catch blocks unless a throw occurs ("zero-cost exceptions"):
//
// https://stackoverflow.com/q/15464891/ (description of the phenomenon)
// https://stackoverflow.com/q/38878999/ (note that it needs linker support)
//
// It also means that C++ API clients can use try/catch blocks without needing
// the rebRescue() abstraction, as well as have destructors run safely.
// (longjmp pulls the rug out from under execution, and doesn't stack unwind).
//
// The other abstraction is for JavaScript, where an emscripten build would
// have to painstakingly emulate setjmp/longjmp.  Using inline JavaScript to
// catch and throw is more efficient, and also provides the benefit of API
// clients being able to use normal try/catch of a RebolError instead of
// having to go through rebRescue().
//
// But using rebRescue() internally allows the core to be compiled and run
// compatibly across all these scenarios.  It is named after Ruby's operation,
// which deals with the identical problem:
//
// http://silverhammermba.github.io/emberb/c/#rescue
//
// !!! As a first step, this only implements the setjmp/longjmp logic.
//
REBVAL *RL_rebRescue(
    REBDNG *dangerous, // !!! pure C function only if not using throw/catch!
    void *opaque
){
    Enter_Api();

    // We want allocations that occur in the body of the C function for the
    // rebRescue() to be automatically cleaned up in the case of an error.
    //
    // !!! This is currently done by knowing what frame an error occurred in
    // and marking any allocations while that frame was in effect as being
    // okay to "leak" (in the sense of leaking to be GC'd).  So we have to
    // make a dummy frame here, and unfortunately the frame must be reified
    // so it has to be an "action frame".  Improve mechanic later, but for
    // now pretend to be applying a dummy native.
    //
    DECLARE_FRAME (f);
    f->out = m_cast(REBVAL*, END); // should not be written

    Push_Frame_For_Apply(f);
    Push_Action(
        f,
        NULL, // opt_label
        NAT_ACTION(rescue),
        UNBOUND
    );
  #if !defined(NDEBUG)
    Prep_Stack_Cell(f->arg);
    Init_Unreadable_Blank(f->arg);
  #endif
    f->param = END; // signal all arguments gathered
    f->refine = NULL;
    f->arg = NULL;
    f->special = NULL;

    struct Reb_State state;
    REBCTX *error_ctx;

    PUSH_TRAP(&error_ctx, &state);

    // The first time through the following code 'error' will be null, but...
    // `fail` can longjmp here, so 'error' won't be null *if* that happens!
    //
    if (error_ctx) {
        if (f->varlist) // was reified
            SET_SER_INFO(f->varlist, FRAME_INFO_FAILED);
        Drop_Action_Core(f, TRUE);
        Abort_Frame(f);
        return Init_Error(Alloc_Value(), error_ctx);
    }

    REBVAL *result = (*dangerous)(opaque);

    DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

    Drop_Action_Core(f, TRUE); // drop_chunks = TRUE
    Drop_Frame_Core(f); // stack may not balance

    if (not result)
        return nullptr; // null is considered a legal result

    assert(not IS_VOID(result)); // leaked non-API null

    // Analogous to how TRAP works, if you don't have a handler for the
    // error case then you can't return an ERROR!, since all errors indicate
    // a failure.
    //
    if (IS_ERROR(result)) {
        rebRelease(result);
        return nullptr;
    }

    // !!! We automatically proxy the ownership of any managed handles to the
    // caller.  Any other handles that leak out (e.g. via state) will not be
    // covered by this, and would have to be unmanaged.  Do another allocation
    // just for the sake of it.

    assert(Is_Api_Value(result)); // !!! this could be relaxed if need be
    REBVAL *proxy = Move_Value(Alloc_Value(), result); // parent is not f
    rebRelease(result);
    return proxy;
}

//
//  rebRescueWith: RL_API
//
// Variant of rebRescue() with a handler hook (parallels TRAP/WITH, except
// for C code as the protected code and the handler).  More similar to
// Ruby's rescue2 operation.
//
REBVAL *RL_rebRescueWith(
    REBDNG *dangerous, // !!! pure C function only if not using throw/catch!
    REBRSC *rescuer, // errors in the rescuer function will *not* be caught
    void *opaque
){
    Enter_Api();

    struct Reb_State state;
    REBCTX *error_ctx;

    PUSH_TRAP(&error_ctx, &state);

    // The first time through the following code 'error' will be null, but...
    // `fail` can longjmp here, so 'error' won't be null *if* that happens!
    //
    if (error_ctx) {
        REBVAL *error = Init_Error(Alloc_Value(), error_ctx);

        REBVAL *result = (*rescuer)(error, opaque); // *not* guarded by trap!

        rebRelease(error);
        return result; // no special handling, may be null
    }

    REBVAL *result = (*dangerous)(opaque); // guarded by trap
    assert(not IS_VOID(result)); // void cells not exposed by API

    DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

    return result; // no special handling, may be NULL
}

inline static REBFRM *Extract_Live_Rebfrm_May_Fail(const REBVAL *frame) {
    if (not IS_FRAME(frame))
        fail ("Not a FRAME!");

    REBFRM *f = CTX_FRAME_MAY_FAIL(VAL_CONTEXT(frame));

    assert(Is_Action_Frame(f) and not Is_Action_Frame_Fulfilling(f));
    return f;
}

//
//  rebFrmNumArgs: RL_API
//
REBCNT RL_rebFrmNumArgs(const REBVAL *frame) {
    Enter_Api();

    REBFRM *f = Extract_Live_Rebfrm_May_Fail(frame);
    return FRM_NUM_ARGS(f);
}

//
//  rebFrmArg: RL_API
//
REBVAL *RL_rebFrmArg(const REBVAL *frame, REBCNT n) {
    Enter_Api();

    REBFRM *f = Extract_Live_Rebfrm_May_Fail(frame);
    return FRM_ARG(f, n);
}

//
//  rebDid: RL_API
//
REBOOL RL_rebDid(const void *p, ...) {
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (condition);
    REBIXO indexor = Do_Va_Core(
        condition,
        p, // opt_first (preloads value)
        &va, // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );
    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(condition));

    return not IS_VOID_OR_FALSEY(condition); // DID treats voids as "falsey"
}

//
//  rebNot: RL_API
//
// !!! If this were going to be a macro like (not (rebDid(...))) it would have
// to be a variadic macro.  Just make a separate entry point for now.
//
REBOOL RL_rebNot(const void *p, ...) {
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (condition);
    REBIXO indexor = Do_Va_Core(
        condition,
        p, // opt_first (preloads value)
        &va, // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );
    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(condition));

    return IS_VOID_OR_FALSEY(condition); // NOT treats voids as "falsey"
}

//
//  rebUnbox: RL_API
//
// C++, JavaScript, and other languages can do some amount of intelligence
// with a generic `rebUnbox()` operation...either picking the type to return
// based on the target in static typing, or returning a dynamically typed
// value.  For convenience in C, make the generic unbox operation return
// an integer for INTEGER!, LOGIC!, CHAR!...assume it's most common so the
// short name is worth it.
//
long RL_rebUnbox(const void *p, ...) {
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (result);
    REBIXO indexor = Do_Va_Core(
        result,
        p, // opt_first (preloads value)
        &va, // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );
    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(result));

    switch (VAL_TYPE(result)) {
    case REB_INTEGER:
        return VAL_INT64(result);

    case REB_CHAR:
        return VAL_CHAR(result);

    case REB_LOGIC:
        return VAL_LOGIC(result) ? 1 : 0;

    default:
        fail ("Only REB_INTEGER, REB_CHAR, REB_LOGIC for rebUnbox() in C");
    }
}

//
//  rebUnboxInteger: RL_API
//
long RL_rebUnboxInteger(const REBVAL *v) {
    Enter_Api();
    return VAL_INT64(v);
}

//
//  rebUnboxDecimal: RL_API
//
REBDEC RL_rebUnboxDecimal(const REBVAL *v) {
    Enter_Api();
    return VAL_DECIMAL(v);
}

//
//  rebUnboxChar: RL_API
//
REBUNI RL_rebUnboxChar(const REBVAL *v) {
    Enter_Api();
    return VAL_CHAR(v);
}

//
//  rebNanoOfTime: RL_API
//
long RL_rebNanoOfTime(const REBVAL *v) {
    Enter_Api();
    return VAL_NANO(v);
}

//
//  rebValTupleData: RL_API
//
REBYTE *RL_rebValTupleData(const REBVAL *v) {
    Enter_Api();
    return VAL_TUPLE_DATA(m_cast(REBVAL*, v));
}

//
//  rebIndexOf: RL_API
//
long RL_rebIndexOf(const REBVAL *v) {
    Enter_Api();
    return VAL_INDEX(v);
}

//
//  rebInitDate: RL_API
//
// !!! Note this doesn't allow you to say whether the date has a time
// or zone component at all.  Those could be extra flags, or if Rebol values
// were used they could be blanks vs. integers.  Further still, this kind
// of API is probably best kept as calls into Rebol code, e.g.
// RL_Do("make time!", ...); which might not offer the best performance, but
// the internal API is available for clients who need that performance,
// who can call date initialization themselves.
//
REBVAL *RL_rebInitDate(
    int year,
    int month,
    int day,
    int seconds,
    int nano,
    int zone
){
    Enter_Api();

    REBVAL *result = Alloc_Value();
    RESET_VAL_HEADER(result, REB_DATE);
    VAL_YEAR(result) = year;
    VAL_MONTH(result) = month;
    VAL_DAY(result) = day;

    SET_VAL_FLAG(result, DATE_FLAG_HAS_ZONE);
    INIT_VAL_ZONE(result, zone / ZONE_MINS);

    SET_VAL_FLAG(result, DATE_FLAG_HAS_TIME);
    VAL_NANO(result) = SECS_TO_NANO(seconds) + nano;
    return result;
}

//
//  rebHandle: RL_API
//
// !!! The HANDLE! type has some complexity to it, because function pointers
// in C and C++ are not actually guaranteed to be the same size as data
// pointers.  Also, there is an optional size stored in the handle, and a
// cleanup function the GC may call when references to the handle are gone.
//
REBVAL *RL_rebHandle(void *data, uintptr_t length, CLEANUP_CFUNC* cleaner)
{
    return Init_Handle_Managed(Alloc_Value(), data, length, cleaner);
}

//
//  rebSpellingOf: RL_API
//
// Extract UTF-8 data from an ANY-STRING! or ANY-WORD!.
//
// API does not return the number of UTF-8 characters for a value, because
// the answer to that is always cached for any value position as LENGTH OF.
// The more immediate quantity of concern to return is the number of bytes.
//
size_t RL_rebSpellingOf(
    char *buf,
    size_t buf_size, // number of bytes
    const REBVAL *v
){
    Enter_Api();

    const char *utf8;
    REBSIZ utf8_size;
    if (ANY_STRING(v)) {
        REBSIZ offset;
        REBSER *temp = Temp_UTF8_At_Managed(
            &offset, &utf8_size, v, VAL_LEN_AT(v)
        );
        utf8 = cs_cast(BIN_AT(temp, offset));
    }
    else {
        assert(ANY_WORD(v));

        REBSTR *spelling = VAL_WORD_SPELLING(v);
        utf8 = STR_HEAD(spelling);
        utf8_size = STR_SIZE(spelling);
    }

    if (not buf) {
        assert(buf_size == 0);
        return utf8_size; // caller must allocate a buffer of size + 1
    }

    REBSIZ limit = MIN(buf_size, utf8_size);
    memcpy(buf, utf8, limit);
    buf[limit] = '\0';
    return utf8_size;
}

//
//  rebSpellAlloc: RL_API
//
// This gives the spelling as UTF-8 bytes.  Length in codepoints should be
// extracted with LENGTH OF.  If size in bytes of the encoded UTF-8 is needed,
// use the binary extraction API (works on ANY-STRING! to get UTF-8)
//
char *RL_rebSpellAlloc(const void *p, ...)
{
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (string);
    REBIXO indexor = Do_Va_Core(
        string,
        p, // opt_first (preloads value)
        &va,  // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(string));

    if (IS_VOID(string))
        return NULL; // NULL is passed through, for opting out

    size_t size = rebSpellingOf(nullptr, 0, string);
    char *result = cast(char*, rebMalloc(size + 1)); // add space for term
    rebSpellingOf(result, size, string);
    return result;
}

//
//  rebSpellingOfW: RL_API
//
// Extract UCS-2 data from an ANY-STRING! or ANY-WORD!.  Note this is *not*
// UTF-16, so codepoints that require more than two bytes to represent will
// cause errors.
//
// !!! Although the rebSpellingOf API deals in bytes, this deals in count of
// characters.  (The use of REBCNT instead of REBSIZ indicates this.)  It may
// be more useful for the wide string APIs to do this so leaving it that way
// for now.
//
REBCNT RL_rebSpellingOfW(
    REBWCHAR *buf,
    REBCNT buf_chars, // characters buffer can hold (not including terminator)
    const REBVAL *v
){
    Enter_Api();

    REBSER *s;
    REBCNT index;
    REBCNT len;
    if (ANY_STRING(v)) {
        s = VAL_SERIES(v);
        index = VAL_INDEX(v);
        len = VAL_LEN_AT(v);
    }
    else {
        assert(ANY_WORD(v));

        REBSTR *spelling = VAL_WORD_SPELLING(v);
        s = Make_Sized_String_UTF8(STR_HEAD(spelling), STR_SIZE(spelling));
        index = 0;
        len = SER_LEN(s);
    }

    if (not buf) { // querying for size
        assert(buf_chars == 0);
        if (ANY_WORD(v))
            Free_Unmanaged_Series(s);
        return len; // caller must now allocate buffer of len + 1
    }

    REBCNT limit = MIN(buf_chars, len);
    REBCNT n = 0;
    for (; index < limit; ++n, ++index)
        buf[n] = GET_ANY_CHAR(s, index);

    buf[limit] = 0;

    if (ANY_WORD(v))
        Free_Unmanaged_Series(s);
    return len;
}

//
//  rebSpellAllocW: RL_API
//
// Gives the spelling as WCHARs.  If length in codepoints is needed, use
// a separate LENGTH OF call.
//
// !!! Unlike with rebSpellAlloc(), there is not an alternative for getting
// the size in UTF-16-encoded characters, just the LENGTH OF result.  While
// that works for UCS-2 (where all codepoints are two bytes), it would not
// work if Rebol supported UTF-16.  Which it may never do in the core or
// API (possible solutions could include usermode UTF-16 conversion to binary,
// and extraction of that with rebBytes(), then dividing the size by 2).
//
REBWCHAR *RL_rebSpellAllocW(const void *p, ...)
{
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (string);
    REBIXO indexor = Do_Va_Core(
        string,
        p, // opt_first (preloads value)
        &va,  // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(string));

    if (IS_VOID(string))
        return NULL; // NULL is passed through, for opting out

    REBCNT len = rebSpellingOfW(nullptr, 0, string);
    REBWCHAR *result = cast(
        REBWCHAR*, rebMalloc(sizeof(REBWCHAR) * (len + 1))
    );
    rebSpellingOfW(result, len, string);
    return result;
}

//
//  rebBytesOfBinary: RL_API
//
// Extract binary data from a BINARY!
//
// !!! Caller must allocate a buffer of the returned size + 1.  It's not clear
// if this is a good idea; but this is based on a longstanding convention of
// zero termination of Rebol series, including binaries.  Review.
//
REBSIZ RL_rebBytesOfBinary(
    REBYTE *buf,
    REBSIZ buf_size,
    const REBVAL *binary
){
    Enter_Api();

    if (not IS_BINARY(binary))
        fail ("rebValBin() only works on BINARY!");

    REBCNT size = VAL_LEN_AT(binary);

    if (not buf) {
        assert(buf_size == 0);
        return size; // currently, caller must allocate a buffer of size + 1
    }

    REBCNT limit = MIN(buf_size, size);
    memcpy(s_cast(buf), cs_cast(VAL_BIN_AT(binary)), limit);
    buf[limit] = '\0';
    return size;
}

//
//  rebBytesAlloc: RL_API
//
// Can be used to get the bytes of a BINARY! and its size, or the UTF-8
// encoding of an ANY-STRING! or ANY-WORD! and that size in bytes.  (Hence,
// for strings it is like rebSpellAlloc() except telling you how many bytes.)
//
// !!! This may wind up being a generic TO BINARY! converter, so you might
// be able to get the byte conversion for any type.
//
REBYTE *RL_rebBytesAlloc(REBSIZ *size_out, const void *p, ...)
{
    Enter_Api();

    va_list va;
    va_start(va, p);

    DECLARE_LOCAL (series);
    REBIXO indexor = Do_Va_Core(
        series,
        p, // opt_first (preloads value)
        &va,  // va_end called by evaluator (has to, e.g. for fail())
        DO_FLAG_EXPLICIT_EVALUATE | DO_FLAG_TO_END
    );

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(series));

    if (IS_VOID(series)) {
        *size_out = 0;
        return NULL; // NULL is passed through, for opting out
    }

    if (ANY_WORD(series) or ANY_STRING(series)) {
        *size_out = rebSpellingOf(nullptr, 0, series);
        REBYTE *result = rebAllocN(REBYTE, (*size_out + 1));
        rebSpellAlloc(result, *size_out, series);
        return result;
    }

    if (IS_BINARY(series)) {
        *size_out = rebBytesOfBinary(nullptr, 0, series);
        REBYTE *result = rebAllocN(REBYTE, (*size_out + 1));
        rebBytesOfBinary(result, *size_out, series);
        return result;
    }

    fail ("rebBytesAlloc() only works with ANY-STRING!/ANY-WORD!/BINARY!");
}

//
//  rebBinary: RL_API
//
REBVAL *RL_rebBinary(const void *bytes, size_t size)
{
    Enter_Api();

    REBSER *bin = Make_Binary(size);
    memcpy(BIN_HEAD(bin), bytes, size);
    TERM_BIN_LEN(bin, size);

    return Init_Binary(Alloc_Value(), bin);
}

//
//  rebSizedText: RL_API
//
// If utf8 does not contain valid UTF-8 data, this may fail().
//
REBVAL *RL_rebSizedText(const char *utf8, size_t size)
{
    Enter_Api();
    return Init_Text(Alloc_Value(), Make_Sized_String_UTF8(utf8, size));
}

//
//  rebText: RL_API
//
REBVAL *RL_rebText(const char *utf8)
{
    // Handles Enter_Api
    return rebSizedText(utf8, strsize(utf8));
}

//
//  rebT: RL_API
//
// Shorthand for `rebR(rebText(...))` to more easily create text parameters.
//
// !!! Since the data is UTF-8, it may be possible to make this a "delayed"
// text argument...that saves the pointer it is given and uses it directly,
// then only proxies it into a series at Move_Value() time.
//
const void *RL_rebT(const char *utf8)
{
    return rebR(rebText(utf8));
}

//
//  rebFile: RL_API
//
REBVAL *RL_rebFile(const char *utf8)
{
    REBVAL *result = rebText(utf8); // Enter_Api() called
    RESET_VAL_HEADER(result, REB_FILE);
    return result;
}

//
//  rebTag: RL_API
//
REBVAL *RL_rebTag(const char *utf8)
{
    REBVAL *result = rebText(utf8);
    RESET_VAL_HEADER(result, REB_TAG);
    return result;
}

//
//  rebLock: RL_API
//
REBVAL *RL_rebLock(REBVAL *p1, const REBVAL *p2)
{
    assert(IS_END(p2)); // Not yet variadic...
    UNUSED(p2);

    REBSER *locker = nullptr;
    Ensure_Value_Immutable(p1, locker);
    return p1;
}

//
//  rebLengthedTextW: RL_API
//
REBVAL *RL_rebLengthedTextW(const REBWCHAR *wstr, REBCNT len)
{
    Enter_Api();

    DECLARE_MOLD (mo);
    Push_Mold(mo);

    if (len == UNKNOWN) {
        for (; *wstr != 0; ++wstr)
            Append_Utf8_Codepoint(mo->series, *wstr);
    }
    else {
        for (; len != 0; --len, ++wstr)
            Append_Utf8_Codepoint(mo->series, *wstr);
    }

    return Init_Text(Alloc_Value(), Pop_Molded_String(mo));
}

//
//  rebTextW: RL_API
//
REBVAL *RL_rebTextW(const REBWCHAR *wstr)
{
    Enter_Api();
    return rebLengthedTextW(wstr, UNKNOWN);
}

//
//  rebSizedWordW: RL_API
//
// !!! Currently needed by ODBC module to make column titles.
//
REBVAL *RL_rebSizedWordW(const REBWCHAR *ucs2, REBCNT len)
{
    Enter_Api();

    DECLARE_MOLD (mo);
    Push_Mold(mo);

    if (len == UNKNOWN) {
        for (; *ucs2 != 0; ++ucs2)
            Append_Utf8_Codepoint(mo->series, *ucs2);
    }
    else {
        for (; len != 0; --len, ++ucs2)
            Append_Utf8_Codepoint(mo->series, *ucs2);
    }

    REBSER *bin = Pop_Molded_UTF8(mo);
    REBSTR *spelling = Intern_UTF8_Managed(BIN_HEAD(bin), BIN_LEN(bin));

    return Init_Word(Alloc_Value(), spelling);
}

//
//  rebFileW: RL_API
//
REBVAL *RL_rebFileW(const REBWCHAR *wstr)
{
    REBVAL *result = rebTextW(wstr); // Enter_Api() called
    RESET_VAL_HEADER(result, REB_FILE);
    return result;
}

//
//  rebManage: RL_API
//
// The "friendliest" default for the API is to assume you want handles to be
// tied to the lifetime of the frame they're in.  Long-running top-level
// processes like the C code running the console would eventually exhaust
// memory if that were the case...so there should be some options for metrics
// as a form of "leak detection" even so.
//
REBVAL *RL_rebManage(REBVAL *v)
{
    Enter_Api();

    assert(Is_Api_Value(v));

    REBARR *a = Singular_From_Cell(v);
    assert(GET_SER_FLAG(a, NODE_FLAG_ROOT));

    if (IS_ARRAY_MANAGED(a))
        fail ("Attempt to rebManage() a handle that's already managed.");

    SET_SER_FLAG(a, NODE_FLAG_MANAGED);
    assert(LINK(a).owner == UNBOUND);
    if (not FS_TOP)
        LINK(a).owner = UNBOUND;
    else
        LINK(a).owner = NOD(CTX_VARLIST(
            Context_For_Frame_May_Reify_Managed(FS_TOP)
        ));

    return v;
}

//
//  rebUnmanage: RL_API
//
// This converts an API handle value to indefinite lifetime.
//
void RL_rebUnmanage(void *p)
{
    Enter_Api();

    REBNOD *nod = NOD(p);
    if (not (nod->header.bits & NODE_FLAG_CELL))
        fail ("rebUnmanage() not yet implemented for rebMalloc() data");

    REBVAL *v = cast(REBVAL*, nod);
    assert(Is_Api_Value(v));

    REBARR *a = Singular_From_Cell(v);
    assert(GET_SER_FLAG(a, NODE_FLAG_ROOT));

    if (not IS_ARRAY_MANAGED(a))
        fail ("Attempt to rebUnmanage() a handle with indefinite lifetime.");

    // It's not safe to convert the average series that might be referred to
    // from managed to unmanaged, because you don't know how many references
    // might be in cells.  But the singular array holding API handles has
    // pointers to its cell being held by client C code only.  It's at their
    // own risk to do this, and not use those pointers after a free.
    //
    CLEAR_SER_FLAG(a, NODE_FLAG_MANAGED);
    assert(
        LINK(a).owner == UNBOUND // freed when program exits
        or GET_SER_FLAG(LINK(a).owner, ARRAY_FLAG_VARLIST)
    );
    LINK(a).owner = UNBOUND;
}

//
//  rebLengthOf: RL_API
//
// !!! Should this be an entry point, vs `rebUnbox("length of", x, END)`?
// It may be one of the cases that is called often enough to warrant it for
// performance, but a question of the libRebol API is whether such hard-coded
// behaviors that subvert hooks on OF (for instance) are a solid plan.
//
long RL_rebLengthOf(const REBVAL *series)
{
    Enter_Api();

    if (not ANY_SERIES(series))
        fail ("rebLengthOf() can only be used on ANY-SERIES!");

    return VAL_LEN_AT(series);
}

//
//  rebRelease: RL_API
//
// An API handle is only 4 platform pointers in size (plus some bookkeeping),
// but it still takes up some storage.  The intended default for API handles
// is that they live as long as the function frame they belong to, but there
// will be several lifetime management tricks to ease releasing them.
//
// !!! For the time being, we lean heavily on explicit release.  Near term
// leak avoidance will need to at least allow for GC of handles across errors
// for their associated frames.
//
void RL_rebRelease(REBVAL *v)
{
    Enter_Api();

    if (not v)
        return; // less rigorous, but makes life easier for C programmers

    if (not Is_Api_Value(v))
        panic ("Attempt to rebRelease() a non-API handle");

    Free_Value(v);
}

//
//  rebError: RL_API
//
// Note: Over the long term, one does not want to hard-code error strings in
// the executable.  That makes them more difficult to hook with translations,
// or to identify systemically with some kind of "error code".  However,
// it's a realistic quick-and-dirty way of delivering a more meaningful
// error than just using a RE_MISC error code, and can be found just as easily
// to clean up later.
//
// !!! Should there be a way for the caller to slip their C file and line
// information through as the source of the FAIL?
//
REBVAL *RL_rebError(const char *msg)
{
    Enter_Api();
    return Init_Error(Alloc_Value(), Error_User(msg));
}

//
//  rebEnd: RL_API
//
const REBVAL *RL_rebEnd(void) {return END;}

//
//  rebDeflateAlloc: RL_API
//
// Exposure of the deflate() of the built-in zlib.  Assumes no envelope.
//
// Uses zlib's recommended default for compression level.
//
// See rebRepossess() for the ability to mutate the result into a BINARY!
//
REBYTE *RL_rebDeflateAlloc(
    REBCNT *out_len,
    const unsigned char* input,
    REBCNT in_len
){
    return Compress_Alloc_Core(out_len, input, in_len, SYM_0);
}

//
//  rebZdeflateAlloc: RL_API
//
// Variant of rebDeflateAlloc() which adds a zlib envelope...which is a 2-byte
// header and 32-bit ADLER32 CRC at the tail.
//
REBYTE *RL_rebZdeflateAlloc(
    REBCNT *out_len,
    const unsigned char* input,
    REBCNT in_len
){
    return Compress_Alloc_Core(out_len, input, in_len, SYM_ZLIB);
}

//
//  rebGzipAlloc: RL_API
//
// Slight variant of deflate() which stores the uncompressed data's size
// implicitly in the returned data, and a CRC32 checksum.
//
REBYTE *RL_rebGzipAlloc(
    REBCNT *out_len,
    const unsigned char* input,
    REBCNT in_len
){
    return Compress_Alloc_Core(out_len, input, in_len, SYM_GZIP);
}

//
//  rebInflateAlloc: RL_API
//
// Exposure of the inflate() of the built-in zlib.  Assumes no envelope.
//
// Use max = -1 to guess decompressed size, or for best memory efficiency,
// specify `max` as the precise size of the original data.
//
// See rebRepossess() for the ability to mutate the result into a BINARY!
//
REBYTE *RL_rebInflateAlloc(
    REBCNT *len_out,
    const REBYTE *input,
    REBCNT len_in,
    REBINT max
){
    return Decompress_Alloc_Core(len_out, input, len_in, max, SYM_0);
}

//
//  rebZinflateAlloc: RL_API
//
// Variant of rebInflateAlloc() which assumes a zlib envelope...checking for
// the 2-byte header and verifying the 32-bit ADLER32 CRC at the tail.
//
REBYTE *RL_rebZinflateAlloc(
    REBCNT *len_out,
    const REBYTE *input,
    REBCNT len_in,
    REBINT max
){
    return Decompress_Alloc_Core(len_out, input, len_in, max, SYM_ZLIB);
}

//
//  rebGunzipAlloc: RL_API
//
// Slight variant of inflate() which is compatible with gzip, and checks its
// CRC32.  For data whose original size was < 2^32 bytes, the gzip envelope
// stored that size...so memory efficiency is achieved even if max = -1.
//
// Note: That size guarantee exists for data compressed with rebGzipAlloc() or
// adhering to the gzip standard.  However, archives created with the GNU
// gzip tool make streams with possible trailing zeros or concatenations:
//
// http://stackoverflow.com/a/9213826
//
REBYTE *RL_rebGunzipAlloc(
    REBCNT *len_out,
    const REBYTE *input,
    REBCNT len_in,
    REBINT max
){
    return Decompress_Alloc_Core(len_out, input, len_in, max, SYM_GZIP);
}

//
//  rebDeflateDetectAlloc: RL_API
//
// Does DEFLATE with detection, and also ignores the size information in a
// gzip file, due to the reasoning here:
//
// http://stackoverflow.com/a/9213826
//
REBYTE *RL_rebDeflateDetectAlloc(
    REBCNT *len_out,
    const REBYTE *input,
    REBCNT len_in,
    REBINT max
){
    return Decompress_Alloc_Core(len_out, input, len_in, max, SYM_DETECT);
}

// !!! Although it is very much the goal to get all OS-specific code out of
// the core (including the API), this particular hook is extremely useful to
// have available to all clients.  It might be done another way (e.g. by
// having hosts HIJACK the FAIL native with an adaptation that processes
// integer arguments).  But for now, stick it in the API just to get the
// wide availability.
//
#ifdef TO_WINDOWS
    #undef IS_ERROR // windows has its own meaning for this.
    #include <windows.h>
#else
    #include <errno.h>
    #define MAX_POSIX_ERROR_LEN 1024
#endif

//
//  rebFail_OS: RL_API [
//      #noreturn
//  ]
//
// Produce an error from an OS error code, by asking the OS for textual
// information it knows internally from its database of error strings.
//
// This function is called via a macro which adds DEAD_END; after it.
//
// Note that error codes coming from WSAGetLastError are the same as codes
// coming from GetLastError in 32-bit and above Windows:
//
// https://stackoverflow.com/q/15586224/
//
// !!! Should not be in core, but extensions need a way to trigger the
// common functionality one way or another.
//
void RL_rebFail_OS(int errnum)
{
    REBCTX *error;

#ifdef TO_WINDOWS
    if (errnum == 0)
        errnum = GetLastError();

    WCHAR *lpMsgBuf; // FormatMessage writes allocated buffer address here

    // Specific errors have %1 %2 slots, and if you know the error ID and
    // that it's one of those then this lets you pass arguments to fill
    // those in.  But since this is a generic error, we have no more
    // parameterization (hence FORMAT_MESSAGE_IGNORE_INSERTS)
    //
    va_list *Arguments = nullptr;

    // Apparently FormatMessage can find its error strings in a variety of
    // DLLs, but we don't have any context here so just use the default.
    //
    LPCVOID lpSource = nullptr;

    DWORD ok = FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER // see lpMsgBuf
            | FORMAT_MESSAGE_FROM_SYSTEM // e.g. ignore lpSource
            | FORMAT_MESSAGE_IGNORE_INSERTS, // see Arguments
        lpSource,
        errnum, // message identifier
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
        cast(WCHAR*, &lpMsgBuf), // allocated buffer address written here
        0, // buffer size (not used since FORMAT_MESSAGE_ALLOCATE_BUFFER)
        Arguments
    );

    if (ok == 0) {
        //
        // Might want to show the value of GetLastError() in this message,
        // but trying to FormatMessage() on *that* would be excessive.
        //
        error = Error_User("FormatMessage() gave no error description");
    }
    else {
        REBVAL *temp = rebTextW(lpMsgBuf);
        LocalFree(lpMsgBuf);

        DECLARE_LOCAL (message);
        Move_Value(message, temp);
        rebRelease(temp);

        error = Error(RE_USER, message, END);
    }
#else
    // strerror() is not thread-safe, but strerror_r is. Unfortunately, at
    // least in glibc, there are two different protocols for strerror_r(),
    // depending on whether you are using the POSIX-compliant implementation
    // or the GNU implementation.
    //
    // The convoluted test below is the inversion of the actual test glibc
    // suggests to discern the version of strerror_r() provided. As other,
    // non-glibc implementations (such as OS X's libSystem) also provide the
    // POSIX-compliant version, we invert the test: explicitly use the
    // older GNU implementation when we are sure about it, and use the
    // more modern POSIX-compliant version otherwise. Finally, we only
    // attempt this feature detection when using glibc (__GNU_LIBRARY__),
    // as this particular combination of the (more widely standardised)
    // _POSIX_C_SOURCE and _XOPEN_SOURCE defines might mean something
    // completely different on non-glibc implementations.
    //
    // (Note that undefined pre-processor names arithmetically compare as 0,
    // which is used in the original glibc test; we are more explicit.)

    #ifdef USE_STRERROR_NOT_STRERROR_R
        char *shared = strerror(errnum);
        error = Error_User(shared);
    #elif defined(__GNU_LIBRARY__) \
            && (defined(_GNU_SOURCE) \
                || ((!defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L) \
                    && (!defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 600)))

        // May return an immutable string instead of filling the buffer

        char buffer[MAX_POSIX_ERROR_LEN];
        char *maybe_str = strerror_r(errnum, buffer, MAX_POSIX_ERROR_LEN);
        if (maybe_str != buffer)
            strncpy(buffer, maybe_str, MAX_POSIX_ERROR_LEN);
        error = Error_User(buffer);
    #else
        // Quoting glibc's strerror_r manpage: "The XSI-compliant strerror_r()
        // function returns 0 on success. On error, a (positive) error number
        // is returned (since glibc 2.13), or -1 is returned and errno is set
        // to indicate the error (glibc versions before 2.13)."

        char buffer[MAX_POSIX_ERROR_LEN];
        int result = strerror_r(errnum, buffer, MAX_POSIX_ERROR_LEN);

        // Alert us to any problems in a debug build.
        assert(result == 0);

        if (result == 0)
            error = Error_User(buffer);
        else if (result == EINVAL)
            error = Error_User("EINVAL: bad errno passed to strerror_r()");
        else if (result == ERANGE)
            error = Error_User("ERANGE: insufficient buffer size for error");
        else
            error = Error_User("Unknown problem with strerror_r() message");
    #endif
#endif

    DECLARE_LOCAL (temp);
    Init_Error(temp, error);
    rebJUMPS ("lib/fail", temp, END);
}

// We wish to define a table of the above functions to pass to clients.  To
// save on typing, the declaration of the table is autogenerated as a file we
// can include here.
//
// It doesn't make a lot of sense to expose this table to clients via an API
// that returns it, because that's a chicken-and-the-egg problem.  The reason
// a table is being used in the first place is because extensions can't link
// to an EXE (in a generic way).  So the table is passed to them, in that
// extension's DLL initialization function.
//
// !!! Note: if Rebol is built as a DLL or LIB, the story is different.
//
extern RL_LIB Ext_Lib;
#include "tmp-reb-lib-table.inc" // declares Ext_Lib


The deteriorating financial performance of state-owned enterprises (SOEs) has been an increasing concern for the government of Ghana. The contributions of SOEs to the gross domestic product (GDP) of the Ghanaian economy have declined, leading to the loss of job opportunities because of the unprofitability and rising debt levels. SOE managers need to adopt strategies to improve the financial performance of their organizations so they can contribute to the GDP and generate employment opportunities. Government, SOE management, and employees will benefit from profitable and sustainable SOEs that have the ability to contribute to the national development agenda. The purpose of this single-case study was to explore strategies Ghanaian SOE managers may use to improve financial performance and reverse unprofitability and unsustainability of SOEs. Transformational leadership theory was used to guide this study. Data were collected through semistructured interviews with 10 Ghanaian SOE managers and SOE documents. Analysis of data generated themes, which included performance management strategies, hindrances to financial improvement, leadership strategies, and core business strategies. By implementing strategies reported by participants, SOE managers may improve the financial performance of SOEs and contribute to the GDP growth of the Ghanaian economy. Findings may be used to promote growth and sustainability of Ghanaian SOEs and thereby increase employment opportunities to improve the social conditions of unemployed youths.


text:
- Jan 29 2001 - Jan 21 2001 - Jan 14 2001 - Jan 1 2001 - Dec 22 2000 - Dec 11 2000 - Dec 4 2000 - Nov 24 2000 - Nov 17 2000 - Nov 7 2000 - Oct 27 2000 - Oct 20 2000 - Oct 12 2000 - Oct 4 2000 - Sep 21 2000 - Sep 13 2000 - Sep 6 2000 - Aug 29 2000 - Aug 22 2000 - Aug 15 2000 - Aug 8 2000 - Jul 31 2000 - Jul 21 2000 - Jul 13 2000 - Jun 27 2000 - Jun 20 2000 - Jun 13 2000 - Jun 7 2000 - May 30 2000 - May 23 2000 - May 16 2000 - Nov 3 1999 - Jun 8 1999 - Apr 30 1999 - Feb 1 1999 - Dec 24 1998 - Oct 19 1998 HTML newsletter index
Welcome to the Papua New Guinea Gossip - 9 Feb 2001. A very special welcome is extended to any new readers. If you have comments or suggestions to make then please e-mail them to giaman@png-gossip.com
Articles for this newsletter
To send a message for submission to the PNG Gossip Newsletter -- please e-mail your contribution to pngnews.queue@png-gossip.com
Remember that this mailing list is mainly meant for people who have an interest and already know a little bit about the people and geography of Papua New Guinea. The PNG Kina, which is mentioned from time to time, is currently worth somewhere around 31 - 32 United States cents but varies on a daily basis.
If you are really keen on finding out what the Kina is worth a currency converter for most world currencies can be located at http://www.michie.net/pnginfo/moni.html
A new currency converter based in PNG is located at Papua New Guinea Business Resources Directory http://pngbd.com. It is made by the people from AAC&L -- http://www.aacl.net
For those who are not so familiar with the geography of Papua New Guinea I suggest taking a look http://maps.expedia.com and then click on the find a map option. Go to this link for a quick start to a link to a map of Port Moresby. http://www.michie.net/pnginfo/pom-map.html
Here is a website being developed by an ex-resident of PNG. He is in the tourism industry so if you are interested in taking a visit to PNG this might be the site to visit. There are only 28 pages at the moment but I have been informed that it is growing even as I type this. Pay a visit to http://way.to/riddaenterprises and as usual if you send email or leave a message please mention the PNG Gossip not because I receive any financial kickback but because it helps me justify the time I put into this newsletter.
Here is another great website with many photographs relating to PNG. It has been developed by Noel who has the dubious honour of having lived in Papua New Guinea for 20 years. Six of those he spent in a Central Province Village. Through this he was able to gain an insight into the local customs and language. http://content.communities.msn.com/LivinginPapuaNewGuinea
Noel is interested in corresponding with anyone who has lived in PNG.
Air Niugini DC3
Air Niugini has been in the news again but this time for a different reason. Captain Dave Wiltshire, who has recently come to the national flag carrier to help in the privatisation exercise, was dismayed to see a plane he once flew around PNG in a very sorry state. The plane is none other than the DC3 mounted on the poles outside Air Niugini House at Jackson's Airport. Captain Wiltshire must be a very keen supporter as he is the President of the Howden project in Melbourne which saw another pole mounted DC3 returned to flying condition and painted in the old Trans Australian Airline Colours. The plane, in Melbourne, is a sister to the one on the pole in Moresby. Both planes saw many hours of use carrying passengers and cargo around pre-independent PNG. Hopefully in the next few weeks I will be able to obtain some pictures of the Melbourne plane which flew around PNG registered as VH-SBA.
Thanks to The National Online - http://www.thenational.com.pg -- I have attached a photo of the PNG DC3 being washed.
Information on this plane can be found at http://www.michie.net/balus/pxp2anq.html. Other pictures, which I have obtained from Eva Arni at Air Niugini - http://www.airniugini.com.pg, can be found at http://www.michie.net/balus/dc3wash2.html
For anyone interested in aviation in PNG I am compiling a database of planes flown in PNG which can be found at http://www.michie.net/balus/index.html. For this project I am interested in obtaining pictures of old planes which I can use on the website as well as any reproducible stories about aviation in PNG from any readers or friends of readers.
Governor Titus Philemon last week opened a wharf on Goodenough Island which he hopes will boost business activities and improve transportation for the islanders
In an effort to save half a million Kina it has been reported in the Post Courier that PNG could risk exports of more than K300 million a year to Germany if it closes its foreign mission in Bonn to save.
The ANZ bank has announced that it will now print the name of the customer on cheque books that are issued. The cheques will also be MICR encoded. (Magnetic Ink Character Recognition) This will enable the electronic processing of cheques something which even the Post Office in Australia has had the ability to do for ages. It is anticipated that other banks will follow.
The cost of relocating the Duke of York Islanders whose island is rapidly being submerged under seawater may snowball into costing millions of Kina as other affected villages are identified. Priority has been given to resettling the people most affected at the moment.
Lae, Elcom
The Electricity Commission has announced the introduction of the pre-paid metered electricity system for Lae. The system, called EasyPay has been successfully trailed in Port Moresby. Consumers have to pre-buy units of electricity at local stores and this is then fed into the meter in the house. Due to the security risk of sending meter readers around the various towns in PNG this is seen as a safe and easy way of providing electricity.
Telikom
Telikom have now called on Associated Plumbing (API) to perform checks and fixes on telephone cables in the Korobosea area. I know the wet season has really hit Moresby but getting plumbers to repair telephone cables is a bit drastic isn't it or is their more than telephone conversations flowing down those cables? Meanwhile a team of scuba divers is needed to dry out some of the cabling around the seven-mile area of the national capital.
The National Capital has had two consecutive days where the rainfall has been greater than 80mm over a 24-hour period. In some case that rain has come down in less than an hour. For the first time ever - in the 30-year history of the dam - Elcom has reported than the dam at Sirinumu used for Hydro Electricity generation and also drinking water for the National Capital has reached the 93 per cent full mark.
Moran Oil field
Production of oil from the Moran oil field has resumed after a closure lasting a week. The landowners who caused the closure have apologised and signed an agreement that one association will represent the 14 clans and 17 sub clans. Chevron estimated the loss of oil to be approximately 113,000 barrels of oil.
The court has ordered the Madang provincial assembly not to elect a new governor whilst the ousted governor, Mr Kas, is seeking clarification on provisions of the Organic Law on Provincial and Local Level Government. It is reported that Mr Kas is pleased with the court ruling.
The Japanese Government has donated three vehicles worth K140,000 to the Department of Agriculture and Livestock for use by the grain and rice projects in the Morobe and Central provinces. http://www.agriculture.org.pg
The governor of Enga, Peter Ipatas, has said he would challenge any decision by the National Government to withdraw his powers.
National Provident Fund
The NPF has started legal proceedings to declare the former fund chairman insolvent. The court case is part of the fund's attempts to bring the fund back on track.
One of the local national parents is wondering why she has paid out K11,000 for her daughter to be in a combined prep, grade 1 and grade 2 classes. Surely this isn't the treatment a person who has paid so much money can expect? I don't think even community schools where the annual fee is more like K70 treat parents and students with such contempt. The International Education Agency has a website at http://www.iea.ac.pg
The Deputy Headmaster of the Mount Hagen Primary School has turned away more than half of the school's 900 students for non-payment of fees.
PNG newspaper sites
Post Courier -- http://www.postcourier.com.pg
The National Online -- http://www.thenational.com.pg
Various Links worth taking a look at
Some PNG Car firms
Boroko Motors - http://www.boroko-motors.com
Ela Motors - http://www.elamotors.com.pg
Hertz Hire Cars - http://www.leasemaster.com.pg
Miscellaneous PNG Sites
The Workers Mutual group are launching their own website. It can be viewed at http://www.workersmutual.com.pg.
PNG Meri
Here are the opening sentences from this website for PNG women - The women of Papua New Guinea are the same as other women in the world. They have families, careers, hopes and dreams. Yet Papua New Guinea women must live under the most difficult conditions. They must first attempt to overcome the low social standing in their society. Some women are viewed more as property then human beings. - With an introduction like that how could you not visit http://www.pngmeri.com ?
Pacific View Media
this is the home of a PNG based advertising agency http://www.pvm.com.pg
Air Niugini -- http://www.airniugini.com.pg
DIVErsion Dive -- http://www.diversionOZ.com/epngdive.htm
Lonely Planet Website -- http://www.lonelyplanet.com.au/dest/aust/png.htm
Madang Diving -- http://www.madangdiving.org.pg
Melanesian Tourist Services -- http://www.meltours.com
MV Golden Dawn -- http://www.mvgoldendawn.com
New Ireland Tourism -- http://www.discovernewireland.com.pg
Niugini Art -- http://www.global.net.pg/niugini-art
Tourism Promotion Authority -- http://www.pngtourism.org.pg
Trans Niugini Tours -- http://www.pngtours.com
Accommodation Sites
Alotau International Hotel -- http://www.alotauinternational.com
Coral Sea Hotels -- http://www.coralseahotels.com.pg
Haus Poroman Lodge -- http://www.hausporoman.com.pg
Kaivuna Resort Hotel -- http://www.kaivunaresort.com
Kulau Lodge -- http://www.global.net.pg/kulau
Lissenung Island Resort -- http://www.lissenung.com
Loloata Resort -- http://www.loloata.com
MV Febrina Dive Boat -- http://www.febrina.com
Nusa Island Retreat -- http://www.nusaislandretreat.com.pg
Pinelodge Resort -- was located at http://www.pinelodge.com.pg
Walindi Resort -- http://www.walindi.com
Daltron
Daltron have advertised a pack of 25 of their in house brand CD-R's for a mere K50. Of course 10% VAT has to be added to that figure but it still appears to be a bargain. http://www.daltron.com.pg
Computers and Communications
C&C have advertised fireproof safes starting from K599 and once again that is without VAT included.
Computer Companies in PNG
Able Computing -- http://www.able.com.pg
Daltron -- http://www.daltron.com.pg
Data Nets -- http://www.datanets.com.pg
Datec -- http://www.datec.com.pg
HT PNG (formerly High Tech Industries) -- http://www.hightech.com.pg/
Global Technologies -- http://www.global.net.pg
National -- http://www.thenational.com.pg
If a friend has given or sent you a copy of this newsletter and you would like to keep receiving free copies then please visit http://www.michie.net/news/index.html for instructions on how to join the list.
If you think you have a PNG page worth mentioning then please click here and use the submission form
To send a message for submission to the PNG Gossip Newsletter queue please click here and use the submission form.
Created 9 Feb, 2001
Updated 20 Feb, 2001

redpajama_set_name: RedPajamaCommonCrawl


text:
Why we need a second Brexit vote: First law of politics is people can change their minds
Leighton Andrews
French voters head to polling stations in Paris to vote in the controversial referendum on the European Union Constitution. (Photo by Owen Franken/Corbis via Getty Images)
People can, and regularly do, change their minds. The public should be given that option once the Brexit deal is done.
REACTIONS AFTER THE MAASTRICHT REFERENDUM (Photo by Francis Dean/Sygma via Getty Images)
The referendum was lost – and badly. All around people were shaking their heads, wondering what would happen next, aghast at their sense that the future had come to a full stop.
Some were already cursing the television for giving them the wrong result. Many were heading for home. Now they were in shock. Later, they would move through the standard stages of adjustment to loss: denial, anger, bargaining, depression, distraction. Some would, eventually, move on to acceptance. The process did not necessarily occur in that order. Others refused to give up hope.
The year was 1979. The referendum on whether Wales should have an elected Assembly was lost by four to one. Eighteen years later, Wales reversed its vote, by a whisker, after years of a London government which had gutted industries central to the Welsh sense of identity like mining and steel-making.
In 2011, in a third referendum, the people of Wales voted overwhelmingly to endorse a stronger Assembly. People can change their minds. It should be the first law of politics.
Yes Supporters at Dublin Castle at Dublin Castle, after Ireland paved the way for EU reform with more than two-thirds of voters overwhelmingly backing the Lisbon Treaty.
A week after the referendum last June, I visited the Viking Museum at Roskilde in Denmark. The museum houses a long-ship built near Dublin in 1042 by Scandinavian boat-builders. It's a reminder that links across our continent, forged in war but also by trade, have centuries-old roots that pre-date the era of modern capitalism.
Ireland and Denmark also have pointers for us when it comes to referendums. In Denmark, two referendums were held before the treaty of Maastricht passed.
Just before the Danish football team headed to victory in the 1992 Euros, Danish voters rejected Maastricht by a small majority. Denmark then negotiated four opt-outs from portions of the treaty. The second referendum in 1993 approved the treaty with the opt-outs.
In 2001 Ireland rejected the Treaty of Nice. After clear statements confirming that Ireland would not have to join a common defence policy and affirming the right of Irish parliamentarians to decide on enhanced cooperation, a second referendum the following year approved the Treaty.
Referendums don't always mean slavish acceptance without further negotiation or debate, as these three examples prove:
French European Constitution referendum, 2005
The French chose not to ratify the proposed Constitution of the European Union. But a change in president and political direction and a rewritten version was passed as part of the Lisbon Treaty. No second vote was held.
Irish Lisbon Treaty referenda, 2008/09
Initially the proposals were rejected by a slim margin because of fears over several issues including Ireland's military neutrality and rules on abortion. But, once opt outs were agreed on the more contentious issues, a year later the public went to the polls again and 68% voted to ratify the treaty.
The Danish Maastricht Treaty referenda, 1992/3
With just a tiny majority the No vote triumphed in the first round of voting for ratification of the Maastricht Treaty in Denmark. A year later – with a new deal on issues including defence and justice – Danes changed their minds with 57% voting to back the proposals.
Ireland called a referendum on the Treaty of Lisbon in 2008 – and again their voters rejected it the first time around. After that the Council of Ministers agreed a statement that other member states would not use the Treaty to reduce the number of permanent commissioners in favour of a rotating system with fewer commissioners, nor threaten Ireland's military neutrality and rules on abortion. Following that, Ireland voted in favour of the treaty in 2009.
People can change their minds.
Here in Wales, it's not been an easy time for the Welsh Government. Unlike Nicola Sturgeon's Scottish Government, able to demonstrate that Scotland voted to remain, the Welsh Government has a relatively weak hand to play. The people of Wales rejected the advice of the two political parties that dominate the national assembly, Welsh Labour and the main opposition party, the Welsh nationalist party Plaid Cymru, and joined England in voting to leave the EU.
Arguably then the Welsh vote was an anti-establishment vote. The Leave message had a clear appeal in post-industrial areas – former mining or steel-producing areas which had lost jobs to globalisation, white working-class areas which have lots of "left behind" voters. These areas have suffered the impact of austerity policies over the last six years – and it has been Labour councils which have had to implement the cuts imposed by Westminster.
While unusually the word sovereignty was heard on the doorsteps, expressed as a desire "to take back control", in the language of the Leave campaign, the biggest driver of the Brexit message was immigration, in Wales as in England.
Immigration, of course, was a proxy for a variety of other fears – about pressures on public services and the alleged under-cutting of wages, about the insecurities of change driven by globalisation – and was ruthlessly exploited by the Leave campaign who said that immigration levels could not be controlled within the EU.
Throughout the referendum campaign, activists for Remain had stressed the benefits Wales gets from Europe. Independent analysis by Cardiff University's Wales Governance Centre showed that Wales is a net beneficiary of the EU: in 2014 the net benefit to Wales of being in the EU was £245 million, or £79 per person.
More than £2 billion for farming and the countryside had been destined for Wales in the 2014-2020 period, and a further £2 billion plus in cohesion funding for the poorest areas of the country – including areas which voted most heavily to Leave. Business in Wales, as in the UK overall, including companies such as Airbus and Toyota, was firmly in support of the Remain campaign.
In January, Carwyn Jones, the Welsh Labour first minister, who heads the Welsh Government, published a white paper 'Securing Wales's Future', along with the leader of Plaid Cymru, setting out Welsh demands for the negotiations over Brexit. The paper sought to reconcile concerns about immigration with membership of the single market. It set out six key areas:
• The importance of continued participation in the single market to support businesses and secure jobs and the future prosperity of Wales
• A balanced approach to immigration linking migration to jobs and good properly-enforced employment practice which protects all workers whatever their country of origin
• The need for the UK Government to make good on promises made during the referendum campaign that Wales would not lose funding as a result of the UK leaving the EU
• A fundamentally different constitutional relationship between the devolved governments and the UK Government – based on mutual respect, reaching agreement through consent
• Maintaining the social and environmental protections, in particular workers' rights, once these are no longer guaranteed through the UK's membership of the EU
• Proper consideration of transitional arrangements if longer-term arrangements have not been agreed at the point of exit from the EU.
But sadly Theresa May has already ruled out UK membership of the single market, and has projected the alternative of a "no-deal" Brexit – a low tax Singapore-style option – which sounds the absolute opposite of the Welsh Government's demands for continued social and environmental protections.
I understand the Welsh Government's position, and were I still in the National Assembly, I would be loyally supporting what it says. But speaking for myself, I'm not in favour of a better Brexit. I'm a Brejectionist.
Cardiff, the city where I was born, and where I now teach, voted 60% to 40% to remain. I passionately believe that the vote to leave the EU is fundamentally damaging to the future of Wales. I applaud the Members of Parliament from Wales who voted against the commencement of Article 50. They speak for me, and for a lot of other people.
After all, you don't give up on democracy because you lose an election. And you don't give up on your principles because you lose a referendum. As Ireland and Denmark have shown us, you argue. You organise. You persuade. You campaign.
People can change their minds. As Tony Blair said recently: "Our mission is to persuade them to do so."
Remember, if the vote had gone the other way on a similar margin, Nigel Farage and his ilk would be campaigning now for a second referendum.
After the referendum last year I argued for the outcome of the Brexit negotiations to be put to the people in a further vote. This would be a vote on whether the Brexit terms are better than the status quo of current membership.
That's a real choice, a meaningful choice.
It would be a vote on a concrete set of proposals, and people would have to decide whether that is better than what we have at present.It would force people to engage with the detail. It would force the media to interrogate the real choice on offer.
It would be an honest position, and a definitive position. If the people endorse the negotiated Brexit position, then we would be out.
If they didn't, then that would be an absolute recognition that the terms of departure are worse than what we have now.
Shouldn't the people be given a real choice on the actual outcome, not the dishonest promises and unfulfilled – and unfulfillable – fantasies of the Leave campaign?
After all, people can change their minds. In Wales, we've seen it happen before.
Leighton Andrews is professor of public service leadership and innovation at Cardiff Business School, a former minister in the Welsh Government and a former elected member of the National Assembly for Wales.


Multi-keystroke bindings are possible. If a sequence of one or more keystrokes partially matches a multi-keystroke binding, the keymap enters a pending state. The pending state is terminated on the next keystroke, or after ::partialMatchTimeout milliseconds has elapsed. When the pending state is terminated via a timeout or a keystroke that leads to no matches, the longest ambiguous bindings that caused the pending state are temporarily disabled and the previous keystrokes are replayed. If there is ambiguity again during the replay, the next longest bindings are disabled and the keystrokes are replayed again.


Got the tank in and need to get the 4 inch bulkhead in. What is the best way to install this? Dont use the gasket and pl inside and outside and tighten.
For 4 inch and flexable liner no bulkheads should be used. Use a flanged connector suitable for thin non-supported liner. Use a Koitoilet 4 inch or have Zac make you something with a wide flange suitable for flexable liner. Dreampond makes 1.5 to 3 inch ones.
Well I was in a pinch and used the bulkhead and gasket with no sealant. So far so good and had to transfer over the rdf and shower as well as 14 koi. Was a 2 day task after I got home from work..fish went a day with no filtration at all but i kept the shower going on the old pond and transferred media today. The shower quickly removed the ammonia in the pond that was treated with safe. I didnt test but I know there was ammonia present when the filter was off.


Lund Opsahl provided structural design services for this new 1.7-acre neighborhood park, part of a redevelopment effort from a low-income housing development to a combination of low-income housing, market rate housing, offices, and community spaces in this urban Seattle neighborhood. Structural elements include the design of concrete site retaining walls, underground mechanical bunker, and remodel work to the existing community center.
Elements include a lovely city view, a water feature, playground space, an artwork installation, and a terrace for farmer's markets. The park also includes a soccer and basketball courts, and open green space for the community. Construction is currently underway.


text:
Gerald Aben
Phyllis Adams
Jonathan Feld
Eric Klein
Kathrin Kudner
Donna O'Connor
Kathleen Reed
First Court Decision Defining "Identified" Overpayments Under the Medicare/Medicaid 60-Day Report and Return Rule
The United States District Court's decision in Kane v Healthfirst, 11 Civ. 2325 (ER) (August 3, 2015) is a reminder to the health care community that the Affordable Care Act of 2010 (ACA) strengthened the False Claims Act and provided the government with powerful tools to seek recovery. The Court acknowledged the "stringent" and "potentially unworkable" nature of the report and return rule, but nonetheless determined that Congress intended to place the onus on providers to quickly investigate, report, and return overpayments within 60 days.
The Significance of the 60-Day Report and Return Rule
The ACA amended the Social Security Act to require a person who has received a Medicare or Medicaid overpayment to report and return the overpayment to the appropriate government agency or contractor, along with the reason for the overpayment, within 60 days after the overpayment is identified. 42 USC 1320a-7k(d). Missing the 60-day deadline transforms an identified overpayment into a False Claims Act obligation, potentially leading to a reverse false claim action by the government or qui tam relators and triggering treble damages, civil money penalties and exclusion from federal health care programs.
In 2012, the Centers for Medicare and Medicaid Services (CMS) proposed regulations implementing the report and return rule, including a definition for "identified" overpayments, but delayed publication of the final rule until February 2016.
Kane v Healthfirst
The overpayment in Healthfirst arose from an insurance company computer error that mistakenly informed the hospitals that they could seek secondary payment from other payers following payment by New York's Medicaid managed care program. This error caused the hospitals' electronic billing systems to bill secondary payers, including New York's Medicaid fee-for-service program, which mistakenly paid the claims, resulting in double Medicaid payment.
The error was initially noticed by the Comptroller for the State of New York, who communicated it to the hospital network operator, Continuum, in September 2010. Continuum asked its employee, Robert Kane, to review the billing issue and identify claims subject to the computer error. In February 2011, Kane sent an email and a spreadsheet to Continuum's management containing 900 hospital claims that needed further analysis with potential liability of over $1 million. Continuum fired Kane four days later. Within two months he filed the qui tam action.
The Comptroller twice provided Continuum with additional claims that were improperly billed and paid, and Continuum reimbursed some claims to the government. However, the government alleged that Continuum did not act to reimburse the vast majority of claims until after the government issued a civil investigative demand in June 2012, and did not complete all reimbursements until March 2013. Half of the claims on Kane's original spreadsheet were properly paid, and half were found to be overpayments and returned to the government.
The government alleged that the overpaid claims were identified when Kane sent his email and spreadsheet to Continuum's management in February 2011, that the failure to report and return all overpayments within 60 days created a False Claims Act obligation, and that defendants knowingly avoided the requirement to report and return the overpayment in violation of the False Claims Act.
The Court agreed, finding that the overpayments did not have to be determined with certainty to be identified. Rather, it is sufficient that a person be put on notice of potential overpayments, after which the person must act quickly to confirm the overpayments and take appropriate action.
The Court also noted that while identified overpayments might qualify as False Claims Act obligations, an obligation in and of itself does not violate the False Claims Act. It is only when an obligation is knowingly concealed or knowingly and improperly avoided or decreased that False Claims Act liability arises. The Court suggested that prosecutorial discretion would counsel against enforcement actions aimed at "well-intentioned healthcare providers working with reasonable haste to address erroneous overpayments." The government's allegation that it took Continuum more than two years and the issuance of a civil investigative demand to complete their investigation and return the overpayments was key to the Court's conclusion that under the alleged facts the defendants acted with recklessness or in deliberate ignorance of whether overpayments were made.
Implications for Providers
The standard imposed by the False Claims Act for reporting and returning overpayments, as acknowledged by the Healthfirst Court, is an exacting standard with dire consequences for missteps. Providers often are inundated with claim and billing information, some of which in retrospect can be characterized as identified overpayments. Providers will need organized, well-designed and equally exacting processes for reviewing claim and billing information and responding quickly to reports of noncompliance, whether coming from inside or outside the organization.
Providers should update their billing and compliance processes now, rather than waiting for the proposed 60-day report and return rule to be finalized by CMS. Not only has the government demonstrated that strong enforcement action can be successful under existing statutes, but, as the Healthfirst Court itself observed, the proposed CMS rule is consistent with the Court's conclusions. Providers should take a critical and comprehensive look at their billing and compliance processes and move to create a streamlined process capable of investigating, reporting and returning potential overpayments within 60 days.
For more information, please contact Donna O'Connor at 248-203-0720, or at doconnor@dykema.com, or contact any of the attorneys in the Health Care Practice Group.


Kital enables businesses to create hybrid IP-PBX/PABX solutions through its suite of Uniphone telecommunication technologies. At the core of these integrated systems is a central switching system, a core engine that gives your people multiple ways to connect and collaborate.
The system acts as both your gatekeeper and gateway to the outside world. It gives you shared internal access to unified communications services, including VoIP, reporting, email, video conferencing, desktop sharing, instant messaging, SMS, and mobility via softphones.
The key productivity features of an IP-PBX/PABX solution are voicemail, auto attendant, conference bridging, intercom calling, parking, and paging. What's more is that this system is future-proof, meaning it continues to evolve in response to your company's needs. DIY telecom options are at hand, allowing you to customize how your phone systems operate.
VoIP-ready – Uniphone's next-generation hybrid IP-PBX/PABX system system has built-in VoIP features that can support several forms of communication. It connects your traditional telephones to the Public Switched Telephone Network (PSTN), but with IP connectivity. By deploying the system, you'll have access to VoIP capabilities. This means you'll enjoy free calls, unparalleled convenience, and the freedom to integrate your office phones with web applications and third-party technologies.
Unified Communications – Hybrid IP-PBX/PABX merges propriety and modern VoIP technologies. This opens you up to a feature-rich and diverse communications landscape that includes mobile telephony, instant messaging, SMS, email, and more. This feature will introduce new efficiencies to your business and help you streamline your operations.
Mobility – A hybrid IP-PBX/PABX system is the most convenient and cost-effective telecom solution for businesses with remote offices and mobile workers. You can remotely control your SIP phones so you can bring your people from multiple disparate locations.
Reporting – Transparency is a key feature of hybrid IP-PBX/PABX. It lets you generate basic call history and call cost reports and other insights. This way, you can measure your telecom spending and create better optimization and management decisions.
For your business to thrive in this fast-paced economy, you need a telecom infrastructure that meets your current and anticipates your future needs.
Make the switch with Kital. We'll amplify your communications with Uniphone technologies and our expertise.
Uniphone Asterisk Hybrid IP-PBX/PABX – this core engine offers total and seamless integration with your IP mobile phones, laptop with softphone, IP desk phones, VoIP gateway, and other hybrid PBX systems.
Uniphone Hybrid IP-PBX/PABX – enhance your existing PBX/PABX system with IP connectivity using this system. It can be configured to meet your exact needs and can expand as your needs grow or change.
UniphoneSmart IP Phones – Smart IP phones (mobile and fixed) integrate perfectly with your IP-PBX system. They are easy to use and have a long list of VoIP and PBX/PABX features.
Uniphone Built-in VoIP Card – ideal for multi-site offices and chain stores, this system lets users from disparate locations communicate with an internal extension. Your business gets secure, cost-effective, and spotless connectivity.
Uniphone enables you to build a system that you can continually upgrade as your business grows. The technology provides continuity and seamless integration with a wide variety of devices and software. Whatever innovation comes up, you can add it to your hybrid IP-PBX/PABX suite for an ever-richer telecom experience.
Uniphone's telecommunication suite is designed to empower businesses across the Philippines. By working with Kital, thesecutting-edge hybrid IP-PBX/PABX technologies can now be in your hands, transforming the way your people communicate and collaborate.
This versatile call system is capable of supporting up to 24 analog, digital, and IP phones, making itan ideal solution for small businesses with mobile workers. It comes with a built-in VoIP,voice mail, and ISDN-BRI interface and endless call optimization options, like LCR, callbarring, and remote programming.
Enjoy all the advanced features of the ISDK-26 but with a capacity of up to 82 users. This systemdelivers dependable andcost-effectiveanalog, digital, and IP telecommunications. It also has a uniform call distribution feature, which is perfect for businesses withmultiple departments, branches, and remote workers, as well as call center environments.
This call system carries all the high-end features of the UD-series, and so much more. It can support up to 162 users and is compatible with a wide rangeof analog, digital, and IP phones, as well as critical communication gateways. Your employees will enjoy endless call management and customization features and spotless voice/call quality.
Kital brings cutting-edge Uniphone technologies to transform businesses across Metro Manila and the Philippines. Whether you're looking for a turnkey solution for your small business or building a custom system from scratch, our technicians and project managers are with you all the way. Get in touch with us today.


text:
Q: How to install Ubuntu along side with Windows 8 in the legacy mode on a acer V5 laptop Earlier I have tried to install Ubuntu along side with a pre installed windows 8 under UEFI mode , but after repairing the boots I lost both the windows 8 and Ubuntu ,Now I uninstall pre installed windows and set the UEFI and Legacy and Managed to Install  Windows . Is there a special way to install Ubuntu on this way enabling dual boot without much configurations ? Thank you in advance

A: If you've installed Windows in BIOS/CSM/legacy mode, then you should be able to install Linux in BIOS/CSM/legacy mode just as you would on an older computer. The main caveat is that, if you used Windows to repartition the disk in MBR mode (rather than the GPT that it presumably used before), Windows will not have completely wiped the GPT data, and this will confuse the Ubuntu installer. To fix this problem, run my FixParts program on the disk. It will detect the stray GPT data and offer to wipe it out. You can run either the Windows version of FixParts or the Linux version from a live CD.
OTOH, if you've installed Windows in EFI/UEFI mode, you should read my page on Linux EFI-mode installations and the Ubuntu wiki on EFI. Between the two, you ought to be able to get it running, provided your firmware isn't one of the most badly broken ones.

redpajama_set_name: RedPajamaStackExchange


Everted rim; inner edge rounded on top, flattens towards outer edge forming ledge for lid; slightly concave below, offset from wall by groove.
Granular fabric is reddish-brown with frequent small light angular inclusions; rare dark circular inclusions. Interior and exterior surfaces are reddish-brown consistent with fabric; grey towards rim showing signs of burning or use.
One sherd preserves 5-10% of rim and small part of upper body. Slight abrasion and flaking around edges and surface; thin, sparse patches of encrustation.