text:
/* debugger.c: Glulxe debugger functions.
    Designed by Andrew Plotkin <erkyrath@eblong.com>
    http://eblong.com/zarf/glulx/index.html
*/

/* This module is a low-level debugger for Inform 6 (and 7) games.

   This code is only compiled in if the "#define VM_DEBUGGER" line in
   glulxe.h is uncommented. You will then have to compile with a Glk
   library that supports the debug feature. (See gi_debug.h as distributed
   with CheapGlk.) You will also need to link the libxml2 library.

   When debugging is compiled in, glulxe looks for a "Dbug" chunk in the
   Blorb file. This should contain the "gameinfo.dbg" file generated by
   the I6 compiler. (Must be the new XML-based debug format.) It also
   offers a bunch of new command-line options:

   --gameinfo filename: Read the "gameinfo.dbg" from an external file.
     (If you're not using Blorb or have not pulled the debug info
     into it.)
   --cpu: Display time and VM CPU cycles used for each input.
   --starttrap: Enter debug mode as soon as the game starts up. (Before
     any code has executed.)
   --quittrap: Enter debug mode when the game quits.
   --crashtrap: Enter debug mode if any fatal error occurs (including
     Glk library fatal errors).

   You do not need "gameinfo.dbg" data to use the debugger. However, that
   file contains the I6-level names of all functions and variables. So
   without it, you'll see a lot of "???" in the debug output.

   The debug model assumes that you have a "debug console" available, which
   can accept debug commands and display responses. (The implementation
   of this depends on the Glk library.) You can enter commands whenever
   the game is awaiting input, or if it pauses because of the --starttrap
   flag, the --crashtrap flag, a breakpoint, or a @debugtrap opcode.

   The debugger is currently pretty minimal. You can display the stack
   (including local variables), display global variables, and set
   breakpoints (only at the beginning of a function). Type "help" into
   the debug console for the command list.

   (If you've compiled with CheapGlk, then the "debug console" is just
   standard output. Enter a line beginning with "/" to send a debug
   command -- "/help", etc.) (This "/" convention is *only* used in
   CheapGlk. Other libraries will have debug interfaces that don't
   suck.)
*/

#include "glk.h"
#include "glulxe.h"

#if VM_DEBUGGER

#include <string.h>
#include <sys/time.h>
#include "gi_debug.h"
#include <libxml/xmlreader.h>

/* Data structures to store the debug info in memory. */

typedef enum grouptype_enum {
    grp_None = 0,
    grp_Constant = 1,
    grp_Attribute = 2,
    grp_Property = 3,
    grp_Routine = 4,
    grp_Global = 5,
    grp_Object = 6,
    grp_Array = 7,
} grouptype;

/* Used for constants, globals, locals, objects -- the meaning of the value
   field varies. */
typedef struct infoconstant_struct {
    const xmlChar *identifier;
    int32_t value;
} infoconstant;

typedef struct inforoutine_struct {
    const xmlChar *identifier;
    int32_t address;
    int32_t length;

    /* Address of the next higher function. May be beyond length if there
       are gaps. */
    int32_t nextaddress;

    /* The locals is a block of infoconstants where the value is
       frame-offset. We adopt Inform's assumption that locals are
       always 4 bytes long. */
    int32_t numlocals;
    infoconstant *locals;
} inforoutine;

typedef struct infoobject_struct {
    const xmlChar *identifier;
    int32_t address;

    /* Address of the next higher function. May be beyond length if there
       are gaps. */
    int32_t nextaddress;
} infoobject;

typedef struct infoarray_struct {
    const xmlChar *identifier;
    int32_t address;
    int32_t bytelength;
    int32_t bytesize; /* per element */
    int32_t count;
    int lengthfield; /* true if the zeroth element is the length */

    /* Address of the next higher function. May be beyond length if there
       are gaps. */
    int32_t nextaddress;
} infoarray;

typedef struct breakpoint_struct {
    inforoutine *func;
    int32_t address;
    struct breakpoint_struct *next; /* linked list of breakpoints */
} breakpoint;

typedef struct debuginfofile_struct {
    strid_t str;
    int32_t strread;
    int32_t strreadmax;
    int failed;

    grouptype curgrouptype;

    int tempcounter;
    infoconstant *tempconstant;
    inforoutine *temproutine;
    infoarray *temparray;
    infoobject *tempobject;
    int tempnumlocals;
    int templocalssize;
    infoconstant *templocals;

    const xmlChar *storyfileprefix;
    xmlHashTablePtr constants;
    xmlHashTablePtr attributes;
    xmlHashTablePtr properties;
    xmlHashTablePtr globals;
    xmlHashTablePtr objects;
    int numobjects;
    infoobject **objectlist; /* array, ordered by address */
    xmlHashTablePtr arrays;
    int numarrays;
    infoarray **arraylist; /* array, ordered by address */
    xmlHashTablePtr routines;
    int numroutines;
    inforoutine **routinelist; /* array, ordered by address */
} debuginfofile;

/* This global holds the loaded debug info, if we have any. */
static debuginfofile *debuginfo = NULL;

/* Lists of breakpoints. */
static breakpoint *funcbreakpoints = NULL; /* linked list */

/* Internal functions used while loading the debug info. */

static int xmlreadstreamfunc(void *rock, char *buffer, int len);
static int xmlreadchunkfunc(void *rock, char *buffer, int len);
static int xmlclosefunc(void *rock);
static void xmlhandlenode(xmlTextReaderPtr reader, debuginfofile *context);
static int finalize_debuginfo(debuginfofile *context);

static debuginfofile *create_debuginfofile()
{
    debuginfofile *context = (debuginfofile *)malloc(sizeof(debuginfofile));
    context->str = NULL;
    context->failed = 0;
    context->tempconstant = NULL;
    context->temparray = NULL;
    context->tempobject = NULL;
    context->temproutine = NULL;
    context->tempnumlocals = 0;
    context->templocals = NULL;
    context->templocalssize = 0;
    context->curgrouptype = grp_None;
    context->constants = xmlHashCreate(16);
    context->attributes = xmlHashCreate(16);
    context->properties = xmlHashCreate(16);
    context->globals = xmlHashCreate(16);
    context->objects = xmlHashCreate(16);
    context->numobjects = 0;
    context->objectlist = NULL;
    context->arrays = xmlHashCreate(16);
    context->numarrays = 0;
    context->arraylist = NULL;
    context->routines = xmlHashCreate(16);
    context->numroutines = 0;
    context->routinelist = NULL;
    context->storyfileprefix = NULL;

    return context;
}

static void free_debuginfofile(debuginfofile *context)
{
    if (!context)
        return;

    context->str = NULL;
    context->tempconstant = NULL;
    context->temparray = NULL;
    context->tempobject = NULL;
    context->temproutine = NULL;

    /* We don't bother to free the member structures, because this
       only happens once at startup and then only on error
       conditions. */

    if (context->constants) {
        xmlHashFree(context->constants, NULL);
        context->constants = NULL;
    }

    if (context->attributes) {
        xmlHashFree(context->attributes, NULL);
        context->attributes = NULL;
    }

    if (context->properties) {
        xmlHashFree(context->properties, NULL);
        context->properties = NULL;
    }

    if (context->globals) {
        xmlHashFree(context->globals, NULL);
        context->globals = NULL;
    }

    if (context->objects) {
        xmlHashFree(context->objects, NULL);
        context->objects = NULL;
    }

    if (context->objectlist) {
        free(context->objectlist);
        context->objectlist = NULL;
    }

    if (context->arrays) {
        xmlHashFree(context->arrays, NULL);
        context->arrays = NULL;
    }

    if (context->arraylist) {
        free(context->arraylist);
        context->arraylist = NULL;
    }

    if (context->routines) {
        xmlHashFree(context->routines, NULL);
        context->routines = NULL;
    }

    if (context->routinelist) {
        free(context->routinelist);
        context->routinelist = NULL;
    }

    free(context);
}

static infoconstant *create_infoconstant()
{
    infoconstant *cons = (infoconstant *)malloc(sizeof(infoconstant));
    cons->identifier = NULL;
    cons->value = 0;
    return cons;
}

static infoobject *create_infoobject()
{
    infoobject *object = (infoobject *)malloc(sizeof(infoobject));
    object->identifier = NULL;
    object->address = 0;
    object->nextaddress = 0;
    return object;
}

static infoarray *create_infoarray()
{
    infoarray *arr = (infoarray *)malloc(sizeof(infoarray));
    arr->identifier = NULL;
    arr->address = 0;
    arr->bytelength = 0;
    arr->bytesize = 1;
    arr->count = 0;
    arr->lengthfield = 0;
    arr->nextaddress = 0;
    return arr;
}

static inforoutine *create_inforoutine()
{
    inforoutine *func = (inforoutine *)malloc(sizeof(inforoutine));
    func->identifier = NULL;
    func->address = 0;
    func->length = 0;
    func->nextaddress = 0;
    func->numlocals = 0;
    func->locals = NULL;
    return func;
}

static breakpoint *create_breakpoint(glui32 addr)
{
    breakpoint *bp = (breakpoint *)malloc(sizeof(breakpoint));
    bp->func = NULL; /* useful? */
    bp->address = addr;
    bp->next = NULL;
    return bp;
}

static void add_object_to_table(void *obj, void *rock, xmlChar *name)
{
    debuginfofile *context = rock;
    infoobject *object = obj;

    if (context->tempcounter >= context->numobjects) {
        printf("### object overflow!\n"); /*###*/
        return;
    }

    context->objectlist[context->tempcounter++] = object;
}

static int sort_objects_table(const void *obj1, const void *obj2)
{
    infoobject **object1 = (infoobject **)obj1;
    infoobject **object2 = (infoobject **)obj2;

    return ((*object1)->address - (*object2)->address);
}

static int find_object_in_table(const void *keyptr, const void *obj)
{
    /* Binary-search callback. We rely on address and nextaddress so
       that there are no gaps. */

    glui32 addr = *(glui32 *)(keyptr);
    infoobject **object = (infoobject **)obj;

    if (addr < (*object)->address)
        return -1;
    if (addr >= (*object)->nextaddress)
        return 1;
    return 0;
}

static void add_array_to_table(void *obj, void *rock, xmlChar *name)
{
    debuginfofile *context = rock;
    infoarray *array = obj;

    if (context->tempcounter >= context->numarrays) {
        printf("### array overflow!\n"); /*###*/
        return;
    }

    context->arraylist[context->tempcounter++] = array;
}

static int sort_arrays_table(const void *obj1, const void *obj2)
{
    infoarray **array1 = (infoarray **)obj1;
    infoarray **array2 = (infoarray **)obj2;

    return ((*array1)->address - (*array2)->address);
}

static int find_array_in_table(const void *keyptr, const void *obj)
{
    /* Binary-search callback. We rely on address and nextaddress so
       that there are no gaps. */

    glui32 addr = *(glui32 *)(keyptr);
    infoarray **array = (infoarray **)obj;

    if (addr < (*array)->address)
        return -1;
    if (addr >= (*array)->nextaddress)
        return 1;
    return 0;
}

static void add_routine_to_table(void *obj, void *rock, xmlChar *name)
{
    debuginfofile *context = rock;
    inforoutine *routine = obj;

    if (context->tempcounter >= context->numroutines) {
        printf("### array overflow!\n"); /*###*/
        return;
    }

    context->routinelist[context->tempcounter++] = routine;
}

static int sort_routines_table(const void *obj1, const void *obj2)
{
    inforoutine **routine1 = (inforoutine **)obj1;
    inforoutine **routine2 = (inforoutine **)obj2;

    return ((*routine1)->address - (*routine2)->address);
}

static int find_routine_in_table(const void *keyptr, const void *obj)
{
    /* Binary-search callback. We rely on address and nextaddress so
       that there are no gaps. */

    glui32 addr = *(glui32 *)(keyptr);
    inforoutine **routine = (inforoutine **)obj;

    if (addr < (*routine)->address)
        return -1;
    if (addr >= (*routine)->nextaddress)
        return 1;
    return 0;
}

/* Top-level function for loading debug info from a Glk stream.
   The debug data must take up the entire file; this will read until EOF.
   If successful, fills out the debuginfo global and returns true.
   If not, reports an error and returns false.
   (The stream will not be closed.)
 */
int debugger_load_info_stream(strid_t stream)
{
    debuginfofile *context = create_debuginfofile();
    context->str = stream;
    context->strread = 0; /* not used */
    context->strreadmax = 0; /* not used */

    xmlTextReaderPtr reader;
    reader = xmlReaderForIO(xmlreadstreamfunc, xmlclosefunc, context,
        NULL, NULL,
        XML_PARSE_RECOVER|XML_PARSE_NOENT|XML_PARSE_NONET|XML_PARSE_NOCDATA|XML_PARSE_COMPACT);
    if (!reader) {
        printf("Error: Unable to create XML reader.\n"); /*###*/
        free_debuginfofile(context);
        return 0;
    }

    while (1) {
        int res = xmlTextReaderRead(reader);
        if (res < 0) {
            context->failed = 1;
            break; /* error */
        }
        if (res == 0) {
            break; /* EOF */
        }
        xmlhandlenode(reader, context);
    }

    xmlFreeTextReader(reader);
    context->str = NULL; /* the reader didn't close it, but we're done with it. */

    if (context->failed) {
        printf("Error: Unable to load debug info.\n"); /*###*/
        free_debuginfofile(context);
        return 0;
    }

    /* Now that all the data is loaded in, we go through and create some
       indexes that will be handy. */
    return finalize_debuginfo(context);
}

/* Top-level function for loading debug info from a segment of a Glk stream.
   This starts at position pos in the file and reads len bytes.
   If successful, fills out the debuginfo global and returns true.
   If not, reports an error and returns false.
   (The stream will not be closed.)
 */
int debugger_load_info_chunk(strid_t stream, glui32 pos, glui32 len)
{
    debuginfofile *context = create_debuginfofile();
    context->str = stream;
    context->strread = 0;
    context->strreadmax = len;

    glk_stream_set_position(stream, pos, seekmode_Start);

    xmlTextReaderPtr reader;
    reader = xmlReaderForIO(xmlreadchunkfunc, xmlclosefunc, context,
        NULL, NULL,
        XML_PARSE_RECOVER|XML_PARSE_NOENT|XML_PARSE_NONET|XML_PARSE_NOCDATA|XML_PARSE_COMPACT);
    if (!reader) {
        printf("Error: Unable to create XML reader.\n"); /*###*/
        free_debuginfofile(context);
        return 0;
    }

    while (1) {
        int res = xmlTextReaderRead(reader);
        if (res < 0) {
            context->failed = 1;
            break; /* error */
        }
        if (res == 0) {
            break; /* EOF */
        }
        xmlhandlenode(reader, context);
    }

    xmlFreeTextReader(reader);
    context->str = NULL; /* the reader didn't close it, but we're done with it. */

    if (context->failed) {
        printf("Error: Unable to load debug info.\n"); /*###*/
        free_debuginfofile(context);
        return 0;
    }

    /* Now that all the data is loaded in, we go through and create some
       indexes that will be handy. */
    return finalize_debuginfo(context);
}

/* xmlReader callback to read from a stream (until EOF). */
static int xmlreadstreamfunc(void *rock, char *buffer, int len)
{
    debuginfofile *context = rock;
    int res = glk_get_buffer_stream(context->str, buffer, len);
    if (res < 0)
        return -1;
    return res;
}

/* xmlReader callback to read from a stream (until a given position). */
static int xmlreadchunkfunc(void *rock, char *buffer, int len)
{
    debuginfofile *context = rock;
    if (context->strread >= context->strreadmax)
        return 0;

    if (len > context->strreadmax - context->strread)
        len = context->strreadmax - context->strread;
    int res = glk_get_buffer_stream(context->str, buffer, len);
    if (res < 0)
        return -1;
    context->strread += res;
    return res;
}

/* xmlReader callback to stop reading a stream. We don't actually close
   the stream here, just discard the reference. */
static int xmlclosefunc(void *rock)
{
    debuginfofile *context = rock;
    context->str = NULL;
    return 0;
}

/* xmlReader callback: an XML node has arrived. (Might be the beginning of
   a tag, the end of a tag, or a block of text.)

   All the work of parsing the debug format happens here, which is why this
   function is big and ugly.
*/
static void xmlhandlenode(xmlTextReaderPtr reader, debuginfofile *context)
{
    int depth = xmlTextReaderDepth(reader);
    int nodetype = xmlTextReaderNodeType(reader);

    if (depth == 0) {
        if (nodetype == XML_ELEMENT_NODE) {
            const xmlChar *name = xmlTextReaderConstName(reader);
            if (xmlStrcmp(name, BAD_CAST "inform-story-file")) {
                printf("Error: This is not an Inform debug info file.\n"); /*###*/
                context->failed = 1;
            }
        }
        else if (nodetype == XML_ELEMENT_DECL) {
            /* End of document */
            context->curgrouptype = grp_None;
            context->tempconstant = NULL;
            context->temparray = NULL;
            context->tempobject = NULL;
            context->temproutine = NULL;
        }
    }
    else if (depth == 1) {
        if (nodetype == XML_ELEMENT_NODE) {
            const xmlChar *name = xmlTextReaderConstName(reader);
            if (!xmlStrcmp(name, BAD_CAST "constant")) {
                context->curgrouptype = grp_Constant;
                context->tempconstant = create_infoconstant();
            }
            else if (!xmlStrcmp(name, BAD_CAST "attribute")) {
                context->curgrouptype = grp_Attribute;
                context->tempconstant = create_infoconstant();
            }
            else if (!xmlStrcmp(name, BAD_CAST "property")) {
                context->curgrouptype = grp_Property;
                context->tempconstant = create_infoconstant();
            }
            else if (!xmlStrcmp(name, BAD_CAST "routine")) {
                context->curgrouptype = grp_Routine;
                context->temproutine = create_inforoutine();
                context->tempnumlocals = 0;
            }
            else if (!xmlStrcmp(name, BAD_CAST "global-variable")) {
                context->curgrouptype = grp_Global;
                context->tempconstant = create_infoconstant();
            }
            else if (!xmlStrcmp(name, BAD_CAST "object")) {
                context->curgrouptype = grp_Object;
                context->tempobject = create_infoobject();
            }
            else if (!xmlStrcmp(name, BAD_CAST "array")) {
                context->curgrouptype = grp_Array;
                context->temparray = create_infoarray();
            }
            else if (!xmlStrcmp(name, BAD_CAST "story-file-prefix")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    context->storyfileprefix = xmlStrdup(nod->children->content);
                }
            }
            else {
                context->curgrouptype = grp_None;
            }
        }
        else if (nodetype == XML_ELEMENT_DECL) {
            /* End of group */
            switch (context->curgrouptype) {
            case grp_Constant:
                if (context->tempconstant) {
                    infoconstant *dat = context->tempconstant;
                    context->tempconstant = NULL;
                    xmlHashAddEntry(context->constants, dat->identifier, dat);
                }
                break;
            case grp_Attribute:
                if (context->tempconstant) {
                    infoconstant *dat = context->tempconstant;
                    context->tempconstant = NULL;
                    xmlHashAddEntry(context->attributes, dat->identifier, dat);
                }
                break;
            case grp_Property:
                if (context->tempconstant) {
                    infoconstant *dat = context->tempconstant;
                    context->tempconstant = NULL;
                    xmlHashAddEntry(context->properties, dat->identifier, dat);
                }
                break;
            case grp_Global:
                if (context->tempconstant) {
                    infoconstant *dat = context->tempconstant;
                    context->tempconstant = NULL;
                    xmlHashAddEntry(context->globals, dat->identifier, dat);
                }
                break;
            case grp_Object:
                if (context->tempobject) {
                    infoobject *dat = context->tempobject;
                    context->tempobject = NULL;
                    xmlHashAddEntry(context->objects, dat->identifier, dat);
                }
                break;
            case grp_Array:
                if (context->temparray) {
                    infoarray *dat = context->temparray;
                    context->temparray = NULL;
                    dat->count = dat->bytelength / dat->bytesize;
                    xmlHashAddEntry(context->arrays, dat->identifier, dat);
                }
                break;
            case grp_Routine:
                if (context->temproutine) {
                    inforoutine *dat = context->temproutine;
                    context->temproutine = NULL;
                    /* Copy the list of locals into the inforoutine structure.
                       This loop totally assumes that locals are found
                       in order in the debug file! */
                    if (context->tempnumlocals && context->templocals) {
                        int ix;
                        dat->numlocals = context->tempnumlocals;
                        dat->locals = (infoconstant *)malloc(context->tempnumlocals * sizeof(infoconstant));
                        for (ix=0; ix<context->tempnumlocals; ix++) {
                            dat->locals[ix].identifier = context->templocals[ix].identifier;
                            dat->locals[ix].value = context->templocals[ix].value;
                            context->templocals[ix].identifier = NULL;
                            context->templocals[ix].value = 0;
                        }
                    }
                    context->tempnumlocals = 0;
                    /* An address of 0 is impossible. Inform uses this to
                       indicate a routine that was eliminated because it was
                       never called. */
                    if (dat->address != 0) {
                        xmlHashAddEntry(context->routines, dat->identifier, dat);
                    }
                }
                break;
            default:
                break;
            }
            context->curgrouptype = grp_None;
        }
    }
    else {
        if (nodetype == XML_ELEMENT_NODE) {
            const xmlChar *name = xmlTextReaderConstName(reader);
            /* These fields are always simple text nodes. */
            if (!xmlStrcmp(name, BAD_CAST "identifier")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    xmlChar *text = nod->children->content;
                    if (context->curgrouptype == grp_Constant
                        || context->curgrouptype == grp_Attribute
                        || context->curgrouptype == grp_Property
                        || context->curgrouptype == grp_Global) {
                        if (context->tempconstant) {
                            if (depth == 2)
                                context->tempconstant->identifier = xmlStrdup(text);
                        }
                    }
                    else if (context->curgrouptype == grp_Object) {
                        if (context->tempobject) {
                            if (depth == 2)
                                context->tempobject->identifier = xmlStrdup(text);
                        }
                    }
                    else if (context->curgrouptype == grp_Array) {
                        if (context->temparray) {
                            if (depth == 2)
                                context->temparray->identifier = xmlStrdup(text);
                        }
                    }
                    else if (context->curgrouptype == grp_Routine) {
                        if (context->temproutine) {
                            if (depth == 2)
                                context->temproutine->identifier = xmlStrdup(text);
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "value")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    if (context->curgrouptype == grp_Constant
                        || context->curgrouptype == grp_Attribute
                        || context->curgrouptype == grp_Property) {
                        if (context->tempconstant) {
                            if (depth == 2)
                                context->tempconstant->value = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                    else if (context->curgrouptype == grp_Object) {
                        if (context->tempobject) {
                            if (depth == 2)
                                context->tempobject->address = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                    else if (context->curgrouptype == grp_Array) {
                        if (context->temparray) {
                            if (depth == 2)
                                context->temparray->address = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                    else if (context->curgrouptype == grp_Routine) {
                        if (context->temproutine) {
                            if (depth == 2)
                                context->temproutine->address = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "address")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    if (context->curgrouptype == grp_Global) {
                        if (context->tempconstant) {
                            if (depth == 2)
                                context->tempconstant->value = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "byte-count")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    if (context->curgrouptype == grp_Routine) {
                        if (context->temproutine) {
                            if (depth == 2)
                                context->temproutine->length = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                    else if (context->curgrouptype == grp_Array) {
                        if (context->temparray) {
                            if (depth == 2)
                                context->temparray->bytelength = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "bytes-per-element")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    if (context->curgrouptype == grp_Array) {
                        if (context->temparray) {
                            if (depth == 2)
                                context->temparray->bytesize = strtol((char *)nod->children->content, NULL, 10);
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "zeroth-element-holds-length")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod && nod->children && nod->children->type == XML_TEXT_NODE) {
                    if (context->curgrouptype == grp_Array) {
                        if (context->temparray) {
                            if (depth == 2)
                                if (!xmlStrcmp(nod->children->content, BAD_CAST "true"))
                                    context->temparray->lengthfield = 1;
                        }
                    }
                }
            }
            else if (!xmlStrcmp(name, BAD_CAST "local-variable")) {
                xmlNodePtr nod = xmlTextReaderExpand(reader);
                if (nod) {
                    if (!context->templocals) {
                        context->templocalssize = 8;
                        context->templocals = (infoconstant *)malloc(context->templocalssize * sizeof(infoconstant));
                    }
                    if (context->tempnumlocals >= context->templocalssize) {
                        context->templocalssize = 2*context->tempnumlocals + 4;
                        context->templocals = (infoconstant *)realloc(context->templocals, context->templocalssize * sizeof(infoconstant));
                    }
                    infoconstant *templocal = &(context->templocals[context->tempnumlocals]);
                    context->tempnumlocals += 1;
                    for (nod = nod->children; nod; nod=nod->next) {
                        if (nod->type == XML_ELEMENT_NODE) {
                            if (!xmlStrcmp(nod->name, BAD_CAST "identifier") && nod->children && nod->children->type == XML_TEXT_NODE) {
                                templocal->identifier = xmlStrdup(nod->children->content);
                            }
                            if (!xmlStrcmp(nod->name, BAD_CAST "frame-offset") && nod->children && nod->children->type == XML_TEXT_NODE) {
                                templocal->value = strtol((char *)nod->children->content, NULL, 10);
                            }
                        }
                    }
                }
            }
        }
    }
}

/* This is called after the XML data is parsed. We wrap up what we've
   found and store it in the debuginfo global. Returns 1 on success.
*/
static int finalize_debuginfo(debuginfofile *context)
{
    int ix;

    context->numarrays = xmlHashSize(context->arrays);
    context->arraylist = (infoarray **)malloc(context->numarrays * sizeof(infoarray *));

    context->tempcounter = 0;
    xmlHashScan(context->arrays, add_array_to_table, context);
    if (context->tempcounter != context->numarrays)
        printf("### array underflow!\n"); /*###*/
    qsort(context->arraylist, context->numarrays, sizeof(infoarray *), sort_arrays_table);

    for (ix=0; ix<context->numarrays; ix++) {
        if (ix+1 < context->numarrays) {
            context->arraylist[ix]->nextaddress = context->arraylist[ix+1]->address;
        }
        else {
            context->arraylist[ix]->nextaddress = context->arraylist[ix]->address + context->arraylist[ix]->bytelength;
        }
    }

    context->numobjects = xmlHashSize(context->objects);
    context->objectlist = (infoobject **)malloc(context->numobjects * sizeof(infoobject *));

    context->tempcounter = 0;
    xmlHashScan(context->objects, add_object_to_table, context);
    if (context->tempcounter != context->numobjects)
        printf("### object underflow!\n"); /*###*/
    qsort(context->objectlist, context->numobjects, sizeof(infoobject *), sort_objects_table);

    for (ix=0; ix<context->numobjects; ix++) {
        if (ix+1 < context->numobjects) {
            context->objectlist[ix]->nextaddress = context->objectlist[ix+1]->address;
        }
        else {
            context->objectlist[ix]->nextaddress = context->objectlist[ix]->address + 1;
        }
    }

    context->numroutines = xmlHashSize(context->routines);
    context->routinelist = (inforoutine **)malloc(context->numroutines * sizeof(inforoutine *));

    context->tempcounter = 0;
    xmlHashScan(context->routines, add_routine_to_table, context);
    if (context->tempcounter != context->numroutines)
        printf("### array underflow!\n"); /*###*/
    qsort(context->routinelist, context->numroutines, sizeof(inforoutine *), sort_routines_table);

    for (ix=0; ix<context->numroutines; ix++) {
        if (ix+1 < context->numroutines) {
            context->routinelist[ix]->nextaddress = context->routinelist[ix+1]->address;
        }
        else {
            context->routinelist[ix]->nextaddress = context->routinelist[ix]->address + context->routinelist[ix]->length;
        }
    }

    if (context->templocals) {
        free(context->templocals);
        context->templocals = NULL;
    }
    context->templocalssize = 0;
    context->tempnumlocals = 0;

    /* Install into global. */
    debuginfo = context;
    return 1;
}

/* Compare main memory to the story-file-prefix we found. If it doesn't
   match, display a warning.
*/
void debugger_check_story_file()
{
    if (!debuginfo || !debuginfo->storyfileprefix)
        return;

    /* Check that this looks like an Inform 6 game file. See the
       Glulx Inform Technical Reference. */
    if (Mem4(0x24) != 0x496E666F) { /* 'Info' */
        gidebug_output("Warning: This game file does not look like it was generated by Inform.");
    }

    /* Decode the storyfileprefix, which is in base64. */

    const unsigned char *cx;
    int pos = 0;
    int count = 0;
    uint32_t word = 0;
    int fail = FALSE;

    for (cx = debuginfo->storyfileprefix; *cx && *cx != '='; cx++) {
        unsigned int sixbit = 0;
        if (*cx >= 'A' && *cx <= 'Z')
            sixbit = (*cx) - 'A';
        else if (*cx >= 'a' && *cx <= 'z')
            sixbit = ((*cx) - 'a') + 26;
        else if (*cx >= '0' && *cx <= '9')
            sixbit = ((*cx) - '0') + 52;
        else if (*cx == '+')
            sixbit = 62;
        else if (*cx == '/')
            sixbit = 63;
        else
            sixbit = 0;

        switch (count) {
        case 0:
            word = (sixbit << 18);
            break;
        case 1:
            word |= (sixbit << 12);
            break;
        case 2:
            word |= (sixbit << 6);
            break;
        case 3:
            word |= (sixbit);
            break;
        }

        count++;
        if (count == 4) {
            unsigned char byte;
            byte = (word >> 16) & 0xFF;
            if (byte != Mem1(pos))
                fail = TRUE;
            byte = (word >> 8) & 0xFF;
            if (byte != Mem1(pos+1))
                fail = TRUE;
            byte = (word) & 0xFF;
            if (byte != Mem1(pos+2))
                fail = TRUE;
            pos += 3;
            count = 0;
        }
    }

    if (fail)
        gidebug_output("Warning: debug info <story-file-prefix> does not match this game file.");
}

/* The rest of this file is the debugger itself. */

static char *linebuf = NULL;
static int linebufsize = 0;

/* Expand the output line buffer to a given length, if necessary.
   If you want to write an N-character string, call ensure_line_buf(N).
   Then use snprintf() just to be safe. */
static void ensure_line_buf(int len)
{
    len += 1; /* for the closing null */
    if (linebuf && len <= linebufsize)
        return;

    linebufsize = linebufsize*2+16;
    if (linebufsize < len)
        linebufsize = len+16;

    if (!linebuf)
        linebuf = malloc(linebufsize * sizeof(char));
    else
        linebuf = realloc(linebuf, linebufsize * sizeof(char));
}

static int track_cpu = FALSE;
static int start_trap = FALSE;
static int crash_trap = FALSE;
static int quit_trap = FALSE;
static int debugger_invoked = FALSE; /* true if cycle handler called */
unsigned long debugger_opcount = 0; /* incremented in exec.c */
static struct timeval debugger_timer;

/* Set the track-CPU flag. (In fact we always track the VM CPU usage.
   This flag determines whether we report it to the debug console.)
*/
void debugger_track_cpu(int flag)
{
    track_cpu = flag;
}

/* Set the block-on-startup flag.
*/
void debugger_set_start_trap(int flag)
{
    start_trap = flag;
}

/* Set the block-on-quit flag.
*/
void debugger_set_quit_trap(int flag)
{
    quit_trap = flag;
}

/* Set the block-on-crash flag.
*/
void debugger_set_crash_trap(int flag)
{
    crash_trap = flag;
}

/* Has the library activated debugger features? If not, we assume
   that the debug console is entirely hidden from the user.
*/
int debugger_ever_invoked()
{
    return debugger_invoked;
}

/* Returns 1 if this is a (hex or decimal) numeric constant; 0 if not;
   -1 if it looks like a constant but is malformed.

   On 1, the number itself is returned in res.
*/
static int parse_numeric_constant(char *arg, glui32 *res)
{
    if (arg[0] == '$') {
        char *cx;
        glui32 val = 0;
        for (cx=arg+1; *cx; cx++) {
            if (*cx >= '0' && *cx <= '9') {
                val = 16 * val + (*cx - '0');
                continue;
            }
            if (*cx >= 'A' && *cx <= 'F') {
                val = 16 * val + (*cx - 'A' + 10);
                continue;
            }
            if (*cx >= 'a' && *cx <= 'f') {
                val = 16 * val + (*cx - 'a' + 10);
                continue;
            }
            snprintf(linebuf, linebufsize, "Invalid hex number");
            gidebug_output(linebuf);
            return -1;
        }
        *res = val;
        return 1;
    }

    if (arg[0] >= '0' && arg[0] <= '9') {
        char *cx;
        glui32 val = 0;
        for (cx=arg; *cx; cx++) {
            if (*cx >= '0' && *cx <= '9') {
                val = 10 * val + (*cx - '0');
                continue;
            }
            snprintf(linebuf, linebufsize, "Invalid number");
            gidebug_output(linebuf);
            return -1;
        }
        *res = val;
        return 1;
    }

    return 0;
}

static infoarray *find_array_for_address(glui32 addr)
{
    if (!debuginfo)
        return NULL;

    infoarray **res = bsearch(&addr, debuginfo->arraylist, debuginfo->numarrays, sizeof(infoarray *), find_array_in_table);
    if (!res)
        return NULL;

    infoarray *arr = *res;
    if (addr < arr->address || addr >= arr->address + arr->bytelength)
        return NULL;

    return arr;
}

static infoobject *find_object_for_address(glui32 addr)
{
    if (!debuginfo)
        return NULL;

    infoobject **res = bsearch(&addr, debuginfo->objectlist, debuginfo->numobjects, sizeof(infoobject *), find_object_in_table);
    if (!res)
        return NULL;

    infoobject *arr = *res;
    if (addr != arr->address)
        return NULL;

    return arr;
}

static inforoutine *find_routine_for_address(glui32 addr)
{
    if (!debuginfo)
        return NULL;

    inforoutine **res = bsearch(&addr, debuginfo->routinelist, debuginfo->numroutines, sizeof(inforoutine *), find_routine_in_table);
    if (!res)
        return NULL;

    inforoutine *func = *res;
    if (addr < func->address || addr >= func->address + func->length)
        return NULL;

    return func;
}

static void render_value_linebuf(glui32 val)
{
    int tmplen;

    /* Special case for single-digit numbers: display the decimal
       digit and stop. */
    if (val < 10) {
        tmplen = strlen(linebuf);
        ensure_line_buf(tmplen+4);
        snprintf(linebuf+tmplen, linebufsize-tmplen, "%d", val);
        return;
    }

    /* Always display the signed decimal and unsigned hex. */
    tmplen = strlen(linebuf);
    ensure_line_buf(tmplen+40);
    if (val <= 0x7FFFFFFF)
        snprintf(linebuf+tmplen, linebufsize-tmplen, "%d ($%X)", val, val);
    else
        snprintf(linebuf+tmplen, linebufsize-tmplen, "%d ($%X)", -(int)(0x100000000-val), val);

    /* If the address of a function, display it. (But not addresses in
       the middle of a function.) */
    inforoutine *func = find_routine_for_address(val);
    if (func) {
        if (val == func->address) {
            tmplen = strlen(linebuf);
            ensure_line_buf(tmplen+40);
            snprintf(linebuf+tmplen, linebufsize-tmplen, ", %s()", func->identifier);
        }
    }

    /* If the address of an array, display it. */
    infoarray *arr = find_array_for_address(val);
    if (arr) {
        if (val == arr->address) {
            tmplen = strlen(linebuf);
            ensure_line_buf(tmplen+40);
            snprintf(linebuf+tmplen, linebufsize-tmplen, ", %s[%d]", arr->identifier, arr->count);
        }
    }

    /* If the address of an object, display it. */
    infoobject *object = find_object_for_address(val);
    if (object) {
        if (val == object->address) {
            tmplen = strlen(linebuf);
            ensure_line_buf(tmplen+40);
            snprintf(linebuf+tmplen, linebufsize-tmplen, ", %s", object->identifier);
        }
    }
}

static void debugcmd_backtrace(int wholestack)
{
    if (stack) {
        ensure_line_buf(256);
        glui32 curpc = pc;
        glui32 curframeptr = frameptr;
        glui32 curstackptr = stackptr;
        glui32 curvalstackbase = valstackbase;
        glui32 curlocalsbase = localsbase;
        glui32 locptr;
        int locnum;
        while (1) {
            inforoutine *routine = find_routine_for_address(curpc);
            if (!routine)
                snprintf(linebuf, linebufsize, "- %s()  (pc=$%.2X)", "???", curpc);
            else
                snprintf(linebuf, linebufsize, "- %s()  (pc=$%.2X)", routine->identifier, curpc);
            gidebug_output(linebuf);

            strcpy(linebuf, "   ");
            /* Again, this loop assumes that all locals are 4 bytes. */
            for (locptr = curlocalsbase, locnum = 0; locptr < curvalstackbase; locptr += 4, locnum++) {
                int tmplen = strlen(linebuf);
                ensure_line_buf(tmplen+32);
                if (!routine || !routine->locals || locnum >= routine->numlocals)
                    snprintf(linebuf+tmplen, linebufsize-tmplen, "%sloc#%d=", (locnum?"; ":""), locnum);
                else
                    snprintf(linebuf+tmplen, linebufsize-tmplen, "%s%s=", (locnum?"; ":""), routine->locals[locnum].identifier);

                glui32 val = Stk4(locptr);
                render_value_linebuf(val);
            }
            if (!locnum) {
                strcat(linebuf, "(no locals)");
            }
            gidebug_output(linebuf);

            if (!wholestack)
                break;

            curstackptr = curframeptr;
            if (curstackptr < 16)
                break;
            curstackptr -= 16;
            glui32 newframeptr = Stk4(curstackptr+12);
            glui32 newpc = Stk4(curstackptr+8);
            curframeptr = newframeptr;
            curpc = newpc;
            curvalstackbase = curframeptr + Stk4(curframeptr);
            curlocalsbase = curframeptr + Stk4(curframeptr+4);
        }
    }
}

static void debugcmd_set_breakpoint(char *arg)
{
    while (*arg == ' ')
        arg++;

    if (*arg == '\0') {
        gidebug_output("What function do you want to set a breakpoint at?");
        return;
    }

    int found = FALSE;
    glui32 addr = 0;

    if (!found) {
        int res = parse_numeric_constant(arg, &addr);
        if (res < 0)
            return;
        if (res > 0) {
            found = TRUE;
            /* If possible, check whether this looks like a function
               address. But if it doesn't, just print a warning. */
            if (debuginfo) {
                inforoutine *routine = find_routine_for_address(addr);
                if (!routine || routine->address != addr) {
                    ensure_line_buf(128);
                    strcpy(linebuf, "This does not look like a function address: ");
                    render_value_linebuf(addr);
                    gidebug_output(linebuf);
                }
            }
        }
    }

    if (!found) {
        if (!debuginfo) {
            gidebug_output("No debug info; cannot look up functions by name");
            return;
        }
        inforoutine *routine = xmlHashLookup(debuginfo->routines, BAD_CAST arg);
        if (!routine) {
            ensure_line_buf(128);
            snprintf(linebuf, linebufsize, "Not a function name: %s", arg);
            gidebug_output(linebuf);
            return;
        }
        found = TRUE;
        addr = routine->address;
    }

    if (!found)
        return;

    breakpoint *bp;
    for (bp = funcbreakpoints; bp; bp=bp->next) {
        if (bp->address == addr) {
            ensure_line_buf(128);
            strcpy(linebuf, "Breakpoint is already set for function: ");
            render_value_linebuf(addr);
            gidebug_output(linebuf);
            return;
        }
    }

    bp = create_breakpoint(addr);
    bp->next = funcbreakpoints;
    funcbreakpoints = bp;

    ensure_line_buf(128);
    strcpy(linebuf, "Breakpoint set for function: ");
    render_value_linebuf(addr);
    gidebug_output(linebuf);
}

static void debugcmd_clear_breakpoint(char *arg)
{
    while (*arg == ' ')
        arg++;

    if (*arg == '\0') {
        gidebug_output("What function do you want to clear a breakpoint at?");
        return;
    }

    int found = FALSE;
    glui32 addr = 0;

    if (!found) {
        int res = parse_numeric_constant(arg, &addr);
        if (res < 0)
            return;
        if (res > 0) {
            found = TRUE;
            /* If possible, check whether this looks like a function
               address. But if it doesn't, just print a warning. */
            if (debuginfo) {
                inforoutine *routine = find_routine_for_address(addr);
                if (!routine || routine->address != addr) {
                    ensure_line_buf(128);
                    strcpy(linebuf, "This does not look like a function address: ");
                    render_value_linebuf(addr);
                    gidebug_output(linebuf);
                }
            }
        }
    }

    if (!found) {
        if (!debuginfo) {
            gidebug_output("No debug info; cannot look up functions by name");
            return;
        }
        inforoutine *routine = xmlHashLookup(debuginfo->routines, BAD_CAST arg);
        if (!routine) {
            ensure_line_buf(128);
            snprintf(linebuf, linebufsize, "Not a function name: %s", arg);
            gidebug_output(linebuf);
            return;
        }
        found = TRUE;
        addr = routine->address;
    }

    if (!found)
        return;

    breakpoint **bpp;
    for (bpp = &funcbreakpoints; *bpp; bpp=&((*bpp)->next)) {
        if ((*bpp)->address == addr) {
            breakpoint *bp = *bpp;
            *bpp = bp->next;
            free(bp);
            ensure_line_buf(128);
            strcpy(linebuf, "Cleared breakpoint for function: ");
            render_value_linebuf(addr);
            gidebug_output(linebuf);
            return;
        }
    }

    ensure_line_buf(128);
    strcpy(linebuf, "No breakpoint found for function: ");
    render_value_linebuf(addr);
    gidebug_output(linebuf);
}

static void debugcmd_print(char *arg)
{
    ensure_line_buf(128); /* for a start */

    while (*arg == ' ')
        arg++;

    if (*arg == '\0') {
        gidebug_output("What do you want to print?");
        return;
    }

    /* For plain numbers, and $HEX numbers, we print the value directly. */

    {
        glui32 val = 0;
        int res = parse_numeric_constant(arg, &val);
        if (res < 0)
            return;
        if (res > 0) {
            strcpy(linebuf, "");
            render_value_linebuf(val);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Symbol recognition should be case-insensitive */

    /* Is it a local variable name? */
    if (debuginfo) {
        glui32 curpc = pc;
        glui32 curlocalsbase = localsbase;
        /* Should have a way to trawl up and down the stack. */
        inforoutine *routine = find_routine_for_address(curpc);
        if (routine && routine->locals) {
            int ix;
            for (ix=0; ix<routine->numlocals; ix++) {
                if (!xmlStrcmp(routine->locals[ix].identifier, BAD_CAST arg)) {
                    glui32 locptr = curlocalsbase + 4*ix;
                    glui32 val = Stk4(locptr);
                    snprintf(linebuf, linebufsize, "local %s = ", routine->locals[ix].identifier);
                    render_value_linebuf(val);
                    gidebug_output(linebuf);
                    return;
                }
            }
        }
    }

    /* Is it a constant name? */
    if (debuginfo) {
        infoconstant *cons = xmlHashLookup(debuginfo->constants, BAD_CAST arg);
        if (cons) {
            snprintf(linebuf, linebufsize, "%d ($%X): constant", cons->value, cons->value);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it an attribute name? */
    if (debuginfo) {
        infoconstant *cons = xmlHashLookup(debuginfo->attributes, BAD_CAST arg);
        if (cons) {
            snprintf(linebuf, linebufsize, "%d ($%X): attribute", cons->value, cons->value);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it a property name? */
    if (debuginfo) {
        infoconstant *cons = xmlHashLookup(debuginfo->properties, BAD_CAST arg);
        if (cons) {
            snprintf(linebuf, linebufsize, "%d ($%X): property", cons->value, cons->value);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it an object name? */
    if (debuginfo) {
        infoobject *cons = xmlHashLookup(debuginfo->objects, BAD_CAST arg);
        if (cons) {
            snprintf(linebuf, linebufsize, "%d ($%X): object", cons->address, cons->address);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it an array name? */
    if (debuginfo) {
        infoarray *arr = xmlHashLookup(debuginfo->arrays, BAD_CAST arg);
        if (arr) {
            snprintf(linebuf, linebufsize, "%d ($%X): array[%d] of %d-byte values", arr->address, arr->address, arr->count, arr->bytesize);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it a global name? */
    if (debuginfo) {
        infoconstant *cons = xmlHashLookup(debuginfo->globals, BAD_CAST arg);
        if (cons) {
            glui32 val = Mem4(cons->value);
            snprintf(linebuf, linebufsize, "global %s = ", cons->identifier);
            render_value_linebuf(val);
            gidebug_output(linebuf);
            return;
        }
    }

    /* Is it a routine name? */
    if (debuginfo) {
        inforoutine *routine = xmlHashLookup(debuginfo->routines, BAD_CAST arg);
        if (routine) {
            snprintf(linebuf, linebufsize, "%d ($%X): routine", routine->address, routine->address);
            gidebug_output(linebuf);
            return;
        }
    }

    gidebug_output("Symbol not found");
}

static void debugcmd_help(char *arg)
{
    gidebug_output("Glulxe built-in debugger. Commands:");
    gidebug_output("- print <val>: Print a symbol or number.");
    gidebug_output("- bt: Display the current stack backtrace, with local variables.");
    gidebug_output("- break <func>: Set a breakpoint. (Must be a function name or the address of a function. Breakpoints currently only work at the start of a function.)");
    gidebug_output("- clear <func>: Clear a breakpoint.");
    gidebug_output("- cont: Continue execution. (From a breakpoint or other trap.)");
    gidebug_output("- help/?: This list.");
}

/* Debug console callback: This is invoked whenever the user enters a debug
   command.
   Returns 0 for most commands, 1 if the command ends a debugger pause.
*/
int debugger_cmd_handler(char *cmd)
{
    /* Trim spaces from start */
    while (*cmd == ' ')
        cmd++;
    /* Trim spaces from end */
    int len = strlen(cmd);
    while (len > 0 && cmd[len-1] == ' ') {
        cmd[len-1] = '\0';
        len--;
    }

    if (*cmd == '\0')
        return 0; /* empty command */

    char *cx;
    for (cx=cmd; *cx && *cx != ' '; cx++) { }
    len = (cx - cmd);

    if (len == 2 && !strncmp(cmd, "bt", len)) {
        debugcmd_backtrace(1);
        return 0;
    }

    if (len == 5 && !strncmp(cmd, "print", len)) {
        debugcmd_print(cx);
        return 0;
    }

    if (len == 5 && !strncmp(cmd, "break", len)) {
        debugcmd_set_breakpoint(cx);
        return 0;
    }

    if (len == 5 && !strncmp(cmd, "clear", len)) {
        debugcmd_clear_breakpoint(cx);
        return 0;
    }

    if (len == 4 && !strncmp(cmd, "cont", len)) {
        gidebug_output("Continuing...");
        return 1;
    }

    if (len == 4 && !strncmp(cmd, "help", len)) {
        debugcmd_help(cx);
        return 0;
    }

    if (len == 1 && !strncmp(cmd, "?", len)) {
        debugcmd_help(cx);
        return 0;
    }

    ensure_line_buf(strlen(cmd) + 64);
    snprintf(linebuf, linebufsize, "Unknown debug command: %s", cmd);
    gidebug_output(linebuf);
    return 0;
}

/* Debug console callback: This is invoked when the game starts, when it
   ends, and when each input cycle begins and ends.

   We take this opportunity to report CPU usage, if the track_cpu flag
   is set.
*/
void debugger_cycle_handler(int cycle)
{
    struct timeval now;
    double diff;

    debugger_invoked = TRUE;

    if (track_cpu) {
        switch (cycle) {

        case gidebug_cycle_Start:
            debugger_opcount = 0;
            gettimeofday(&debugger_timer, NULL);
            break;

        case gidebug_cycle_InputWait:
        case gidebug_cycle_DebugPause:
            gettimeofday(&now, NULL);
            diff = (now.tv_sec - debugger_timer.tv_sec) * 1000.0 + (now.tv_usec - debugger_timer.tv_usec) / 1000.0;
            ensure_line_buf(64);
            snprintf(linebuf, linebufsize, "VM: %ld cycles in %.3f ms", debugger_opcount, diff);
            gidebug_output(linebuf);
            break;

        case gidebug_cycle_InputAccept:
        case gidebug_cycle_DebugUnpause:
            debugger_opcount = 0;
            gettimeofday(&debugger_timer, NULL);
            break;

        }
    }
}

/* Do any start-time setup. This is called after VM memory is loaded,
   but before execution begins.
*/
void debugger_setup_start_state(void)
{
    if (start_trap) {
        /* Block and debug right now. This doesn't install a trap, it
           just calls gidebug_pause() directly. */
        gidebug_output("Break at start:");
        debugcmd_backtrace(0);
        gidebug_pause();
    }
}

/* Check whether we've set a breakpoint at this function address.
   If so, block and debug.
*/
void debugger_check_func_breakpoint(glui32 addr)
{
    int pause = FALSE;
    breakpoint *bp;

    for (bp = funcbreakpoints; bp; bp=bp->next) {
        if (bp->address == addr) {
            pause = TRUE;
            break;
        }
    }

    if (pause) {
        gidebug_output("Breakpoint:");
        debugcmd_backtrace(0);
        gidebug_pause();
    }
}

/* Invoke the library's debug-block mechanism.
*/
void debugger_block_and_debug(char *msg)
{
    gidebug_output(msg);
    gidebug_pause();
}

/* If the quit-trap preference is set, enter debug mode.
*/
void debugger_handle_quit()
{
    if (quit_trap) {
        gidebug_output("Quit trap, pausing...");
        debugcmd_backtrace(0);
        gidebug_pause();
    }
}

/* Report a fatal error to the debug console, along with the current
   stack trace. Depending on preferences, we may then enter full-on debug mode.
*/
void debugger_handle_crash(char *msg)
{
    char *prefix = "Glulxe fatal error: ";
    ensure_line_buf(strlen(prefix) + strlen(msg));
    strcpy(linebuf, prefix);
    strcat(linebuf, msg);
    gidebug_output(linebuf);

    debugcmd_backtrace(1);

    if (crash_trap) {
        gidebug_pause();
    }
}

#endif /* VM_DEBUGGER */


text:
package org.uberfire.preferences.shared.bean;

import org.uberfire.mvp.Command;
import org.uberfire.mvp.ParameterizedCommand;
import org.uberfire.preferences.shared.PreferenceScope;
import org.uberfire.preferences.shared.impl.PreferenceScopeResolutionStrategyInfo;

/**
 * Interface that all preference beans should implement. It allows load and save operations
 * when injecting it through CDI. The methods in this class are only really implemented by
 * a generated preference bean, that will be the one injected by CDI.
 * @param <T> The preference bean type implementing the interface.
 */
public interface BasePreference<T> extends Preference {

    /**
     * Loads the preference content recursively through its properties.
     */
    default void load() {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Loads the preference content recursively through its properties.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void load(final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Loads the preference content recursively through its properties.
     * @param successCallback Success callback that returns the loaded preference.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void load(final ParameterizedCommand<T> successCallback,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Loads the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     */
    default void load(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Loads the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void load(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Loads the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     * @param successCallback Success callback that returns the loaded preference.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void load(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy,
                      final ParameterizedCommand<T> successCallback,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     */
    default void save() {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param successCallback Success callback that indicates that the preference was saved.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final Command successCallback,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     */
    default void save(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScopeResolutionStrategy Custom preference scope resolution strategy to be used.
     * @param successCallback Success callback that indicates that the preference was saved.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final PreferenceScopeResolutionStrategyInfo customScopeResolutionStrategy,
                      final Command successCallback,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScope Custom preference scope to be used.
     */
    default void save(final PreferenceScope customScope) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScope Custom preference scope to be used.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final PreferenceScope customScope,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Saves the preference content recursively through its properties.
     * @param customScope Custom preference scope to be used.
     * @param successCallback Success callback that indicates that the preference was saved.
     * @param errorCallback Error callback that returns the exception that occurred (if any).
     */
    default void save(final PreferenceScope customScope,
                      final Command successCallback,
                      final ParameterizedCommand<Throwable> errorCallback) {
        throw new UnsupportedOperationException("You should call this method only for default qualified injected instances.");
    }

    /**
     * Returns the default value of this preference object. All users will start with this value
     * unless they change it.
     * @param defaultValue Preference to be filled and returned. It contains all child preferences already instantiated.
     * @return The defaultValue received as parameter, just with its value updated. Null if there is not a default value.
     */
    default T defaultValue(T defaultValue) {
        return null;
    }
}

redpajama_set_name: RedPajamaGithub


text:
/*
 * Minio Cloud Storage, (C) 2014-2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"encoding/json"
	"fmt"
	"net/url"
	"path"
	"sync"
)

// s3Peer structs contains the address of a peer in the cluster, and
// its BucketMetaState interface objects.
type s3Peer struct {
	// address in `host:port` format
	addr string
	// BucketMetaState client interface
	bmsClient BucketMetaState
}

// type representing all peers in the cluster
type s3Peers []s3Peer

// makeS3Peers makes an s3Peers struct value from the given urls
// slice. The urls slice is assumed to be non-empty and free of nil
// values.
func makeS3Peers(eps []*url.URL) s3Peers {
	var ret []s3Peer

	// map to store peers that are already added to ret
	seenAddr := make(map[string]bool)

	// add local (self) as peer in the array
	ret = append(ret, s3Peer{
		globalMinioAddr,
		&localBucketMetaState{ObjectAPI: newObjectLayerFn},
	})
	seenAddr[globalMinioAddr] = true

	serverCred := serverConfig.GetCredential()
	// iterate over endpoints to find new remote peers and add
	// them to ret.
	for _, ep := range eps {
		if ep.Host == "" {
			continue
		}

		// Check if the remote host has been added already
		if !seenAddr[ep.Host] {
			cfg := authConfig{
				accessKey:       serverCred.AccessKey,
				secretKey:       serverCred.SecretKey,
				serverAddr:      ep.Host,
				serviceEndpoint: path.Join(reservedBucket, s3Path),
				secureConn:      globalIsSSL,
				serviceName:     "S3",
			}

			ret = append(ret, s3Peer{
				addr:      ep.Host,
				bmsClient: &remoteBucketMetaState{newAuthRPCClient(cfg)},
			})
			seenAddr[ep.Host] = true
		}
	}

	return ret
}

// initGlobalS3Peers - initialize globalS3Peers by passing in
// endpoints - intended to be called early in program start-up.
func initGlobalS3Peers(eps []*url.URL) {
	globalS3Peers = makeS3Peers(eps)
}

// GetPeerClient - fetch BucketMetaState interface by peer address
func (s3p s3Peers) GetPeerClient(peer string) BucketMetaState {
	for _, p := range s3p {
		if p.addr == peer {
			return p.bmsClient
		}
	}
	return nil
}

// SendUpdate sends bucket metadata updates to all given peer
// indices. The update calls are sent in parallel, and errors are
// returned per peer in an array. The returned error arrayslice is
// always as long as s3p.peers.addr.
//
// The input peerIndex slice can be nil if the update is to be sent to
// all peers. This is the common case.
//
// The updates are sent via a type implementing the BucketMetaState
// interface. This makes sure that the local node is directly updated,
// and remote nodes are updated via RPC calls.
func (s3p s3Peers) SendUpdate(peerIndex []int, args BucketUpdater) []error {

	// peer error array
	errs := make([]error, len(s3p))

	// Start a wait group and make RPC requests to peers.
	var wg sync.WaitGroup

	// Function that sends update to peer at `index`
	sendUpdateToPeer := func(index int) {
		defer wg.Done()
		errs[index] = args.BucketUpdate(s3p[index].bmsClient)
	}

	// Special (but common) case of peerIndex == nil, implies send
	// update to all peers.
	if peerIndex == nil {
		for idx := 0; idx < len(s3p); idx++ {
			wg.Add(1)
			go sendUpdateToPeer(idx)
		}
	} else {
		// Send update only to given peer indices.
		for _, idx := range peerIndex {
			// check idx is in array bounds.
			if !(idx >= 0 && idx < len(s3p)) {
				errorIf(
					fmt.Errorf("Bad peer index %d input to SendUpdate()", idx),
					"peerIndex out of bounds",
				)
				continue
			}
			wg.Add(1)
			go sendUpdateToPeer(idx)
		}
	}

	// Wait for requests to complete and return
	wg.Wait()
	return errs
}

// S3PeersUpdateBucketNotification - Sends Update Bucket notification
// request to all peers. Currently we log an error and continue.
func S3PeersUpdateBucketNotification(bucket string, ncfg *notificationConfig) {
	setBNPArgs := &SetBucketNotificationPeerArgs{Bucket: bucket, NCfg: ncfg}
	errs := globalS3Peers.SendUpdate(nil, setBNPArgs)
	for idx, err := range errs {
		errorIf(
			err,
			"Error sending update bucket notification to %s - %v",
			globalS3Peers[idx].addr, err,
		)
	}
}

// S3PeersUpdateBucketListener - Sends Update Bucket listeners request
// to all peers. Currently we log an error and continue.
func S3PeersUpdateBucketListener(bucket string, lcfg []listenerConfig) {
	setBLPArgs := &SetBucketListenerPeerArgs{Bucket: bucket, LCfg: lcfg}
	errs := globalS3Peers.SendUpdate(nil, setBLPArgs)
	for idx, err := range errs {
		errorIf(
			err,
			"Error sending update bucket listener to %s - %v",
			globalS3Peers[idx].addr, err,
		)
	}
}

// S3PeersUpdateBucketPolicy - Sends update bucket policy request to
// all peers. Currently we log an error and continue.
func S3PeersUpdateBucketPolicy(bucket string, pCh policyChange) {
	byts, err := json.Marshal(pCh)
	if err != nil {
		errorIf(err, "Failed to marshal policyChange - this is a BUG!")
		return
	}
	setBPPArgs := &SetBucketPolicyPeerArgs{Bucket: bucket, PChBytes: byts}
	errs := globalS3Peers.SendUpdate(nil, setBPPArgs)
	for idx, err := range errs {
		errorIf(
			err,
			"Error sending update bucket policy to %s - %v",
			globalS3Peers[idx].addr, err,
		)
	}
}


text:
For key values that are too large to be used as array indices. This symbol
table implementation keeps an ordered linked list. An ordered linked list provides a
slower insert, but because the list is ordered, the functions select and
sort are available. Search still takes the same time as an unordered linked
list, so only use this if you need a list-based symbol table with select
and sort functions.


text:
package org.neo4j.ogm.persistence.examples.locking;

import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;

import java.util.Collection;
import java.util.Date;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.neo4j.ogm.domain.locking.EnemyOf;
import org.neo4j.ogm.domain.locking.FriendOf;
import org.neo4j.ogm.domain.locking.User;
import org.neo4j.ogm.exception.OptimisticLockingException;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.testutil.TestContainersTestBase;

/**
 * @author Frantisek Hartman
 */
public class RelationshipEntityOptimisticLockingTest extends TestContainersTestBase {

    private static SessionFactory sessionFactory;

    private Session session;

    @BeforeClass
    public static void setUpClass() {
        sessionFactory = new SessionFactory(getDriver(), "org.neo4j.ogm.domain.locking");
    }

    @Before
    public void setUp() throws Exception {
        session = sessionFactory.openSession();
        session.purgeDatabase();
    }

    @Test
    public void whenSaveRelationshipEntityThenSetVersionToZero() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        assertThat(friendOf.getVersion()).isEqualTo(0L);
    }

    @Test
    public void givenRelationshipEntityWhenUpdateThenIncrementVersion() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        Date sinceUpdated = new Date();
        friendOf.setSince(sinceUpdated);

        session.save(michael);
        assertThat(friendOf.getVersion()).isEqualTo(1L);

        session.clear();

        FriendOf loaded = session.load(FriendOf.class, friendOf.getId());
        assertThat(loaded.getSince()).isEqualTo(sinceUpdated);
        assertThat(loaded.getVersion()).isEqualTo(1L);
    }

    @Test
    public void givenRelationshipEntityWithWrongVersionWhenSaveThenFailWithOptimisticLockingException() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);
        friendOf.setSince(new Date());
        session.save(friendOf);

        FriendOf wrongVersion = new FriendOf(michael, oliver);
        wrongVersion.setId(friendOf.getId());
        wrongVersion.setVersion(0L);
        Date updatedSince = new Date();
        wrongVersion.setSince(updatedSince);

        assertThatThrownBy(() -> session.save(wrongVersion, 0))
            .isInstanceOf(OptimisticLockingException.class);
    }

    @Test
    public void givenRelationshipEntityCustomThenOptimisticLockingCheckWorksAndThrowsException() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");

        EnemyOf enemyOf = new EnemyOf(michael, oliver, new Date());
        session.save(enemyOf);
        assertThat(enemyOf.getCustomVersion()).isEqualTo(0L);

        enemyOf.setSince(new Date());
        session.save(enemyOf);
        assertThat(enemyOf.getCustomVersion()).isEqualTo(1L);

        EnemyOf wrongVersion = new EnemyOf(michael, oliver, new Date());
        wrongVersion.setId(enemyOf.getId());
        wrongVersion.setCustomVersion(0L);
        assertThatThrownBy(() -> session.save(wrongVersion, 0))
            .isInstanceOf(OptimisticLockingException.class);
    }

    @Test
    public void givenRelationshipEntityWhenDeleteThenDeleteRelationshipEntity() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        session.delete(friendOf);

        Collection<FriendOf> friendOfs = session.loadAll(FriendOf.class);
        assertThat(friendOfs).isEmpty();
    }

    @Test
    public void saveDeletedRelationshipEntityShouldFailWithOptimisticLockingException() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        // someone else deletes node
        session.delete(friendOf);

        friendOf.setSince(new Date());

        // save should throw exception
        assertThatThrownBy(() -> session.save(friendOf))
            .isInstanceOf(OptimisticLockingException.class);

        // save through related entity should trow exception
        assertThatThrownBy(() -> session.save(michael))
            .isInstanceOf(OptimisticLockingException.class);

        // and relationship should not exist
        Collection<FriendOf> friendOfs = session.loadAll(FriendOf.class);
        assertThat(friendOfs).isEmpty();
    }

    @Test
    public void givenRelationshipEntityWithWrongVersionWhenDeleteThenThrowOptimisticLockingException() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(friendOf);

        friendOf.setVersion(1L);

        assertThatThrownBy(() -> session.delete(friendOf))
            .isInstanceOf(OptimisticLockingException.class);

        session.clear();
        Collection<FriendOf> friendOfs = session.loadAll(FriendOf.class);
        assertThat(friendOfs).hasSize(1);
    }

    @Test
    public void optimisticLockingExceptionShouldRollbackDefaultTransaction() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);
        friendOf.setSince(new Date());
        session.save(friendOf);

        michael.setName("Michael Updated");
        oliver.setName("Oliver Updated");
        FriendOf wrongVersion = new FriendOf(michael, oliver);
        wrongVersion.setId(friendOf.getId());
        wrongVersion.setVersion(0L);
        Date updatedSince = new Date();
        wrongVersion.setSince(updatedSince);

        try {
            session.save(wrongVersion, 0);
            fail("Expected OptimisticLockingException");
        } catch (OptimisticLockingException ex) {
            session.clear();

            Collection<User> users = session.loadAll(User.class);
            assertThat(users).extracting(User::getName).containsOnly("Michael", "Oliver");
        }
    }

    @Test
    public void removeVersionedRelationship() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        michael.clearFriends();

        session.save(michael);

        ensureFriendsOfRelationshipsHaveCount(0);
    }

    @Test // GH-746
    public void updateVersionedRelationship() {
        User michael = new User("Michael");
        User oliver = new User("Oliver");
        FriendOf friendOf = michael.addFriend(oliver);

        session.save(michael);

        oliver.clearFriends();

        User george = new User("George");
        george.addFriend(friendOf);
        session.save(michael);

        ensureFriendsOfRelationshipsHaveCount(1);
    }

    private void ensureFriendsOfRelationshipsHaveCount(long count) {
        session.clear();
        Long relationshipCount = (Long) session.query("MATCH ()-[r:FRIEND_OF]->() return count(r) as c", emptyMap())
            .queryResults().iterator().next().get("c");
        assertThat(relationshipCount).isEqualTo(count);
    }

}


text:
package org.sd.nlp;

/**
 * Interface for computing breaks in a string of code points.
 *
 * @author Spence Koehler
 */
public interface BreakStrategy {

  /**
   * Compute the breaks in the string of code points.
   *
   * @return one break type for each code point.
   */
  public Break[] computeBreaks(int[] codePoints);

}


text:
package integration

import (
	"errors"
	"flag"
	"testing"

	aggclient "github.com/m3db/m3aggregator/client"
	aggserver "github.com/m3db/m3collector/integration/server"
	"github.com/m3db/m3collector/reporter"
	aggreporter "github.com/m3db/m3collector/reporter/m3aggregator"
	"github.com/m3db/m3metrics/matcher"
	"github.com/m3db/m3metrics/matcher/cache"
	"github.com/m3db/m3x/server"

	"github.com/stretchr/testify/require"
)

var (
	serverAddrArg          = flag.String("serverAddr", "localhost:6789", "server address")
	errServerStartTimedOut = errors.New("server took too long to start")
	errServerStopTimedOut  = errors.New("server took too long to stop")
)

type testSetup struct {
	opts       testOptions
	client     aggclient.Client
	reporter   reporter.Reporter
	serverAddr string
	server     server.Server
}

func newTestSetup(t *testing.T, opts testOptions) *testSetup {
	if opts == nil {
		opts = newTestOptions()
	}

	// Create reporter.
	cache := cache.NewCache(opts.CacheOptions())
	matcher, err := matcher.NewMatcher(cache, opts.MatcherOptions())
	require.NoError(t, err)
	client := aggclient.NewClient(opts.AggregatorClientOptions())
	reporter := aggreporter.NewReporter(matcher, client, opts.AggregatorReporterOptions())

	// Create server.
	serverAddr := *serverAddrArg
	if addr := opts.ServerAddr(); addr != "" {
		serverAddr = addr
	}
	server := aggserver.NewServer(serverAddr, opts.ServerOptions())

	return &testSetup{
		opts:       opts,
		client:     client,
		reporter:   reporter,
		serverAddr: serverAddr,
		server:     server,
	}
}

func (ts *testSetup) Reporter() reporter.Reporter { return ts.reporter }

func (ts *testSetup) newClient() *client {
	aggClientOpts := ts.opts.AggregatorClientOptions()
	connectTimeout := aggClientOpts.ConnectionOptions().ConnectionTimeout()
	return newClient(ts.serverAddr, connectTimeout)
}

func (ts *testSetup) waitUntilServerIsUp() error {
	c := ts.newClient()
	defer c.close()

	serverIsUp := func() bool { return c.testConnection() }
	if waitUntil(serverIsUp, ts.opts.ServerStateChangeTimeout()) {
		return nil
	}
	return errServerStartTimedOut
}

func (ts *testSetup) waitUntilServerIsDown() error {
	c := ts.newClient()
	defer c.close()

	serverIsDown := func() bool { return !c.testConnection() }
	if waitUntil(serverIsDown, ts.opts.ServerStateChangeTimeout()) {
		return nil
	}
	return errServerStopTimedOut
}

func (ts *testSetup) startServer() error {
	if err := ts.server.ListenAndServe(); err != nil {
		return err
	}
	if err := ts.waitUntilServerIsUp(); err != nil {
		return err
	}
	return ts.client.Init()
}

func (ts *testSetup) stopServer() error {
	ts.server.Close()
	return ts.waitUntilServerIsDown()
}

func (ts *testSetup) close() {
	ts.reporter.Close()
	ts.server.Close()
}


text:
package com.kzmen.sczxjf.net;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by Administrator on 2017/8/17.
 */

public class DataFactory {
    public static Object getInstanceByJson(Class<?> clazz, String json)
    {
        Object obj = null;
        Gson gson = new Gson();
        obj = gson.fromJson(json, clazz);
        return obj;
    }

    /**
     * @author I321533
     * @param json
     * @param clazz
     * @return
     */
    public static <T> List<T> jsonToList(String json, Class<T[]> clazz)
    {
        Gson gson = new Gson();
        T[] array = gson.fromJson(json, clazz);
        return Arrays.asList(array);
    }

    /**
     * @param json
     * @param clazz
     * @return
     */
    public static <T> ArrayList<T> jsonToArrayList(String json, Class<T> clazz)
    {
        Type type = new TypeToken<ArrayList<JsonObject>>()
        {}.getType();
        ArrayList<JsonObject> jsonObjects = new Gson().fromJson(json, type);

        ArrayList<T> arrayList = new ArrayList<>();
        for (JsonObject jsonObject : jsonObjects)
        {
            arrayList.add(new Gson().fromJson(jsonObject, clazz));
        }
        return arrayList;
    }
}


text:
import os
import subprocess
import sys
import tempfile
from textwrap import dedent
from gooey import Gooey, GooeyParser
import pexpect

def local_path():
  return getattr(sys, 'frozen', None) or os.path.dirname(__file__)

@Gooey(program_name='Gooey Packager', monospace_display=True)
def main():
  parser = GooeyParser(description='Package your Gooey applications into standalone executables')
  parser.add_argument(
    "program_name",
    metavar='Program Name',
    help='Destination name for the packaged executable'
  )
  parser.add_argument(
    "source_path",
    metavar="Program Source",
    help='The main source file of your program',
    widget="FileChooser"
  )

  parser.add_argument(
    "output_dir",
    metavar="Output Directory",
    help='Location to store the generated files',
    widget="DirChooser"
  )

  args = parser.parse_args()

  if not os.path.exists(args.source_path):
    raise IOError('{} does not appear to be a valid file path'.format(args.source_path))

  if not os.path.exists(args.output_dir):
    raise IOError('{} does not appear to be a valid directory'.format(args.output_dir))

  with open(os.path.join(local_path(), 'build_template'), 'r') as f:
    spec_details = f.read().format(program_name=args.program_name, source_path=args.source_path)

  fileno, path = tempfile.mkstemp(prefix='gooeybuild', suffix='.spec')
  with open(path, 'w') as f:
    f.write(spec_details)

  cmd = 'pyinstaller "{0}" --distpath="{1}"'.format(path, args.output_dir)
  print cmd
  from pexpect.popen_spawn import PopenSpawn
  child = PopenSpawn(cmd)
  child.logfile = sys.stdout
  child.wait()
  print dedent('''
  ___  _ _  ______                 _
 / _ \| | | |  _  \               | |
/ /_\ \ | | | | | |___  _ __   ___| |
|  _  | | | | | | / _ \| '_ \ / _ \ |
| | | | | | | |/ / (_) | | | |  __/_|
\_| |_/_|_| |___/ \___/|_| |_|\___(_)
  ''')
  print 'Wrote Executable file to {}'.format(args.output_dir)

if __name__ == '__main__':
  sys.argv += ["My Cool App", "C:\\Users\\Chris\\Documents\\learning_django\\widget_demo.py", "C:\\Users\\Chris\\Documents\\learning_django\\dist"]
  main()


text:
<?php
/**
 * WPSEO Premium plugin file.
 *
 * @package WPSEO\Premium|Classes
 */

/**
 * The metabox for premium.
 */
class WPSEO_Premium_Metabox implements WPSEO_WordPress_Integration {

	/**
	 * Instance of the WPSEO_Metabox_Link_Suggestions class.
	 *
	 * @var WPSEO_Metabox_Link_Suggestions
	 */
	protected $link_suggestions;

	/**
	 * Creates the meta box class.
	 *
	 * @param WPSEO_Metabox_Link_Suggestions|null $link_suggestions The link suggestions meta box.
	 */
	public function __construct( WPSEO_Metabox_Link_Suggestions $link_suggestions = null ) {
		if ( $link_suggestions === null ) {
			$link_suggestions = new WPSEO_Metabox_Link_Suggestions();
		}

		$this->link_suggestions = $link_suggestions;
	}

	/**
	 * Registers relevant hooks to WordPress.
	 *
	 * @codeCoverageIgnore Method uses dependencies.
	 *
	 * @return void
	 */
	public function register_hooks() {
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
		add_action( 'admin_init', array( $this, 'initialize' ) );

		$this->link_suggestions->register_hooks();
	}

	/**
	 * Initializes the metabox by loading the register_hooks for the dependencies.
	 *
	 * @return void
	 */
	public function initialize() {
		if ( ! $this->load_metabox( $this->get_current_page() ) ) {
			return;
		}

		foreach ( $this->get_metabox_integrations() as $integration ) {
			$integration->register_hooks();
		}
	}

	/**
	 * Enqueues assets when relevant.
	 *
	 * @codeCoverageIgnore Method uses dependencies.
	 *
	 * @return void
	 */
	public function enqueue_assets() {
		if ( ! $this->load_metabox( $this->get_current_page() ) ) {
			return;
		}

		wp_enqueue_script( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-metabox' );
		wp_enqueue_style( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-metabox' );

		$localization = new WPSEO_Admin_Asset_Yoast_Components_L10n();
		$localization->localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-metabox' );

		$premium_localization = new WPSEO_Premium_Asset_JS_L10n();
		$premium_localization->localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-metabox' );

		$this->send_data_to_assets();
	}

	/**
	 * Send data to assets by using wp_localize_script.
	 *
	 * @return void
	 */
	public function send_data_to_assets() {
		$analysis_seo = new WPSEO_Metabox_Analysis_SEO();

		$data = array(
			'restApi'            => $this->get_rest_api_config(),
			'seoAnalysisEnabled' => $analysis_seo->is_enabled(),
			'licensedURL'        => WPSEO_Utils::get_home_url(),
		);

		if ( WPSEO_Metabox::is_post_edit( $this->get_current_page() ) ) {
			$data = array_merge( $data, $this->get_post_metabox_config() );
		}
		elseif ( WPSEO_Taxonomy::is_term_edit( $this->get_current_page() ) ) {
			$data = array_merge( $data, $this->get_term_metabox_config() );
		}

		// Use an extra level in the array to preserve booleans. WordPress sanitizes scalar values in the first level of the array.
		wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'premium-metabox', 'wpseoPremiumMetaboxData', array( 'data' => $data ) );
	}

	/**
	 * Retrieves the metabox config for a post.
	 *
	 * @return array The config.
	 */
	protected function get_post_metabox_config() {
		$insights_enabled         = WPSEO_Options::get( 'enable_metabox_insights', false );
		$link_suggestions_enabled = WPSEO_Options::get( 'enable_link_suggestions', false );

		$language_support = new WPSEO_Premium_Prominent_Words_Language_Support();

		if ( ! $language_support->is_language_supported( WPSEO_Language_Utils::get_language( get_locale() ) ) ) {
			$insights_enabled         = false;
			$link_suggestions_enabled = false;
		}

		$post = $this->get_post();

		$post_type_support = new WPSEO_Premium_Prominent_Words_Support();
		if ( ! $post_type_support->is_post_type_supported( get_post_type( $post ) ) ) {
			$insights_enabled = false;
		}

		return array(
			'insightsEnabled'          => ( $insights_enabled ) ? 'enabled' : 'disabled',
			'postID'                   => $this->get_post_ID(),
			'linkSuggestionsEnabled'   => ( $link_suggestions_enabled ) ? 'enabled' : 'disabled',
			'linkSuggestionsAvailable' => $this->link_suggestions->is_available( $post->post_type ),
			'linkSuggestionsUnindexed' => $this->link_suggestions->is_site_unindexed() && WPSEO_Capability_Utils::current_user_can( 'wpseo_manage_options' ),
			'linkSuggestions'          => $this->link_suggestions->get_js_data(),
		);
	}

	/**
	 * Retrieves the metabox config for a term.
	 *
	 * @return array The config.
	 */
	protected function get_term_metabox_config() {
		return array(
			'insightsEnabled'          => 'disabled',
			'linkSuggestionsEnabled'   => 'disabled',
			'linkSuggestionsAvailable' => false,
			'linkSuggestionsUnindexed' => false,
			'linkSuggestions'          => false,
		);
	}

	/**
	 * Retrieves the REST API configuration.
	 *
	 * @return array The configuration.
	 */
	protected function get_rest_api_config() {
		return array(
			'available'                 => WPSEO_Utils::is_api_available(),
			'contentEndpointsAvailable' => WPSEO_Utils::are_content_endpoints_available(),
			'root'                      => esc_url_raw( rest_url() ),
			'nonce'                     => wp_create_nonce( 'wp_rest' ),
		);
	}

	/**
	 * Returns the post for the current admin page.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return WP_Post The post for the current admin page.
	 */
	protected function get_post() {
		return get_post( $this->get_post_ID() );
	}

	/**
	 * Retrieves the post ID from the globals.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return int The post ID.
	 */
	protected function get_post_ID() {
		return $GLOBALS['post_ID'];
	}

	/**
	 * Retrieves the metabox specific integrations.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return WPSEO_WordPress_Integration[] The metabox integrations.
	 */
	protected function get_metabox_integrations() {
		return array(
			'social-previews'       => new WPSEO_Social_Previews(),

			// Add custom fields plugin to post and page edit pages.
			'premium-custom-fields' => new WPSEO_Custom_Fields_Plugin(),
		);
	}

	/**
	 * Checks whether or not the metabox related scripts should be loaded.
	 *
	 * @codeCoverageIgnore
	 *
	 * @param string $current_page The page we are on.
	 *
	 * @return bool True when it should be loaded.
	 */
	protected function load_metabox( $current_page ) {
		// When the current page is a term related one.
		if ( WPSEO_Taxonomy::is_term_edit( $current_page ) || WPSEO_Taxonomy::is_term_overview( $current_page ) ) {
			return WPSEO_Options::get( 'display-metabox-tax-' . $this->get_current_taxonomy() );
		}

		// When the current page isn't a post related one.
		if ( WPSEO_Metabox::is_post_edit( $current_page ) || WPSEO_Metabox::is_post_overview( $current_page ) ) {
			return WPSEO_Post_Type::has_metabox_enabled( $this->get_current_post_type() );
		}

		// Make sure ajax integrations are loaded.
		return wp_doing_ajax();
	}

	/**
	 * Retrieves the current post type.
	 *
	 * @codeCoverageIgnore It depends on external request input.
	 *
	 * @return string The post type.
	 */
	protected function get_current_post_type() {
		$post = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_STRING );

		if ( $post ) {
			return get_post_type( get_post( $post ) );
		}

		return filter_input(
			INPUT_GET,
			'post_type',
			FILTER_SANITIZE_STRING,
			array(
				'options' => array(
					'default' => 'post',
				),
			)
		);
	}

	/**
	 * Retrieves the current taxonomy.
	 *
	 * @codeCoverageIgnore This function depends on external request input.
	 *
	 * @return string The taxonomy.
	 */
	protected function get_current_taxonomy() {
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- doing a strict in_array check should be sufficient.
		if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || ! in_array( $_SERVER['REQUEST_METHOD'], array( 'GET', 'POST' ), true ) ) {
			return '';
		}

		if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
			return (string) filter_input(
				INPUT_POST,
				'taxonomy',
				FILTER_SANITIZE_STRING
			);
		}

		return (string) filter_input(
			INPUT_GET,
			'taxonomy',
			FILTER_SANITIZE_STRING
		);
	}

	/**
	 * Retrieves the value of the pagenow variable.
	 *
	 * @codeCoverageIgnore
	 *
	 * @return string The value of pagenow.
	 */
	private function get_current_page() {
		global $pagenow;

		return $pagenow;
	}

	/**
	 * Registers assets to WordPress.
	 *
	 * @deprecated 9.4
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public function register_assets() {
		_deprecated_function( 'WPSEO_Premium_Metabox::register_assets', '9.4' );
	}
}


text:
package org.apache.nifi.snmp.factory.trap;

import org.apache.nifi.snmp.configuration.V1TrapConfiguration;
import org.junit.jupiter.api.Test;
import org.snmp4j.PDU;
import org.snmp4j.PDUv1;
import org.snmp4j.Target;

import java.time.Instant;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;

class V1TrapPDUFactoryTest {

    private static final String AGENT_ADDRESS = "127.0.0.1";
    private static final String ENTERPRISE_OID = "1.3.6.1.4.1.8072.2.3.0.1";
    private static final int GENERIC_TRAP_TYPE = 6;
    private static final int SPECIFIC_TRAP_TYPE = 2;

    @Test
    void testCreateV1TrapPdu() {
        final Target mockTarget = mock(Target.class);
        final V1TrapConfiguration v1TrapConfiguration = V1TrapConfiguration.builder()
                .enterpriseOid(ENTERPRISE_OID)
                .agentAddress(AGENT_ADDRESS)
                .genericTrapType(String.valueOf(GENERIC_TRAP_TYPE))
                .specificTrapType(String.valueOf(SPECIFIC_TRAP_TYPE))
                .build();

        final V1TrapPDUFactory v1TrapPduFactory = new V1TrapPDUFactory(mockTarget, Instant.now());
        final PDU pdu = v1TrapPduFactory.get(v1TrapConfiguration);

        assertEquals(PDU.V1TRAP, pdu.getType());

        final PDUv1 pduV1 = (PDUv1) pdu;

        assertEquals(ENTERPRISE_OID, pduV1.getEnterprise().toString());
        assertEquals(AGENT_ADDRESS, pduV1.getAgentAddress().toString());
        assertEquals(GENERIC_TRAP_TYPE, pduV1.getGenericTrap());
        assertEquals(SPECIFIC_TRAP_TYPE, pduV1.getSpecificTrap());
    }
}


text:
namespace Google.Cloud.Bigtable.V2.Snippets
{
    // [START bigtable_v2_generated_BigtableServiceApi_PingAndWarm_async]
    using Google.Cloud.Bigtable.Common.V2;
    using Google.Cloud.Bigtable.V2;
    using System.Threading.Tasks;

    public sealed partial class GeneratedBigtableServiceApiClientSnippets
    {
        /// <summary>Snippet for PingAndWarmAsync</summary>
        /// <remarks>
        /// This snippet has been automatically generated for illustrative purposes only.
        /// It may require modifications to work in your environment.
        /// </remarks>
        public async Task PingAndWarmRequestObjectAsync()
        {
            // Create client
            BigtableServiceApiClient bigtableServiceApiClient = await BigtableServiceApiClient.CreateAsync();
            // Initialize request argument(s)
            PingAndWarmRequest request = new PingAndWarmRequest
            {
                InstanceName = InstanceName.FromProjectInstance("[PROJECT]", "[INSTANCE]"),
                AppProfileId = "",
            };
            // Make the request
            PingAndWarmResponse response = await bigtableServiceApiClient.PingAndWarmAsync(request);
        }
    }
    // [END bigtable_v2_generated_BigtableServiceApi_PingAndWarm_async]
}