URI: 
       tInitial commit - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 6d318819d95cceedcc5bca92e45cee03142ed3b5
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Sun,  1 Jan 2017 09:45:55 +0100
       
       Initial commit
       
       Diffstat:
         A LICENSE                             |      22 ++++++++++++++++++++++
         A Makefile                            |       7 +++++++
         A README.md                           |      11 +++++++++++
         A button.c                            |     257 +++++++++++++++++++++++++++++++
         A button.h                            |      78 +++++++++++++++++++++++++++++++
         A cJSON.c                             |    1492 +++++++++++++++++++++++++++++++
         A cJSON.h                             |     160 +++++++++++++++++++++++++++++++
         A common.c                            |      80 +++++++++++++++++++++++++++++++
         A common.h                            |      52 +++++++++++++++++++++++++++++++
         A event.c                             |      42 +++++++++++++++++++++++++++++++
         A event.h                             |      31 +++++++++++++++++++++++++++++++
         A grid.c                              |     313 +++++++++++++++++++++++++++++++
         A grid.h                              |      53 ++++++++++++++++++++++++++++++
         A ltk.c                               |      79 +++++++++++++++++++++++++++++++
         A ltk.h                               |      57 +++++++++++++++++++++++++++++++
         A main.c                              |      52 +++++++++++++++++++++++++++++++
         A test1.c                             |      41 +++++++++++++++++++++++++++++++
         A theme.c                             |      64 +++++++++++++++++++++++++++++++
         A theme.h                             |      39 +++++++++++++++++++++++++++++++
         A themes/default.json                 |      34 +++++++++++++++++++++++++++++++
         A uthash.h                            |    1074 +++++++++++++++++++++++++++++++
         A widget.c                            |      23 +++++++++++++++++++++++
         A widget.h                            |      68 +++++++++++++++++++++++++++++++
         A window.c                            |     165 +++++++++++++++++++++++++++++++
         A window.h                            |      56 +++++++++++++++++++++++++++++++
       
       25 files changed, 4350 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/LICENSE b/LICENSE
       t@@ -0,0 +1,22 @@
       +MIT/X Consortium License
       +
       +The Lumidify ToolKit (LTK)
       +Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       +
       +Permission is hereby granted, free of charge, to any person obtaining a copy
       +of this software and associated documentation files (the "Software"), to deal
       +in the Software without restriction, including without limitation the rights
       +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +copies of the Software, and to permit persons to whom the Software is
       +furnished to do so, subject to the following conditions:
       +
       +The above copyright notice and this permission notice shall be included in all
       +copies or substantial portions of the Software.
       +
       +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +SOFTWARE.
   DIR diff --git a/Makefile b/Makefile
       t@@ -0,0 +1,7 @@
       +LIBS = -lX11 -lm -ldl
       +STD = -std=c89
       +FLAGS = -g -w -Wall -Werror -Wextra -pedantic
       +CFILES = ltk.c event.c cJSON.c common.c widget.c grid.c window.c theme.c button.c test1.c
       +
       +all: test1.c
       +        gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test
   DIR diff --git a/README.md b/README.md
       t@@ -0,0 +1,11 @@
       +# LTK - Lumidify Toolkit
       +
       +This is work in progress.
       +
       +Please do not attempt to actually use any of the code.
       +
       +## Licenses of Other Libraries Used
       +
       +[uthash](https://troydhanson.github.io/uthash/): [BSD Revised](https://troydhanson.github.io/uthash/license.html)
       +
       +[cJSON](https://github.com/DaveGamble/cJSON): [MIT/X](https://github.com/DaveGamble/cJSON/blob/master/LICENSE)
   DIR diff --git a/button.c b/button.c
       t@@ -0,0 +1,257 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json)
       +{
       +    LtkButtonTheme *button_theme = malloc(sizeof(LtkButtonTheme));
       +    cJSON *normal_json = cJSON_GetObjectItem(button_json, "normal");
       +    if (!normal_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +    }
       +    cJSON *border_width = cJSON_GetObjectItem(normal_json, "border-width");
       +    cJSON *font_size = cJSON_GetObjectItem(normal_json, "font-size");
       +    cJSON *border_color = cJSON_GetObjectItem(normal_json, "border-color");
       +    cJSON *fill_color = cJSON_GetObjectItem(normal_json, "fill-color");
       +    cJSON *padding_left = cJSON_GetObjectItem(normal_json, "padding-left");
       +    cJSON *padding_right = cJSON_GetObjectItem(normal_json, "padding-right");
       +    cJSON *padding_top = cJSON_GetObjectItem(normal_json, "padding-top");
       +    cJSON *padding_bottom = cJSON_GetObjectItem(normal_json, "padding-bottom");
       +
       +    button_theme->border_width = border_width != NULL ? border_width->valueint : 0;
       +    button_theme->font_size = font_size != NULL ? font_size->valueint : 20;
       +    button_theme->border_color = ltk_create_xcolor(border_color->valuestring);
       +    button_theme->fill_color = ltk_create_xcolor(fill_color->valuestring);
       +    button_theme->padding_left = padding_left != NULL ? padding_left->valueint : 0;
       +    button_theme->padding_right = padding_right != NULL ? padding_right->valueint : 0;
       +    button_theme->padding_top = padding_top != NULL ? padding_top->valueint : 0;
       +    button_theme->padding_bottom = padding_bottom != NULL ? padding_bottom->valueint : 0;
       +
       +    cJSON *hover_json = cJSON_GetObjectItem(button_json, "hover");
       +    if (!hover_json)
       +        {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +    }
       +    cJSON *border_width_hover = cJSON_GetObjectItem(hover_json, "border-width");
       +    cJSON *font_size_hover = cJSON_GetObjectItem(hover_json, "font-size");
       +    cJSON *border_color_hover = cJSON_GetObjectItem(hover_json, "border-color");
       +    cJSON *fill_color_hover = cJSON_GetObjectItem(hover_json, "fill-color");
       +
       +    button_theme->border_width_hover = border_width_hover != NULL ? border_width_hover->valueint : button_theme->border_width;
       +    button_theme->font_size_hover = font_size_hover != NULL ? font_size_hover->valueint : button_theme->font_size;
       +    button_theme->border_color_hover = border_color_hover != NULL ? ltk_create_xcolor(border_color_hover->valuestring) : button_theme->border_color;
       +    button_theme->fill_color_hover = fill_color_hover != NULL ? ltk_create_xcolor(fill_color_hover->valuestring) : button_theme->fill_color;
       +
       +    cJSON *pressed_json = cJSON_GetObjectItem(button_json, "pressed");
       +    if (!pressed_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +    }
       +    cJSON *border_width_pressed = cJSON_GetObjectItem(pressed_json, "border-width");
       +    cJSON *font_size_pressed = cJSON_GetObjectItem(pressed_json, "font-size");
       +    cJSON *border_color_pressed = cJSON_GetObjectItem(pressed_json, "border-color");
       +    cJSON *fill_color_pressed = cJSON_GetObjectItem(pressed_json, "fill-color");
       +
       +    button_theme->border_width_pressed = border_width_pressed != NULL ? border_width_pressed->valueint : button_theme->border_width;
       +    button_theme->font_size_pressed = font_size_hover != NULL ? font_size_pressed->valueint : button_theme->font_size;
       +    button_theme->border_color_pressed = border_color_pressed != NULL ? ltk_create_xcolor(border_color_pressed->valuestring) : button_theme->border_color;
       +    button_theme->fill_color_pressed = fill_color_pressed != NULL ? ltk_create_xcolor(fill_color_pressed->valuestring) : button_theme->fill_color;
       +
       +    cJSON *active_json = cJSON_GetObjectItem(button_json, "active");
       +    if (!active_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +    }
       +    cJSON *border_width_active = cJSON_GetObjectItem(active_json, "border-width");
       +    cJSON *font_size_active = cJSON_GetObjectItem(active_json, "font-size");
       +    cJSON *border_color_active = cJSON_GetObjectItem(active_json, "border-color");
       +    cJSON *fill_color_active = cJSON_GetObjectItem(active_json, "fill-color");
       +
       +    button_theme->border_width_active = border_width_active != NULL ? border_width_active->valueint : button_theme->border_width;
       +    button_theme->font_size_active = font_size_active != NULL ? font_size_active->valueint : button_theme->font_size;
       +    button_theme->border_color_active = border_color_active != NULL ? ltk_create_xcolor(border_color_active->valuestring) : button_theme->border_color;
       +    button_theme->fill_color_active = fill_color_active != NULL ? ltk_create_xcolor(fill_color_active->valuestring) : button_theme->fill_color;
       +
       +    cJSON *disabled_json = cJSON_GetObjectItem(button_json, "disabled");
       +    if (!disabled_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +    }
       +    cJSON *border_width_disabled = cJSON_GetObjectItem(disabled_json, "border-width");
       +    cJSON *font_size_disabled = cJSON_GetObjectItem(disabled_json, "font-size");
       +    cJSON *border_color_disabled = cJSON_GetObjectItem(disabled_json, "border-color");
       +    cJSON *fill_color_disabled = cJSON_GetObjectItem(disabled_json, "fill-color");
       +
       +    button_theme->border_width_disabled = border_width_disabled != NULL ? border_width_disabled->valueint : button_theme->border_width;
       +    button_theme->font_size_disabled = font_size_disabled != NULL ? font_size_disabled->valueint : button_theme->font_size;
       +    button_theme->border_color_disabled = border_color_disabled != NULL ? ltk_create_xcolor(border_color_disabled->valuestring) : button_theme->border_color;
       +    button_theme->fill_color_disabled = fill_color_disabled != NULL ? ltk_create_xcolor(fill_color_disabled->valuestring) : button_theme->fill_color;
       +
       +    return button_theme;
       +}
       +
       +void ltk_draw_button(void *widget)
       +{
       +    LtkButton *button = widget;
       +    LtkButtonTheme *theme = ltk_global->theme->button;
       +    LtkWindow *window = button->widget.window;
       +    LtkRect rect = button->widget.rect;
       +    XColor border_color;
       +    XColor fill_color;
       +    int border_width;
       +    switch (button->widget.state)
       +    {
       +    case NORMAL:
       +        border_color = theme->border_color;
       +        fill_color = theme->fill_color;
       +        border_width = theme->border_width;
       +        break;
       +    case HOVERACTIVE:
       +    case HOVER:
       +        border_color = theme->border_color_hover;
       +        fill_color = theme->fill_color_hover;
       +        border_width = theme->border_width_hover;
       +        break;
       +    case PRESSED:
       +        border_color = theme->border_color_pressed;
       +        fill_color = theme->fill_color_pressed;
       +        border_width = theme->border_width_pressed;
       +        break;
       +    case ACTIVE:
       +        border_color = theme->border_color_active;
       +        fill_color = theme->fill_color_active;
       +        border_width = theme->border_width_active;
       +        break;
       +    case DISABLED:
       +        border_color = theme->border_color_disabled;
       +        fill_color = theme->fill_color_disabled;
       +        border_width = theme->border_width_disabled;
       +        break;
       +    default:
       +        ltk_fatal("No style found for button!\n");
       +    }
       +    XSetForeground(ltk_global->display, window->gc, fill_color.pixel);
       +    XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       +    XSetForeground(ltk_global->display, window->gc, border_color.pixel);
       +    XSetLineAttributes(ltk_global->display, window->gc, border_width, LineSolid, CapButt, JoinMiter);
       +    XDrawRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       +}
       +
       +LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void))
       +{
       +    LtkButton *button = malloc(sizeof(LtkButton));
       +
       +    if (button == NULL)
       +    {
       +        printf("Button could not be created.\n");
       +            exit(1);
       +    }
       +
       +    button->widget.window = window;
       +    button->widget.parent = NULL;
       +    button->widget.active_widget = NULL;
       +    button->widget.hover_widget = NULL;
       +    button->widget.key_func = &ltk_button_key_event;
       +    button->widget.mouse_func = &ltk_button_mouse_event;
       +    button->widget.update_function = NULL;
       +    button->widget.draw_function = &ltk_draw_button;
       +    button->widget.destroy_function = &ltk_destroy_button;
       +    button->widget.rect.x = 0;
       +    button->widget.rect.y = 0;
       +    /* For testing, will default to size of text once text is implemented */
       +    button->widget.rect.w = 100;
       +    button->widget.rect.h = 100;
       +    button->widget.state = NORMAL;
       +
       +    button->callback = callback;
       +    button->text = strdup(text);
       +
       +    return button;
       +}
       +
       +void ltk_destroy_button(void *widget)
       +{
       +    LtkButton *button = (LtkButton *)widget;
       +    if (!button)
       +    {
       +        printf("Tried to destroy NULL button.\n");
       +    }
       +    free(button->text);
       +    free(button);
       +}
       +
       +void ltk_button_key_event(void *widget, XEvent event)
       +{
       +}
       +
       +void ltk_button_mouse_event(void *widget, XEvent event)
       +{
       +    LtkButton *button = widget;
       +    if (button->widget.state == DISABLED)
       +    {
       +        return;
       +    }
       +    if (event.type == ButtonPress && event.xbutton.button == 1)
       +    {
       +        LtkWidget *parent = button->widget.parent;
       +        if (parent)
       +        {
       +            ltk_remove_active_widget(parent);
       +            parent->active_widget = button;
       +        }
       +        button->widget.state = PRESSED;
       +        ltk_draw_button(button);
       +    }
       +    else if (event.type == ButtonRelease)
       +    {
       +        if (button->widget.state == PRESSED)
       +        {
       +            button->widget.state = HOVERACTIVE;
       +            ltk_draw_button(button);
       +        }
       +    }
       +    else if (event.type == MotionNotify)
       +    {
       +        if (button->widget.state == NORMAL || button->widget.state == ACTIVE)
       +        {
       +            if (button->widget.state == ACTIVE)
       +            {
       +                button->widget.state = HOVERACTIVE;
       +            }
       +            else
       +            {
       +                button->widget.state = HOVER;
       +            }
       +            LtkWidget *parent = button->widget.parent;
       +            LtkWidget *hover_widget;
       +            if (parent)
       +            {
       +                ltk_remove_hover_widget(parent);
       +                parent->hover_widget = button;
       +            }
       +            ltk_draw_button(button);
       +        }
       +    }
       +}
   DIR diff --git a/button.h b/button.h
       t@@ -0,0 +1,78 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_BUTTON_H_
       +#define _LTK_BUTTON_H_
       +
       +#include "widget.h"
       +
       +typedef struct
       +{
       +    LtkWidget widget;
       +    void (*callback)(void);
       +    char *text;
       +} LtkButton;
       +
       +typedef struct LtkButtonTheme
       +{
       +    int border_width;
       +    int font_size;
       +    XColor border_color;
       +    XColor fill_color;
       +    int padding_left;
       +    int padding_right;
       +    int padding_top;
       +    int padding_bottom;
       +
       +    int border_width_hover;
       +    int font_size_hover;
       +    XColor border_color_hover;
       +    XColor fill_color_hover;
       +
       +    int border_width_pressed;
       +    int font_size_pressed;
       +    XColor border_color_pressed;
       +    XColor fill_color_pressed;
       +
       +    int border_width_active;
       +    int font_size_active;
       +    XColor border_color_active;
       +    XColor fill_color_active;
       +
       +    int border_width_disabled;
       +    int font_size_disabled;
       +    XColor border_color_disabled;
       +    XColor fill_color_disabled;
       +
       +} LtkButtonTheme;
       +
       +LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json);
       +void ltk_draw_button(void *widget);
       +LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void));
       +void ltk_button_key_event(void *widget, XEvent event);
       +void ltk_button_mouse_event(void *widget, XEvent event);
       +void ltk_destroy_button(void *widget);
       +void ltk_button_key_event(void *widget, XEvent event);
       +void ltk_button_mouse_event(void *widget, XEvent event);
       +
       +#endif
   DIR diff --git a/cJSON.c b/cJSON.c
       t@@ -0,0 +1,1492 @@
       +/*
       +  Copyright (c) 2009 Dave Gamble
       +
       +  Permission is hereby granted, free of charge, to any person obtaining a copy
       +  of this software and associated documentation files (the "Software"), to deal
       +  in the Software without restriction, including without limitation the rights
       +  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +  copies of the Software, and to permit persons to whom the Software is
       +  furnished to do so, subject to the following conditions:
       +
       +  The above copyright notice and this permission notice shall be included in
       +  all copies or substantial portions of the Software.
       +
       +  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       +  THE SOFTWARE.
       +*/
       +
       +/* cJSON */
       +/* JSON parser in C. */
       +
       +#include <string.h>
       +#include <stdio.h>
       +#include <math.h>
       +#include <stdlib.h>
       +#include <float.h>
       +#include <limits.h>
       +#include <ctype.h>
       +#include "cJSON.h"
       +
       +static const char *global_ep;
       +
       +const char *cJSON_GetErrorPtr(void)
       +{
       +        return global_ep;
       +}
       +
       +static int cJSON_strcasecmp(const char *s1, const char *s2)
       +{
       +        if (!s1)
       +                return (s1 == s2) ? 0 : 1;
       +        if (!s2)
       +                return 1;
       +        for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)
       +                if (*s1 == 0)
       +                        return 0;
       +        return tolower(*(const unsigned char *)s1) -
       +            tolower(*(const unsigned char *)s2);
       +}
       +
       +static void *(*cJSON_malloc) (size_t sz) = malloc;
       +static void (*cJSON_free) (void *ptr) = free;
       +
       +static char *cJSON_strdup(const char *str)
       +{
       +        size_t len;
       +        char *copy;
       +
       +        len = strlen(str) + 1;
       +        if (!(copy = (char *)cJSON_malloc(len)))
       +                return 0;
       +        memcpy(copy, str, len);
       +        return copy;
       +}
       +
       +void cJSON_InitHooks(cJSON_Hooks * hooks)
       +{
       +        if (!hooks) {                /* Reset hooks */
       +                cJSON_malloc = malloc;
       +                cJSON_free = free;
       +                return;
       +        }
       +
       +        cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
       +        cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
       +}
       +
       +/* Internal constructor. */
       +static cJSON *cJSON_New_Item(void)
       +{
       +        cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON));
       +        if (node)
       +                memset(node, 0, sizeof(cJSON));
       +        return node;
       +}
       +
       +/* Delete a cJSON structure. */
       +void cJSON_Delete(cJSON * c)
       +{
       +        cJSON *next;
       +        while (c) {
       +                next = c->next;
       +                if (!(c->type & cJSON_IsReference) && c->child)
       +                        cJSON_Delete(c->child);
       +                if (!(c->type & cJSON_IsReference) && c->valuestring)
       +                        cJSON_free(c->valuestring);
       +                if (!(c->type & cJSON_StringIsConst) && c->string)
       +                        cJSON_free(c->string);
       +                cJSON_free(c);
       +                c = next;
       +        }
       +}
       +
       +/* Parse the input text to generate a number, and populate the result into item. */
       +static const char *parse_number(cJSON * item, const char *num)
       +{
       +        double n = 0, sign = 1, scale = 0;
       +        int subscale = 0, signsubscale = 1;
       +
       +        if (*num == '-')
       +                sign = -1, num++;        /* Has sign? */
       +        if (*num == '0')
       +                num++;                /* is zero */
       +        if (*num >= '1' && *num <= '9')
       +                do
       +                        n = (n * 10.0) + (*num++ - '0');
       +                while (*num >= '0' && *num <= '9');        /* Number? */
       +        if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
       +                num++;
       +                do
       +                        n = (n * 10.0) + (*num++ - '0'), scale--;
       +                while (*num >= '0' && *num <= '9');
       +        }                        /* Fractional part? */
       +        if (*num == 'e' || *num == 'E') {        /* Exponent? */
       +                num++;
       +                if (*num == '+')
       +                        num++;
       +                else if (*num == '-')
       +                        signsubscale = -1, num++;        /* With sign? */
       +                while (*num >= '0' && *num <= '9')
       +                        subscale = (subscale * 10) + (*num++ - '0');        /* Number? */
       +        }
       +
       +        n = sign * n * pow(10.0, (scale + subscale * signsubscale));        /* number = +/- number.fraction * 10^+/- exponent */
       +
       +        item->valuedouble = n;
       +        item->valueint = (int)n;
       +        item->type = cJSON_Number;
       +        return num;
       +}
       +
       +static int pow2gt(int x)
       +{
       +        --x;
       +        x |= x >> 1;
       +        x |= x >> 2;
       +        x |= x >> 4;
       +        x |= x >> 8;
       +        x |= x >> 16;
       +        return x + 1;
       +}
       +
       +typedef struct {
       +        char *buffer;
       +        int length;
       +        int offset;
       +} printbuffer;
       +
       +static char *ensure(printbuffer * p, int needed)
       +{
       +        char *newbuffer;
       +        int newsize;
       +        if (!p || !p->buffer)
       +                return 0;
       +        needed += p->offset;
       +        if (needed <= p->length)
       +                return p->buffer + p->offset;
       +
       +        newsize = pow2gt(needed);
       +        newbuffer = (char *)cJSON_malloc(newsize);
       +        if (!newbuffer) {
       +                cJSON_free(p->buffer);
       +                p->length = 0, p->buffer = 0;
       +                return 0;
       +        }
       +        if (newbuffer)
       +                memcpy(newbuffer, p->buffer, p->length);
       +        cJSON_free(p->buffer);
       +        p->length = newsize;
       +        p->buffer = newbuffer;
       +        return newbuffer + p->offset;
       +}
       +
       +static int update(printbuffer * p)
       +{
       +        char *str;
       +        if (!p || !p->buffer)
       +                return 0;
       +        str = p->buffer + p->offset;
       +        return p->offset + strlen(str);
       +}
       +
       +/* Render the number nicely from the given item into a string. */
       +static char *print_number(cJSON * item, printbuffer * p)
       +{
       +        char *str = 0;
       +        double d = item->valuedouble;
       +        if (d == 0) {
       +                if (p)
       +                        str = ensure(p, 2);
       +                else
       +                        str = (char *)cJSON_malloc(2);        /* special case for 0. */
       +                if (str)
       +                        strcpy(str, "0");
       +        } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON
       +                   && d <= INT_MAX && d >= INT_MIN) {
       +                if (p)
       +                        str = ensure(p, 21);
       +                else
       +                        str = (char *)cJSON_malloc(21);        /* 2^64+1 can be represented in 21 chars. */
       +                if (str)
       +                        sprintf(str, "%d", item->valueint);
       +        } else {
       +                if (p)
       +                        str = ensure(p, 64);
       +                else
       +                        str = (char *)cJSON_malloc(64);        /* This is a nice tradeoff. */
       +                if (str) {
       +                        if (d * 0 != 0)
       +                                sprintf(str, "null");        /* This checks for NaN and Infinity */
       +                        else if (fabs(floor(d) - d) <= DBL_EPSILON
       +                                 && fabs(d) < 1.0e60)
       +                                sprintf(str, "%.0f", d);
       +                        else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
       +                                sprintf(str, "%e", d);
       +                        else
       +                                sprintf(str, "%f", d);
       +                }
       +        }
       +        return str;
       +}
       +
       +static unsigned parse_hex4(const char *str)
       +{
       +        unsigned h = 0;
       +        if (*str >= '0' && *str <= '9')
       +                h += (*str) - '0';
       +        else if (*str >= 'A' && *str <= 'F')
       +                h += 10 + (*str) - 'A';
       +        else if (*str >= 'a' && *str <= 'f')
       +                h += 10 + (*str) - 'a';
       +        else
       +                return 0;
       +        h = h << 4;
       +        str++;
       +        if (*str >= '0' && *str <= '9')
       +                h += (*str) - '0';
       +        else if (*str >= 'A' && *str <= 'F')
       +                h += 10 + (*str) - 'A';
       +        else if (*str >= 'a' && *str <= 'f')
       +                h += 10 + (*str) - 'a';
       +        else
       +                return 0;
       +        h = h << 4;
       +        str++;
       +        if (*str >= '0' && *str <= '9')
       +                h += (*str) - '0';
       +        else if (*str >= 'A' && *str <= 'F')
       +                h += 10 + (*str) - 'A';
       +        else if (*str >= 'a' && *str <= 'f')
       +                h += 10 + (*str) - 'a';
       +        else
       +                return 0;
       +        h = h << 4;
       +        str++;
       +        if (*str >= '0' && *str <= '9')
       +                h += (*str) - '0';
       +        else if (*str >= 'A' && *str <= 'F')
       +                h += 10 + (*str) - 'A';
       +        else if (*str >= 'a' && *str <= 'f')
       +                h += 10 + (*str) - 'a';
       +        else
       +                return 0;
       +        return h;
       +}
       +
       +/* Parse the input text into an unescaped cstring, and populate item. */
       +static const unsigned char firstByteMark[7] =
       +    { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
       +static const char *parse_string(cJSON * item, const char *str, const char **ep)
       +{
       +        const char *ptr = str + 1, *end_ptr = str + 1;
       +        char *ptr2;
       +        char *out;
       +        int len = 0;
       +        unsigned uc, uc2;
       +        if (*str != '\"') {
       +                *ep = str;
       +                return 0;
       +        }
       +        /* not a string! */
       +        while (*end_ptr != '\"' && *end_ptr && ++len)
       +                if (*end_ptr++ == '\\')
       +                        end_ptr++;        /* Skip escaped quotes. */
       +
       +        out = (char *)cJSON_malloc(len + 1);        /* This is how long we need for the string, roughly. */
       +        if (!out)
       +                return 0;
       +        item->valuestring = out;        /* assign here so out will be deleted during cJSON_Delete() later */
       +        item->type = cJSON_String;
       +
       +        ptr = str + 1;
       +        ptr2 = out;
       +        while (ptr < end_ptr) {
       +                if (*ptr != '\\')
       +                        *ptr2++ = *ptr++;
       +                else {
       +                        ptr++;
       +                        switch (*ptr) {
       +                        case 'b':
       +                                *ptr2++ = '\b';
       +                                break;
       +                        case 'f':
       +                                *ptr2++ = '\f';
       +                                break;
       +                        case 'n':
       +                                *ptr2++ = '\n';
       +                                break;
       +                        case 'r':
       +                                *ptr2++ = '\r';
       +                                break;
       +                        case 't':
       +                                *ptr2++ = '\t';
       +                                break;
       +                        case 'u':        /* transcode utf16 to utf8. */
       +                                uc = parse_hex4(ptr + 1);
       +                                ptr += 4;        /* get the unicode char. */
       +                                if (ptr >= end_ptr) {
       +                                        *ep = str;
       +                                        return 0;
       +                                }
       +                                /* invalid */
       +                                if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) {
       +                                        *ep = str;
       +                                        return 0;
       +                                }
       +                                /* check for invalid.   */
       +                                if (uc >= 0xD800 && uc <= 0xDBFF) {        /* UTF16 surrogate pairs.       */
       +                                        if (ptr + 6 > end_ptr) {
       +                                                *ep = str;
       +                                                return 0;
       +                                        }        /* invalid */
       +                                        if (ptr[1] != '\\' || ptr[2] != 'u') {
       +                                                *ep = str;
       +                                                return 0;
       +                                        }        /* missing second-half of surrogate.    */
       +                                        uc2 = parse_hex4(ptr + 3);
       +                                        ptr += 6;
       +                                        if (uc2 < 0xDC00 || uc2 > 0xDFFF) {
       +                                                *ep = str;
       +                                                return 0;
       +                                        }        /* invalid second-half of surrogate.    */
       +                                        uc = 0x10000 +
       +                                            (((uc & 0x3FF) << 10) |
       +                                             (uc2 & 0x3FF));
       +                                }
       +
       +                                len = 4;
       +                                if (uc < 0x80)
       +                                        len = 1;
       +                                else if (uc < 0x800)
       +                                        len = 2;
       +                                else if (uc < 0x10000)
       +                                        len = 3;
       +                                ptr2 += len;
       +
       +                                switch (len) {
       +                                case 4:
       +                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       +                                        uc >>= 6;
       +                                case 3:
       +                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       +                                        uc >>= 6;
       +                                case 2:
       +                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       +                                        uc >>= 6;
       +                                case 1:
       +                                        *--ptr2 = (uc | firstByteMark[len]);
       +                                }
       +                                ptr2 += len;
       +                                break;
       +                        default:
       +                                *ptr2++ = *ptr;
       +                                break;
       +                        }
       +                        ptr++;
       +                }
       +        }
       +        *ptr2 = 0;
       +        if (*ptr == '\"')
       +                ptr++;
       +        return ptr;
       +}
       +
       +/* Render the cstring provided to an escaped version that can be printed. */
       +static char *print_string_ptr(const char *str, printbuffer * p)
       +{
       +        const char *ptr;
       +        char *ptr2, *out;
       +        int len = 0, flag = 0;
       +        unsigned char token;
       +
       +        if (!str) {
       +                if (p)
       +                        out = ensure(p, 3);
       +                else
       +                        out = (char *)cJSON_malloc(3);
       +                if (!out)
       +                        return 0;
       +                strcpy(out, "\"\"");
       +                return out;
       +        }
       +
       +        for (ptr = str; *ptr; ptr++)
       +                flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"')
       +                         || (*ptr == '\\')) ? 1 : 0;
       +        if (!flag) {
       +                len = ptr - str;
       +                if (p)
       +                        out = ensure(p, len + 3);
       +                else
       +                        out = (char *)cJSON_malloc(len + 3);
       +                if (!out)
       +                        return 0;
       +                ptr2 = out;
       +                *ptr2++ = '\"';
       +                strcpy(ptr2, str);
       +                ptr2[len] = '\"';
       +                ptr2[len + 1] = 0;
       +                return out;
       +        }
       +
       +        ptr = str;
       +        while ((token = *ptr) && ++len) {
       +                if (strchr("\"\\\b\f\n\r\t", token))
       +                        len++;
       +                else if (token < 32)
       +                        len += 5;
       +                ptr++;
       +        }
       +
       +        if (p)
       +                out = ensure(p, len + 3);
       +        else
       +                out = (char *)cJSON_malloc(len + 3);
       +        if (!out)
       +                return 0;
       +
       +        ptr2 = out;
       +        ptr = str;
       +        *ptr2++ = '\"';
       +        while (*ptr) {
       +                if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
       +                        *ptr2++ = *ptr++;
       +                else {
       +                        *ptr2++ = '\\';
       +                        switch (token = *ptr++) {
       +                        case '\\':
       +                                *ptr2++ = '\\';
       +                                break;
       +                        case '\"':
       +                                *ptr2++ = '\"';
       +                                break;
       +                        case '\b':
       +                                *ptr2++ = 'b';
       +                                break;
       +                        case '\f':
       +                                *ptr2++ = 'f';
       +                                break;
       +                        case '\n':
       +                                *ptr2++ = 'n';
       +                                break;
       +                        case '\r':
       +                                *ptr2++ = 'r';
       +                                break;
       +                        case '\t':
       +                                *ptr2++ = 't';
       +                                break;
       +                        default:
       +                                sprintf(ptr2, "u%04x", token);
       +                                ptr2 += 5;
       +                                break;        /* escape and print */
       +                        }
       +                }
       +        }
       +        *ptr2++ = '\"';
       +        *ptr2++ = 0;
       +        return out;
       +}
       +
       +/* Invote print_string_ptr (which is useful) on an item. */
       +static char *print_string(cJSON * item, printbuffer * p)
       +{
       +        return print_string_ptr(item->valuestring, p);
       +}
       +
       +/* Predeclare these prototypes. */
       +static const char *parse_value(cJSON * item, const char *value,
       +                               const char **ep);
       +static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p);
       +static const char *parse_array(cJSON * item, const char *value,
       +                               const char **ep);
       +static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p);
       +static const char *parse_object(cJSON * item, const char *value,
       +                                const char **ep);
       +static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p);
       +
       +/* Utility to jump whitespace and cr/lf */
       +static const char *skip(const char *in)
       +{
       +        while (in && *in && (unsigned char)*in <= 32)
       +                in++;
       +        return in;
       +}
       +
       +/* Parse an object - create a new root, and populate. */
       +cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
       +                           int require_null_terminated)
       +{
       +        const char *end = 0, **ep =
       +            return_parse_end ? return_parse_end : &global_ep;
       +        cJSON *c = cJSON_New_Item();
       +        *ep = 0;
       +        if (!c)
       +                return 0;        /* memory fail */
       +
       +        end = parse_value(c, skip(value), ep);
       +        if (!end) {
       +                cJSON_Delete(c);
       +                return 0;
       +        }
       +
       +        /* parse failure. ep is set. */
       +        /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
       +        if (require_null_terminated) {
       +                end = skip(end);
       +                if (*end) {
       +                        cJSON_Delete(c);
       +                        *ep = end;
       +                        return 0;
       +                }
       +        }
       +        if (return_parse_end)
       +                *return_parse_end = end;
       +        return c;
       +}
       +
       +/* Default options for cJSON_Parse */
       +cJSON *cJSON_Parse(const char *value)
       +{
       +        return cJSON_ParseWithOpts(value, 0, 0);
       +}
       +
       +/* Render a cJSON item/entity/structure to text. */
       +char *cJSON_Print(cJSON * item)
       +{
       +        return print_value(item, 0, 1, 0);
       +}
       +
       +char *cJSON_PrintUnformatted(cJSON * item)
       +{
       +        return print_value(item, 0, 0, 0);
       +}
       +
       +char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt)
       +{
       +        printbuffer p;
       +        p.buffer = (char *)cJSON_malloc(prebuffer);
       +        p.length = prebuffer;
       +        p.offset = 0;
       +        return print_value(item, 0, fmt, &p);
       +}
       +
       +/* Parser core - when encountering text, process appropriately. */
       +static const char *parse_value(cJSON * item, const char *value, const char **ep)
       +{
       +        if (!value)
       +                return 0;        /* Fail on null. */
       +        if (!strncmp(value, "null", 4)) {
       +                item->type = cJSON_NULL;
       +                return value + 4;
       +        }
       +        if (!strncmp(value, "false", 5)) {
       +                item->type = cJSON_False;
       +                return value + 5;
       +        }
       +        if (!strncmp(value, "true", 4)) {
       +                item->type = cJSON_True;
       +                item->valueint = 1;
       +                return value + 4;
       +        }
       +        if (*value == '\"') {
       +                return parse_string(item, value, ep);
       +        }
       +        if (*value == '-' || (*value >= '0' && *value <= '9')) {
       +                return parse_number(item, value);
       +        }
       +        if (*value == '[') {
       +                return parse_array(item, value, ep);
       +        }
       +        if (*value == '{') {
       +                return parse_object(item, value, ep);
       +        }
       +
       +        *ep = value;
       +        return 0;                /* failure. */
       +}
       +
       +/* Render a value to text. */
       +static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p)
       +{
       +        char *out = 0;
       +        if (!item)
       +                return 0;
       +        if (p) {
       +                switch ((item->type) & 255) {
       +                case cJSON_NULL:
       +                        {
       +                                out = ensure(p, 5);
       +                                if (out)
       +                                        strcpy(out, "null");
       +                                break;
       +                        }
       +                case cJSON_False:
       +                        {
       +                                out = ensure(p, 6);
       +                                if (out)
       +                                        strcpy(out, "false");
       +                                break;
       +                        }
       +                case cJSON_True:
       +                        {
       +                                out = ensure(p, 5);
       +                                if (out)
       +                                        strcpy(out, "true");
       +                                break;
       +                        }
       +                case cJSON_Number:
       +                        out = print_number(item, p);
       +                        break;
       +                case cJSON_String:
       +                        out = print_string(item, p);
       +                        break;
       +                case cJSON_Array:
       +                        out = print_array(item, depth, fmt, p);
       +                        break;
       +                case cJSON_Object:
       +                        out = print_object(item, depth, fmt, p);
       +                        break;
       +                }
       +        } else {
       +                switch ((item->type) & 255) {
       +                case cJSON_NULL:
       +                        out = cJSON_strdup("null");
       +                        break;
       +                case cJSON_False:
       +                        out = cJSON_strdup("false");
       +                        break;
       +                case cJSON_True:
       +                        out = cJSON_strdup("true");
       +                        break;
       +                case cJSON_Number:
       +                        out = print_number(item, 0);
       +                        break;
       +                case cJSON_String:
       +                        out = print_string(item, 0);
       +                        break;
       +                case cJSON_Array:
       +                        out = print_array(item, depth, fmt, 0);
       +                        break;
       +                case cJSON_Object:
       +                        out = print_object(item, depth, fmt, 0);
       +                        break;
       +                }
       +        }
       +        return out;
       +}
       +
       +/* Build an array from input text. */
       +static const char *parse_array(cJSON * item, const char *value, const char **ep)
       +{
       +        cJSON *child;
       +        if (*value != '[') {
       +                *ep = value;
       +                return 0;
       +        }
       +        /* not an array! */
       +        item->type = cJSON_Array;
       +        value = skip(value + 1);
       +        if (*value == ']')
       +                return value + 1;        /* empty array. */
       +
       +        item->child = child = cJSON_New_Item();
       +        if (!item->child)
       +                return 0;        /* memory fail */
       +        value = skip(parse_value(child, skip(value), ep));        /* skip any spacing, get the value. */
       +        if (!value)
       +                return 0;
       +
       +        while (*value == ',') {
       +                cJSON *new_item;
       +                if (!(new_item = cJSON_New_Item()))
       +                        return 0;        /* memory fail */
       +                child->next = new_item;
       +                new_item->prev = child;
       +                child = new_item;
       +                value = skip(parse_value(child, skip(value + 1), ep));
       +                if (!value)
       +                        return 0;        /* memory fail */
       +        }
       +
       +        if (*value == ']')
       +                return value + 1;        /* end of array */
       +        *ep = value;
       +        return 0;                /* malformed. */
       +}
       +
       +/* Render an array to text */
       +static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p)
       +{
       +        char **entries;
       +        char *out = 0, *ptr, *ret;
       +        int len = 5;
       +        cJSON *child = item->child;
       +        int numentries = 0, i = 0, fail = 0;
       +        size_t tmplen = 0;
       +
       +        /* How many entries in the array? */
       +        while (child)
       +                numentries++, child = child->next;
       +        /* Explicitly handle numentries==0 */
       +        if (!numentries) {
       +                if (p)
       +                        out = ensure(p, 3);
       +                else
       +                        out = (char *)cJSON_malloc(3);
       +                if (out)
       +                        strcpy(out, "[]");
       +                return out;
       +        }
       +
       +        if (p) {
       +                /* Compose the output array. */
       +                i = p->offset;
       +                ptr = ensure(p, 1);
       +                if (!ptr)
       +                        return 0;
       +                *ptr = '[';
       +                p->offset++;
       +                child = item->child;
       +                while (child && !fail) {
       +                        print_value(child, depth + 1, fmt, p);
       +                        p->offset = update(p);
       +                        if (child->next) {
       +                                len = fmt ? 2 : 1;
       +                                ptr = ensure(p, len + 1);
       +                                if (!ptr)
       +                                        return 0;
       +                                *ptr++ = ',';
       +                                if (fmt)
       +                                        *ptr++ = ' ';
       +                                *ptr = 0;
       +                                p->offset += len;
       +                        }
       +                        child = child->next;
       +                }
       +                ptr = ensure(p, 2);
       +                if (!ptr)
       +                        return 0;
       +                *ptr++ = ']';
       +                *ptr = 0;
       +                out = (p->buffer) + i;
       +        } else {
       +                /* Allocate an array to hold the values for each */
       +                entries = (char **)cJSON_malloc(numentries * sizeof(char *));
       +                if (!entries)
       +                        return 0;
       +                memset(entries, 0, numentries * sizeof(char *));
       +                /* Retrieve all the results: */
       +                child = item->child;
       +                while (child && !fail) {
       +                        ret = print_value(child, depth + 1, fmt, 0);
       +                        entries[i++] = ret;
       +                        if (ret)
       +                                len += strlen(ret) + 2 + (fmt ? 1 : 0);
       +                        else
       +                                fail = 1;
       +                        child = child->next;
       +                }
       +
       +                /* If we didn't fail, try to malloc the output string */
       +                if (!fail)
       +                        out = (char *)cJSON_malloc(len);
       +                /* If that fails, we fail. */
       +                if (!out)
       +                        fail = 1;
       +
       +                /* Handle failure. */
       +                if (fail) {
       +                        for (i = 0; i < numentries; i++)
       +                                if (entries[i])
       +                                        cJSON_free(entries[i]);
       +                        cJSON_free(entries);
       +                        return 0;
       +                }
       +
       +                /* Compose the output array. */
       +                *out = '[';
       +                ptr = out + 1;
       +                *ptr = 0;
       +                for (i = 0; i < numentries; i++) {
       +                        tmplen = strlen(entries[i]);
       +                        memcpy(ptr, entries[i], tmplen);
       +                        ptr += tmplen;
       +                        if (i != numentries - 1) {
       +                                *ptr++ = ',';
       +                                if (fmt)
       +                                        *ptr++ = ' ';
       +                                *ptr = 0;
       +                        }
       +                        cJSON_free(entries[i]);
       +                }
       +                cJSON_free(entries);
       +                *ptr++ = ']';
       +                *ptr++ = 0;
       +        }
       +        return out;
       +}
       +
       +/* Build an object from the text. */
       +static const char *parse_object(cJSON * item, const char *value,
       +                                const char **ep)
       +{
       +        cJSON *child;
       +        if (*value != '{') {
       +                *ep = value;
       +                return 0;
       +        }
       +        /* not an object! */
       +        item->type = cJSON_Object;
       +        value = skip(value + 1);
       +        if (*value == '}')
       +                return value + 1;        /* empty array. */
       +
       +        item->child = child = cJSON_New_Item();
       +        if (!item->child)
       +                return 0;
       +        value = skip(parse_string(child, skip(value), ep));
       +        if (!value)
       +                return 0;
       +        child->string = child->valuestring;
       +        child->valuestring = 0;
       +        if (*value != ':') {
       +                *ep = value;
       +                return 0;
       +        }                        /* fail! */
       +        value = skip(parse_value(child, skip(value + 1), ep));        /* skip any spacing, get the value. */
       +        if (!value)
       +                return 0;
       +
       +        while (*value == ',') {
       +                cJSON *new_item;
       +                if (!(new_item = cJSON_New_Item()))
       +                        return 0;        /* memory fail */
       +                child->next = new_item;
       +                new_item->prev = child;
       +                child = new_item;
       +                value = skip(parse_string(child, skip(value + 1), ep));
       +                if (!value)
       +                        return 0;
       +                child->string = child->valuestring;
       +                child->valuestring = 0;
       +                if (*value != ':') {
       +                        *ep = value;
       +                        return 0;
       +                }                /* fail! */
       +                value = skip(parse_value(child, skip(value + 1), ep));        /* skip any spacing, get the value. */
       +                if (!value)
       +                        return 0;
       +        }
       +
       +        if (*value == '}')
       +                return value + 1;        /* end of array */
       +        *ep = value;
       +        return 0;                /* malformed. */
       +}
       +
       +/* Render an object to text. */
       +static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p)
       +{
       +        char **entries = 0, **names = 0;
       +        char *out = 0, *ptr, *ret, *str;
       +        int len = 7, i = 0, j;
       +        cJSON *child = item->child;
       +        int numentries = 0, fail = 0;
       +        size_t tmplen = 0;
       +        /* Count the number of entries. */
       +        while (child)
       +                numentries++, child = child->next;
       +        /* Explicitly handle empty object case */
       +        if (!numentries) {
       +                if (p)
       +                        out = ensure(p, fmt ? depth + 4 : 3);
       +                else
       +                        out = (char *)cJSON_malloc(fmt ? depth + 4 : 3);
       +                if (!out)
       +                        return 0;
       +                ptr = out;
       +                *ptr++ = '{';
       +                if (fmt) {
       +                        *ptr++ = '\n';
       +                        for (i = 0; i < depth; i++)
       +                                *ptr++ = '\t';
       +                }
       +                *ptr++ = '}';
       +                *ptr++ = 0;
       +                return out;
       +        }
       +        if (p) {
       +                /* Compose the output: */
       +                i = p->offset;
       +                len = fmt ? 2 : 1;
       +                ptr = ensure(p, len + 1);
       +                if (!ptr)
       +                        return 0;
       +                *ptr++ = '{';
       +                if (fmt)
       +                        *ptr++ = '\n';
       +                *ptr = 0;
       +                p->offset += len;
       +                child = item->child;
       +                depth++;
       +                while (child) {
       +                        if (fmt) {
       +                                ptr = ensure(p, depth);
       +                                if (!ptr)
       +                                        return 0;
       +                                for (j = 0; j < depth; j++)
       +                                        *ptr++ = '\t';
       +                                p->offset += depth;
       +                        }
       +                        print_string_ptr(child->string, p);
       +                        p->offset = update(p);
       +
       +                        len = fmt ? 2 : 1;
       +                        ptr = ensure(p, len);
       +                        if (!ptr)
       +                                return 0;
       +                        *ptr++ = ':';
       +                        if (fmt)
       +                                *ptr++ = '\t';
       +                        p->offset += len;
       +
       +                        print_value(child, depth, fmt, p);
       +                        p->offset = update(p);
       +
       +                        len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
       +                        ptr = ensure(p, len + 1);
       +                        if (!ptr)
       +                                return 0;
       +                        if (child->next)
       +                                *ptr++ = ',';
       +                        if (fmt)
       +                                *ptr++ = '\n';
       +                        *ptr = 0;
       +                        p->offset += len;
       +                        child = child->next;
       +                }
       +                ptr = ensure(p, fmt ? (depth + 1) : 2);
       +                if (!ptr)
       +                        return 0;
       +                if (fmt)
       +                        for (i = 0; i < depth - 1; i++)
       +                                *ptr++ = '\t';
       +                *ptr++ = '}';
       +                *ptr = 0;
       +                out = (p->buffer) + i;
       +        } else {
       +                /* Allocate space for the names and the objects */
       +                entries = (char **)cJSON_malloc(numentries * sizeof(char *));
       +                if (!entries)
       +                        return 0;
       +                names = (char **)cJSON_malloc(numentries * sizeof(char *));
       +                if (!names) {
       +                        cJSON_free(entries);
       +                        return 0;
       +                }
       +                memset(entries, 0, sizeof(char *) * numentries);
       +                memset(names, 0, sizeof(char *) * numentries);
       +
       +                /* Collect all the results into our arrays: */
       +                child = item->child;
       +                depth++;
       +                if (fmt)
       +                        len += depth;
       +                while (child && !fail) {
       +                        names[i] = str = print_string_ptr(child->string, 0);
       +                        entries[i++] = ret = print_value(child, depth, fmt, 0);
       +                        if (str && ret)
       +                                len +=
       +                                    strlen(ret) + strlen(str) + 2 + (fmt ? 2 +
       +                                                                     depth : 0);
       +                        else
       +                                fail = 1;
       +                        child = child->next;
       +                }
       +
       +                /* Try to allocate the output string */
       +                if (!fail)
       +                        out = (char *)cJSON_malloc(len);
       +                if (!out)
       +                        fail = 1;
       +
       +                /* Handle failure */
       +                if (fail) {
       +                        for (i = 0; i < numentries; i++) {
       +                                if (names[i])
       +                                        cJSON_free(names[i]);
       +                                if (entries[i])
       +                                        cJSON_free(entries[i]);
       +                        }
       +                        cJSON_free(names);
       +                        cJSON_free(entries);
       +                        return 0;
       +                }
       +
       +                /* Compose the output: */
       +                *out = '{';
       +                ptr = out + 1;
       +                if (fmt)
       +                        *ptr++ = '\n';
       +                *ptr = 0;
       +                for (i = 0; i < numentries; i++) {
       +                        if (fmt)
       +                                for (j = 0; j < depth; j++)
       +                                        *ptr++ = '\t';
       +                        tmplen = strlen(names[i]);
       +                        memcpy(ptr, names[i], tmplen);
       +                        ptr += tmplen;
       +                        *ptr++ = ':';
       +                        if (fmt)
       +                                *ptr++ = '\t';
       +                        strcpy(ptr, entries[i]);
       +                        ptr += strlen(entries[i]);
       +                        if (i != numentries - 1)
       +                                *ptr++ = ',';
       +                        if (fmt)
       +                                *ptr++ = '\n';
       +                        *ptr = 0;
       +                        cJSON_free(names[i]);
       +                        cJSON_free(entries[i]);
       +                }
       +
       +                cJSON_free(names);
       +                cJSON_free(entries);
       +                if (fmt)
       +                        for (i = 0; i < depth - 1; i++)
       +                                *ptr++ = '\t';
       +                *ptr++ = '}';
       +                *ptr++ = 0;
       +        }
       +        return out;
       +}
       +
       +/* Get Array size/item / object item. */
       +int cJSON_GetArraySize(cJSON * array)
       +{
       +        cJSON *c = array->child;
       +        int i = 0;
       +        while (c)
       +                i++, c = c->next;
       +        return i;
       +}
       +
       +cJSON *cJSON_GetArrayItem(cJSON * array, int item)
       +{
       +        cJSON *c = array ? array->child : 0;
       +        while (c && item > 0)
       +                item--, c = c->next;
       +        return c;
       +}
       +
       +cJSON *cJSON_GetObjectItem(cJSON * object, const char *string)
       +{
       +        cJSON *c = object ? object->child : 0;
       +        while (c && cJSON_strcasecmp(c->string, string))
       +                c = c->next;
       +        return c;
       +}
       +
       +int cJSON_HasObjectItem(cJSON * object, const char *string)
       +{
       +        return cJSON_GetObjectItem(object, string) ? 1 : 0;
       +}
       +
       +/* Utility for array list handling. */
       +static void suffix_object(cJSON * prev, cJSON * item)
       +{
       +        prev->next = item;
       +        item->prev = prev;
       +}
       +
       +/* Utility for handling references. */
       +static cJSON *create_reference(cJSON * item)
       +{
       +        cJSON *ref = cJSON_New_Item();
       +        if (!ref)
       +                return 0;
       +        memcpy(ref, item, sizeof(cJSON));
       +        ref->string = 0;
       +        ref->type |= cJSON_IsReference;
       +        ref->next = ref->prev = 0;
       +        return ref;
       +}
       +
       +/* Add item to array/object. */
       +void cJSON_AddItemToArray(cJSON * array, cJSON * item)
       +{
       +        cJSON *c = array->child;
       +        if (!item)
       +                return;
       +        if (!c) {
       +                array->child = item;
       +        } else {
       +                while (c && c->next)
       +                        c = c->next;
       +                suffix_object(c, item);
       +        }
       +}
       +
       +void cJSON_AddItemToObject(cJSON * object, const char *string, cJSON * item)
       +{
       +        if (!item)
       +                return;
       +        if (item->string)
       +                cJSON_free(item->string);
       +        item->string = cJSON_strdup(string);
       +        cJSON_AddItemToArray(object, item);
       +}
       +
       +void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item)
       +{
       +        if (!item)
       +                return;
       +        if (!(item->type & cJSON_StringIsConst) && item->string)
       +                cJSON_free(item->string);
       +        item->string = (char *)string;
       +        item->type |= cJSON_StringIsConst;
       +        cJSON_AddItemToArray(object, item);
       +}
       +
       +void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item)
       +{
       +        cJSON_AddItemToArray(array, create_reference(item));
       +}
       +
       +void
       +cJSON_AddItemReferenceToObject(cJSON * object, const char *string, cJSON * item)
       +{
       +        cJSON_AddItemToObject(object, string, create_reference(item));
       +}
       +
       +cJSON *cJSON_DetachItemFromArray(cJSON * array, int which)
       +{
       +        cJSON *c = array->child;
       +        while (c && which > 0)
       +                c = c->next, which--;
       +        if (!c)
       +                return 0;
       +        if (c->prev)
       +                c->prev->next = c->next;
       +        if (c->next)
       +                c->next->prev = c->prev;
       +        if (c == array->child)
       +                array->child = c->next;
       +        c->prev = c->next = 0;
       +        return c;
       +}
       +
       +void cJSON_DeleteItemFromArray(cJSON * array, int which)
       +{
       +        cJSON_Delete(cJSON_DetachItemFromArray(array, which));
       +}
       +
       +cJSON *cJSON_DetachItemFromObject(cJSON * object, const char *string)
       +{
       +        int i = 0;
       +        cJSON *c = object->child;
       +        while (c && cJSON_strcasecmp(c->string, string))
       +                i++, c = c->next;
       +        if (c)
       +                return cJSON_DetachItemFromArray(object, i);
       +        return 0;
       +}
       +
       +void cJSON_DeleteItemFromObject(cJSON * object, const char *string)
       +{
       +        cJSON_Delete(cJSON_DetachItemFromObject(object, string));
       +}
       +
       +/* Replace array/object items with new ones. */
       +void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem)
       +{
       +        cJSON *c = array->child;
       +        while (c && which > 0)
       +                c = c->next, which--;
       +        if (!c) {
       +                cJSON_AddItemToArray(array, newitem);
       +                return;
       +        }
       +        newitem->next = c;
       +        newitem->prev = c->prev;
       +        c->prev = newitem;
       +        if (c == array->child)
       +                array->child = newitem;
       +        else
       +                newitem->prev->next = newitem;
       +}
       +
       +void cJSON_ReplaceItemInArray(cJSON * array, int which, cJSON * newitem)
       +{
       +        cJSON *c = array->child;
       +        while (c && which > 0)
       +                c = c->next, which--;
       +        if (!c)
       +                return;
       +        newitem->next = c->next;
       +        newitem->prev = c->prev;
       +        if (newitem->next)
       +                newitem->next->prev = newitem;
       +        if (c == array->child)
       +                array->child = newitem;
       +        else
       +                newitem->prev->next = newitem;
       +        c->next = c->prev = 0;
       +        cJSON_Delete(c);
       +}
       +
       +void
       +cJSON_ReplaceItemInObject(cJSON * object, const char *string, cJSON * newitem)
       +{
       +        int i = 0;
       +        cJSON *c = object->child;
       +        while (c && cJSON_strcasecmp(c->string, string))
       +                i++, c = c->next;
       +        if (c) {
       +                newitem->string = cJSON_strdup(string);
       +                cJSON_ReplaceItemInArray(object, i, newitem);
       +        }
       +}
       +
       +/* Create basic types: */
       +cJSON *cJSON_CreateNull(void)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = cJSON_NULL;
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateTrue(void)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = cJSON_True;
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateFalse(void)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = cJSON_False;
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateBool(int b)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = b ? cJSON_True : cJSON_False;
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateNumber(double num)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item) {
       +                item->type = cJSON_Number;
       +                item->valuedouble = num;
       +                item->valueint = (int)num;
       +        }
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateString(const char *string)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item) {
       +                item->type = cJSON_String;
       +                item->valuestring = cJSON_strdup(string);
       +                if (!item->valuestring) {
       +                        cJSON_Delete(item);
       +                        return 0;
       +                }
       +        }
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateArray(void)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = cJSON_Array;
       +        return item;
       +}
       +
       +cJSON *cJSON_CreateObject(void)
       +{
       +        cJSON *item = cJSON_New_Item();
       +        if (item)
       +                item->type = cJSON_Object;
       +        return item;
       +}
       +
       +/* Create Arrays: */
       +cJSON *cJSON_CreateIntArray(const int *numbers, int count)
       +{
       +        int i;
       +        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       +        for (i = 0; a && i < count; i++) {
       +                n = cJSON_CreateNumber(numbers[i]);
       +                if (!n) {
       +                        cJSON_Delete(a);
       +                        return 0;
       +                }
       +                if (!i)
       +                        a->child = n;
       +                else
       +                        suffix_object(p, n);
       +                p = n;
       +        }
       +        return a;
       +}
       +
       +cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
       +{
       +        int i;
       +        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       +        for (i = 0; a && i < count; i++) {
       +                n = cJSON_CreateNumber(numbers[i]);
       +                if (!n) {
       +                        cJSON_Delete(a);
       +                        return 0;
       +                }
       +                if (!i)
       +                        a->child = n;
       +                else
       +                        suffix_object(p, n);
       +                p = n;
       +        }
       +        return a;
       +}
       +
       +cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
       +{
       +        int i;
       +        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       +        for (i = 0; a && i < count; i++) {
       +                n = cJSON_CreateNumber(numbers[i]);
       +                if (!n) {
       +                        cJSON_Delete(a);
       +                        return 0;
       +                }
       +                if (!i)
       +                        a->child = n;
       +                else
       +                        suffix_object(p, n);
       +                p = n;
       +        }
       +        return a;
       +}
       +
       +cJSON *cJSON_CreateStringArray(const char **strings, int count)
       +{
       +        int i;
       +        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       +        for (i = 0; a && i < count; i++) {
       +                n = cJSON_CreateString(strings[i]);
       +                if (!n) {
       +                        cJSON_Delete(a);
       +                        return 0;
       +                }
       +                if (!i)
       +                        a->child = n;
       +                else
       +                        suffix_object(p, n);
       +                p = n;
       +        }
       +        return a;
       +}
       +
       +/* Duplication */
       +cJSON *cJSON_Duplicate(cJSON * item, int recurse)
       +{
       +        cJSON *newitem, *cptr, *nptr = 0, *newchild;
       +        /* Bail on bad ptr */
       +        if (!item)
       +                return 0;
       +        /* Create new item */
       +        newitem = cJSON_New_Item();
       +        if (!newitem)
       +                return 0;
       +        /* Copy over all vars */
       +        newitem->type = item->type & (~cJSON_IsReference), newitem->valueint =
       +            item->valueint, newitem->valuedouble = item->valuedouble;
       +        if (item->valuestring) {
       +                newitem->valuestring = cJSON_strdup(item->valuestring);
       +                if (!newitem->valuestring) {
       +                        cJSON_Delete(newitem);
       +                        return 0;
       +                }
       +        }
       +        if (item->string) {
       +                newitem->string = cJSON_strdup(item->string);
       +                if (!newitem->string) {
       +                        cJSON_Delete(newitem);
       +                        return 0;
       +                }
       +        }
       +        /* If non-recursive, then we're done! */
       +        if (!recurse)
       +                return newitem;
       +        /* Walk the ->next chain for the child. */
       +        cptr = item->child;
       +        while (cptr) {
       +                newchild = cJSON_Duplicate(cptr, 1);        /* Duplicate (with recurse) each item in the ->next chain */
       +                if (!newchild) {
       +                        cJSON_Delete(newitem);
       +                        return 0;
       +                }
       +                if (nptr) {
       +                        nptr->next = newchild, newchild->prev = nptr;
       +                        nptr = newchild;
       +                } /* If newitem->child already set, then crosswire ->prev and ->next and move on */
       +                else {
       +                        newitem->child = newchild;
       +                        nptr = newchild;
       +                }                /* Set newitem->child and move to it */
       +                cptr = cptr->next;
       +        }
       +        return newitem;
       +}
       +
       +void cJSON_Minify(char *json)
       +{
       +        char *into = json;
       +        while (*json) {
       +                if (*json == ' ')
       +                        json++;
       +                else if (*json == '\t')
       +                        json++;        /* Whitespace characters. */
       +                else if (*json == '\r')
       +                        json++;
       +                else if (*json == '\n')
       +                        json++;
       +                else if (*json == '/' && json[1] == '/')
       +                        while (*json && *json != '\n')
       +                                json++;        /* double-slash comments, to end of line. */
       +                else if (*json == '/' && json[1] == '*') {
       +                        while (*json && !(*json == '*' && json[1] == '/'))
       +                                json++;
       +                        json += 2;
       +                } /* multiline comments. */
       +                else if (*json == '\"') {
       +                        *into++ = *json++;
       +                        while (*json && *json != '\"') {
       +                                if (*json == '\\')
       +                                        *into++ = *json++;
       +                                *into++ = *json++;
       +                        }
       +                        *into++ = *json++;
       +                } /* string literals, which are \" sensitive. */
       +                else
       +                        *into++ = *json++;        /* All other characters. */
       +        }
       +        *into = 0;                /* and null-terminate. */
       +}
   DIR diff --git a/cJSON.h b/cJSON.h
       t@@ -0,0 +1,160 @@
       +/*
       +  Copyright (c) 2009 Dave Gamble
       + 
       +  Permission is hereby granted, free of charge, to any person obtaining a copy
       +  of this software and associated documentation files (the "Software"), to deal
       +  in the Software without restriction, including without limitation the rights
       +  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       +  copies of the Software, and to permit persons to whom the Software is
       +  furnished to do so, subject to the following conditions:
       + 
       +  The above copyright notice and this permission notice shall be included in
       +  all copies or substantial portions of the Software.
       + 
       +  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       +  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       +  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       +  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       +  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       +  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       +  THE SOFTWARE.
       +*/
       +
       +#ifndef cJSON__h
       +#define cJSON__h
       +
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +/* cJSON Types: */
       +#define cJSON_False  (1 << 0)
       +#define cJSON_True   (1 << 1)
       +#define cJSON_NULL   (1 << 2)
       +#define cJSON_Number (1 << 3)
       +#define cJSON_String (1 << 4)
       +#define cJSON_Array  (1 << 5)
       +#define cJSON_Object (1 << 6)
       +
       +#define cJSON_IsReference 256
       +#define cJSON_StringIsConst 512
       +
       +/* The cJSON structure: */
       +        typedef struct cJSON {
       +                struct cJSON *next, *prev;        /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
       +                struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
       +
       +                int type;        /* The type of the item, as above. */
       +
       +                char *valuestring;        /* The item's string, if type==cJSON_String */
       +                int valueint;        /* The item's number, if type==cJSON_Number */
       +                double valuedouble;        /* The item's number, if type==cJSON_Number */
       +
       +                char *string;        /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
       +        } cJSON;
       +
       +        typedef struct cJSON_Hooks {
       +                void *(*malloc_fn) (size_t sz);
       +                void (*free_fn) (void *ptr);
       +        } cJSON_Hooks;
       +
       +/* Supply malloc, realloc and free functions to cJSON */
       +        extern void cJSON_InitHooks(cJSON_Hooks * hooks);
       +
       +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
       +        extern cJSON *cJSON_Parse(const char *value);
       +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
       +        extern char *cJSON_Print(cJSON * item);
       +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
       +        extern char *cJSON_PrintUnformatted(cJSON * item);
       +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
       +        extern char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt);
       +/* Delete a cJSON entity and all subentities. */
       +        extern void cJSON_Delete(cJSON * c);
       +
       +/* Returns the number of items in an array (or object). */
       +        extern int cJSON_GetArraySize(cJSON * array);
       +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
       +        extern cJSON *cJSON_GetArrayItem(cJSON * array, int item);
       +/* Get item "string" from object. Case insensitive. */
       +        extern cJSON *cJSON_GetObjectItem(cJSON * object, const char *string);
       +        extern int cJSON_HasObjectItem(cJSON * object, const char *string);
       +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
       +        extern const char *cJSON_GetErrorPtr(void);
       +
       +/* These calls create a cJSON item of the appropriate type. */
       +        extern cJSON *cJSON_CreateNull(void);
       +        extern cJSON *cJSON_CreateTrue(void);
       +        extern cJSON *cJSON_CreateFalse(void);
       +        extern cJSON *cJSON_CreateBool(int b);
       +        extern cJSON *cJSON_CreateNumber(double num);
       +        extern cJSON *cJSON_CreateString(const char *string);
       +        extern cJSON *cJSON_CreateArray(void);
       +        extern cJSON *cJSON_CreateObject(void);
       +
       +/* These utilities create an Array of count items. */
       +        extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
       +        extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
       +        extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
       +        extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
       +
       +/* Append item to the specified array/object. */
       +        extern void cJSON_AddItemToArray(cJSON * array, cJSON * item);
       +        extern void cJSON_AddItemToObject(cJSON * object, const char *string,
       +                                          cJSON * item);
       +        extern void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item);        /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
       +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
       +        extern void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item);
       +        extern void cJSON_AddItemReferenceToObject(cJSON * object,
       +                                                   const char *string,
       +                                                   cJSON * item);
       +
       +/* Remove/Detatch items from Arrays/Objects. */
       +        extern cJSON *cJSON_DetachItemFromArray(cJSON * array, int which);
       +        extern void cJSON_DeleteItemFromArray(cJSON * array, int which);
       +        extern cJSON *cJSON_DetachItemFromObject(cJSON * object,
       +                                                 const char *string);
       +        extern void cJSON_DeleteItemFromObject(cJSON * object,
       +                                               const char *string);
       +
       +/* Update array items. */
       +        extern void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem);        /* Shifts pre-existing items to the right. */
       +        extern void cJSON_ReplaceItemInArray(cJSON * array, int which,
       +                                             cJSON * newitem);
       +        extern void cJSON_ReplaceItemInObject(cJSON * object,
       +                                              const char *string,
       +                                              cJSON * newitem);
       +
       +/* Duplicate a cJSON item */
       +        extern cJSON *cJSON_Duplicate(cJSON * item, int recurse);
       +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
       +need to be released. With recurse!=0, it will duplicate any children connected to the item.
       +The item->next and ->prev pointers are always zero on return from Duplicate. */
       +
       +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
       +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
       +        extern cJSON *cJSON_ParseWithOpts(const char *value,
       +                                          const char **return_parse_end,
       +                                          int require_null_terminated);
       +
       +        extern void cJSON_Minify(char *json);
       +
       +/* Macros for creating things quickly. */
       +#define cJSON_AddNullToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateNull())
       +#define cJSON_AddTrueToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
       +#define cJSON_AddFalseToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
       +#define cJSON_AddBoolToObject(object,name,b)        cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
       +#define cJSON_AddNumberToObject(object,name,n)        cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
       +#define cJSON_AddStringToObject(object,name,s)        cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
       +
       +/* When assigning an integer value, it needs to be propagated to valuedouble too. */
       +#define cJSON_SetIntValue(object,val)                        ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
       +#define cJSON_SetNumberValue(object,val)                ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
       +
       +/* Macro for iterating over an array */
       +#define cJSON_ArrayForEach(pos, head)                        for(pos = (head)->child; pos != NULL; pos = pos->next)
       +
       +#ifdef __cplusplus
       +}
       +#endif
       +#endif
   DIR diff --git a/common.c b/common.c
       t@@ -0,0 +1,80 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +char *ltk_read_file(const char *path)
       +{
       +    FILE *f;
       +    long len;
       +    char *file_contents;
       +    f = fopen(path, "rb");
       +    fseek(f, 0, SEEK_END);
       +    len = ftell(f);
       +    fseek(f, 0, SEEK_SET);
       +    file_contents = malloc(len + 1);
       +    fread(file_contents, 1, len, f);
       +    file_contents[len] = '\0';
       +    fclose(f);
       +
       +    return file_contents;
       +}
       +
       +int ltk_collide_rect(LtkRect rect, int x, int y)
       +{
       +    return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y);
       +}
       +
       +/* Recursively set all active_widget states to NORMAL, redraw them,
       + * and remove the references to them in their parent functions */
       +void ltk_remove_active_widget(void *widget)
       +{
       +    if (!widget) return;
       +    LtkWidget *parent = widget;
       +    LtkWidget *child;
       +    while (parent->active_widget)
       +    {
       +        child = parent->active_widget;
       +        child->state = NORMAL;
       +        child->draw_function(child);
       +        parent->active_widget = NULL;
       +        parent = child;
       +    }
       +}
       +
       +/* Recursively set all hover_widget states to NORMAL, redraw them,
       + * and remove the references to them in their parent functions */
       +void ltk_remove_hover_widget(void *widget)
       +{
       +    if (!widget) return;
       +    LtkWidget *parent = widget;
       +    LtkWidget *child;
       +    while (parent->hover_widget)
       +    {
       +        child = parent->hover_widget;
       +        child->state = child->state == HOVERACTIVE ? ACTIVE : NORMAL;
       +        child->draw_function(child);
       +        parent->hover_widget = NULL;
       +        parent = child;
       +    }
       +}
   DIR diff --git a/common.h b/common.h
       t@@ -0,0 +1,52 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_COMMON_H_
       +#define _LTK_COMMON_H_
       +
       +typedef struct LtkWidget LtkWidget;
       +
       +typedef enum
       +{
       +    NORMAL,
       +    HOVER,
       +    PRESSED,
       +    ACTIVE,
       +    HOVERACTIVE,
       +    DISABLED
       +} LtkWidgetState;
       +
       +typedef struct
       +{
       +    int x;
       +    int y;
       +    int w;
       +    int h;
       +} LtkRect;
       +
       +int ltk_collide_rect(LtkRect rect, int x, int y);
       +char *ltk_read_file(const char *path);
       +void ltk_remove_active_widget(void *widget);
       +void ltk_remove_hover_widget(void *widget);
       +
       +#endif
   DIR diff --git a/event.c b/event.c
       t@@ -0,0 +1,42 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +void ltk_handle_event(XEvent event)
       +{
       +    LtkWindow *window;
       +    HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
       +    if ((event.type == KeyPress || event.type == KeyRelease) && window->key_func)
       +    {
       +        window->key_func(window, event);
       +    }
       +    else if ((event.type == ButtonPress || event.type == ButtonRelease || event.type == MotionNotify) && window->mouse_func)
       +    {
       +        window->mouse_func(window, event);
       +    }
       +    else if (window->other_func)
       +    {
       +        window->other_func(window, event);
       +    }
       +}
   DIR diff --git a/event.h b/event.h
       t@@ -0,0 +1,31 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +#ifndef _LTK_EVENT_H_
       +#define _LTK_EVENT_H_
       +
       +typedef void (*LTK_EVENT_FUNC)(void *widget, XEvent event);
       +
       +#endif
   DIR diff --git a/grid.c b/grid.c
       t@@ -0,0 +1,313 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +/* TODO: remove_widget function that also adjusts static width */
       +
       +#include "ltk.h"
       +
       +void ltk_set_row_weight(LtkGrid *grid, int row, int weight)
       +{
       +    grid->row_weights[row] = weight;
       +    ltk_recalculate_grid(grid);
       +}
       +
       +void ltk_set_column_weight(LtkGrid *grid, int column, int weight)
       +{
       +    grid->column_weights[column] = weight;
       +    ltk_recalculate_grid(grid);
       +}
       +
       +void ltk_draw_grid(LtkGrid *grid)
       +{
       +    int i;
       +    for (i = 0; i < grid->rows * grid->columns; i++)
       +    {
       +        if (!grid->widget_grid[i])
       +        {
       +            continue;
       +        }
       +        LtkWidget *ptr = grid->widget_grid[i];
       +        ptr->draw_function(ptr);
       +    }
       +}
       +
       +LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns)
       +{
       +    LtkGrid *grid = malloc(sizeof(LtkGrid));
       +
       +    grid->widget.window = window;
       +    grid->widget.parent = NULL;
       +    grid->widget.key_func = &ltk_grid_key_event;
       +    grid->widget.mouse_func = &ltk_grid_mouse_event;
       +    grid->widget.update_function = &ltk_recalculate_grid;
       +    grid->widget.draw_function = &ltk_draw_grid;
       +    grid->widget.destroy_function = &ltk_destroy_grid;
       +    grid->widget.rect.x = 0;
       +    grid->widget.rect.y = 0;
       +    grid->widget.rect.w = 0;
       +    grid->widget.rect.h = 0;
       +    grid->widget.active_widget = NULL;
       +    grid->widget.state = NORMAL;
       +
       +    grid->rows = rows;
       +    grid->columns = columns;
       +    grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget));
       +    grid->row_heights = malloc(rows * sizeof(int));
       +    grid->column_widths = malloc(rows * sizeof(int));
       +    grid->row_weights = malloc(rows * sizeof(int));
       +    grid->column_weights = malloc(columns * sizeof(int));
       +    /* Positions have one extra for the end */
       +    grid->row_pos = malloc((rows + 1) * sizeof(int));
       +    grid->column_pos = malloc((columns + 1) * sizeof(int));
       +    int i;
       +    for (i = 0; i < rows; i++)
       +    {
       +        grid->row_heights[i] = 0;
       +        grid->row_weights[i] = 0;
       +        grid->row_pos[i] = 0;
       +    }
       +    grid->row_pos[rows] = 0;
       +    for (i = 0; i < columns; i++)
       +    {
       +        grid->column_widths[i] = 0;
       +        grid->column_weights[i] = 0;
       +        grid->column_pos[i] = 0;
       +    }
       +    grid->column_pos[columns] = 0;
       +    for (i = 0; i < rows * columns; i++)
       +    {
       +        grid->widget_grid[i] = NULL;
       +    }
       +
       +    ltk_recalculate_grid(grid);
       +    return grid;
       +}
       +
       +void ltk_destroy_grid(void *widget)
       +{
       +    LtkGrid *grid = widget;
       +    LtkWidget *ptr;
       +    int i;
       +    for (i = 0; i < grid->rows * grid->columns; i++)
       +    {
       +        if (grid->widget_grid[i])
       +        {
       +            ptr = grid->widget_grid[i];
       +            ptr->destroy_function(ptr);
       +        }
       +    }
       +    free(grid->widget_grid);
       +    free(grid->row_heights);
       +    free(grid->column_widths);
       +    free(grid->row_weights);
       +    free(grid->column_weights);
       +    free(grid->row_pos);
       +    free(grid->column_pos);
       +    free(grid);
       +}
       +
       +void ltk_recalculate_grid(void *widget)
       +{
       +    LtkGrid *grid = widget;
       +    unsigned int height_static = 0, width_static = 0;
       +    unsigned int total_row_weight = 0, total_column_weight = 0;
       +    float height_unit = 0, width_unit = 0;
       +    unsigned int currentx = 0, currenty = 0;
       +    int i, j;
       +    for (i = 0; i < grid->rows; i++)
       +    {
       +        total_row_weight += grid->row_weights[i];
       +        if (grid->row_weights[i] == 0)
       +        {
       +            height_static += grid->row_heights[i];
       +        }
       +    }
       +    for (i = 0; i < grid->columns; i++)
       +    {
       +        total_column_weight += grid->column_weights[i];
       +        if (grid->column_weights[i] == 0)
       +        {
       +            width_static += grid->column_widths[i];
       +        }
       +    }
       +    if (total_row_weight > 0)
       +    {
       +        height_unit = (float)(grid->widget.rect.h - height_static) / (float)total_row_weight;
       +    }
       +    if (total_column_weight > 0)
       +    {
       +        width_unit = (float)(grid->widget.rect.w - width_static) / (float)total_column_weight;
       +    }
       +    for (i = 0; i < grid->rows; i++)
       +    {
       +        grid->row_pos[i] = currenty;
       +        if (grid->row_weights[i] > 0)
       +        {
       +            grid->row_heights[i] = grid->row_weights[i] * height_unit;
       +        }
       +        currenty += grid->row_heights[i];
       +    }
       +    grid->row_pos[grid->rows] = currenty;
       +    for (i = 0; i < grid->columns; i++)
       +    {
       +        grid->column_pos[i] = currentx;
       +        if (grid->column_weights[i] > 0)
       +        {
       +            grid->column_widths[i] = grid->column_weights[i] * width_unit;
       +        }
       +        currentx += grid->column_widths[i];
       +    }
       +    grid->column_pos[grid->columns] = currentx;
       +    int orig_width, orig_height;
       +    int end_column, end_row;
       +    for (i = 0; i < grid->rows; i++)
       +    {
       +        for (j = 0; j < grid->columns; j++)
       +        {
       +            if (!grid->widget_grid[i * grid->columns + j])
       +            {
       +                continue;
       +            }
       +            LtkWidget *ptr = grid->widget_grid[i * grid->columns + j];
       +            orig_width = ptr->rect.w;
       +            orig_height = ptr->rect.h;
       +            end_row = i + ptr->row_span;
       +            end_column = j + ptr->column_span;
       +            if (ptr->sticky[1] == 1 && ptr->sticky[3] == 1)
       +            {
       +                ptr->rect.w = grid->column_pos[end_column] - grid->column_pos[j];
       +            }
       +            if (ptr->sticky[0] == 1 && ptr->sticky[2] == 1)
       +            {
       +                ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i];
       +            }
       +            if (orig_width != ptr->rect.w || orig_height != ptr->rect.h)
       +            {
       +                if (ptr->update_function)
       +                {
       +                    ptr->update_function(ptr);
       +                }
       +            }
       +
       +            if (ptr->sticky[1] == 1)
       +            {
       +                ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w;
       +            }
       +            else if (ptr->sticky[3] == 1)
       +            {
       +                ptr->rect.x = grid->column_pos[j];
       +            }
       +            else
       +            {
       +                ptr->rect.x = grid->column_pos[j] + ((grid->column_pos[end_column] - grid->column_pos[j]) / 2 - ptr->rect.w / 2);
       +            }
       +
       +            if (ptr->sticky[2] == 1)
       +            {
       +                ptr->rect.y = grid->row_pos[end_row] - ptr->rect.h;
       +            }
       +            else if (ptr->sticky[0] == 1)
       +            {
       +                ptr->rect.y = grid->row_pos[i];
       +            }
       +            else
       +            {
       +                ptr->rect.y = grid->row_pos[i] + ((grid->row_pos[end_row] - grid->row_pos[i]) / 2 - ptr->rect.h / 2);
       +            }
       +        }
       +    }
       +}
       +
       +void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int row_span, int column_span, int sticky[4])
       +{
       +    LtkWidget *widget = ptr;
       +    memcpy(widget->sticky, sticky, 4 * sizeof(int));
       +    widget->row = row;
       +    widget->column = column;
       +    widget->row_span = row_span;
       +    widget->column_span = column_span;
       +    if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column]) {
       +        grid->column_widths[column] = widget->rect.w;
       +    }
       +    if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) {
       +        grid->row_heights[row] = widget->rect.h;
       +    }
       +    grid->widget_grid[widget->row * grid->columns + widget->column] = widget;
       +    widget->parent = grid;
       +    ltk_recalculate_grid(grid);
       +}
       +
       +void ltk_grid_key_event(void *widget, XEvent event)
       +{
       +    LtkGrid *grid = widget;
       +    LtkWidget *ptr = grid->widget.active_widget;
       +    if (ptr && ptr->key_func)
       +    {
       +        ptr->key_func(ptr, event);
       +    }
       +}
       +
       +void ltk_grid_mouse_event(void *widget, XEvent event)
       +{
       +    LtkGrid *grid = widget;
       +    LtkWidget *ptr;
       +    int i;
       +    int x, y;
       +    int row, column;
       +    if (event.type == ButtonPress || event.type == ButtonRelease)
       +    {
       +        x = event.xbutton.x;
       +        y = event.xbutton.y;
       +    }
       +    else if (event.type == MotionNotify)
       +    {
       +        x = event.xmotion.x;
       +        y = event.xmotion.y;
       +    }
       +    for (i = 0; i < grid->columns; i++)
       +    {
       +        if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x)
       +        {
       +            column = i;
       +        }
       +    }
       +    for (i = 0; i < grid->rows; i++)
       +    {
       +        if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y)
       +        {
       +            row = i;
       +        }
       +    }
       +    ptr = grid->widget_grid[row * grid->columns + column];
       +    if (ptr && ptr->mouse_func)
       +    {
       +        if (ltk_collide_rect(ptr->rect, x, y))
       +        {
       +        ptr->mouse_func(ptr, event);
       +        }
       +        else if (grid->widget.hover_widget)
       +        {
       +            ltk_remove_hover_widget(grid);
       +        }
       +    }
       +}
   DIR diff --git a/grid.h b/grid.h
       t@@ -0,0 +1,53 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_GRID_H_
       +#define _LTK_GRID_H_
       +
       +#include "ltk.h"
       +
       +typedef struct LtkGrid
       +{
       +    LtkWidget widget;
       +    unsigned int rows;
       +    unsigned int columns;
       +    void **widget_grid;
       +    unsigned int *row_heights;
       +    unsigned int *column_widths;
       +    unsigned int *row_weights;
       +    unsigned int *column_weights;
       +    unsigned int *row_pos;
       +    unsigned int *column_pos;
       +} LtkGrid;
       +
       +void ltk_set_row_weight(LtkGrid *grid, int row, int weight);
       +void ltk_set_column_weight(LtkGrid *grid, int column, int weight);
       +void ltk_draw_grid(LtkGrid *grid);
       +LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns);
       +void ltk_destroy_grid(void *widget);
       +void ltk_recalculate_grid(void *widget);
       +void ltk_grid_key_event(void *widget, XEvent event);
       +void ltk_grid_mouse_event(void *widget, XEvent event);
       +void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int rowspan, int columnspan, int sticky[4]);
       +
       +#endif
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -0,0 +1,79 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +void ltk_init(const char *theme_path)
       +{
       +    ltk_global = malloc(sizeof(Ltk));
       +    Ltk *ltk = ltk_global; /* For convenience */
       +    ltk->display = XOpenDisplay(NULL);
       +    ltk->screen = DefaultScreen(ltk->display);
       +    ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
       +    ltk->theme = ltk_load_theme(theme_path);
       +    ltk->window_hash = NULL;
       +}
       +
       +void ltk_quit(void)
       +{
       +    printf("CLEAN UP!\n");
       +    exit(1);
       +}
       +
       +void ltk_fatal(const char *msg)
       +{
       +    printf(msg);
       +    ltk_quit();
       +};
       +
       +XColor ltk_create_xcolor(const char *hex)
       +{
       +    XColor color;
       +    XParseColor(ltk_global->display, ltk_global->colormap, hex, &color);
       +    XAllocColor(ltk_global->display, ltk_global->colormap, &color);
       +
       +    return color;
       +}
       +
       +void ltk_mainloop(void)
       +{
       +    XEvent event;
       +    KeySym key;
       +    char text[255];
       +
       +    while(1)
       +    {
       +        XNextEvent(ltk_global->display, &event);
       +        ltk_handle_event(event);
       +        /*
       +        if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       +        {
       +            if (text[0] == 'q')
       +            {
       +                XCloseDisplay(ltk_global->display);
       +                exit(0);
       +            }
       +        }
       +        */
       +    }
       +}
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -0,0 +1,57 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_H_
       +#define _LTK_H_
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include "cJSON.h"
       +#include "uthash.h"
       +#include "common.h"
       +#include "widget.h"
       +#include "event.h"
       +#include "window.h"
       +#include "theme.h"
       +#include "grid.h"
       +#include "button.h"
       +
       +typedef struct
       +{
       +    LtkTheme *theme;
       +    Display *display;
       +    int screen;
       +    Colormap colormap;
       +    LtkWindow *window_hash;
       +} Ltk;
       +
       +Ltk *ltk_global;
       +
       +void ltk_init(const char *theme_path);
       +void ltk_fatal(const char *msg);
       +XColor ltk_create_xcolor(const char *hex);
       +void ltk_mainloop(void);
       +
       +#endif
   DIR diff --git a/main.c b/main.c
       t@@ -0,0 +1,52 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <X11/Xos.h>
       +
       +int main(int argc, char *argv[])
       +{
       +    Display *display;
       +    int screen;
       +    Window window;
       +    GC gc;
       +
       +    unsigned long black, white;
       +    XColor green;
       +    Colormap colormap;
       +    display = XOpenDisplay((char *)0);
       +    screen = DefaultScreen(display);
       +    colormap = DefaultColormap(display, screen);
       +    black = BlackPixel(display, screen);
       +    white = WhitePixel(display, screen);
       +    XParseColor(display, colormap, "#00FF00", &green);
       +    XAllocColor(display, colormap, &green);
       +    window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 200, 300, 0, white, green.pixel);
       +    XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
       +    XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
       +    gc = XCreateGC(display, window, 0, 0);
       +    XSetBackground(display, gc, white);
       +    XSetForeground(display, gc, black);
       +    XClearWindow(display, window);
       +    XMapRaised(display, window);
       +
       +    XEvent event;
       +    KeySym key;
       +    char text[255];
       +
       +    while(1)
       +    {
       +        XNextEvent(display, &event);
       +        if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       +        {
       +            if (text[0] == 'q')
       +            {
       +                XFreeGC(display, gc);
       +                XFreeColormap(display, colormap);
       +                XDestroyWindow(display, window);
       +                XCloseDisplay(display);
       +                exit(0);
       +            }
       +        }
       +    }
       +}
   DIR diff --git a/test1.c b/test1.c
       t@@ -0,0 +1,41 @@
       +#include "ltk.h"
       +
       +void bob(void *window, XEvent event)
       +{
       +    KeySym key;
       +    char text[255];
       +    if (XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       +    {
       +        if (text[0] == 'q')
       +        {
       +            ltk_quit();
       +        }
       +    }
       +}
       +
       +void bob1(void *window, XEvent event)
       +{
       +    printf("mouse\n");
       +}
       +
       +int main(int argc, char *argv[])
       +{
       +    ltk_init("themes/default.json");
       +    LtkWindow *window1 = ltk_create_window("Cool Window!", 0, 0, 500, 500);
       +    LtkGrid *grid1 = ltk_create_grid(window1, 2, 2);
       +    window1->root_widget = grid1;
       +    ltk_set_row_weight(grid1, 0, 1);
       +    ltk_set_row_weight(grid1, 1, 1);
       +    ltk_set_column_weight(grid1, 0, 1);
       +    ltk_set_column_weight(grid1, 1, 1);
       +    LtkButton *button1 = ltk_create_button(window1, "I'm a button!", NULL);
       +    int sticky1[4] = {0, 1, 0, 1};
       +    ltk_grid_widget(button1, grid1, 0, 0, 1, 1, sticky1);
       +    LtkButton *button2 = ltk_create_button(window1, "I'm a button!", NULL);
       +    ltk_grid_widget(button2, grid1, 0, 1, 1, 1, sticky1);
       +    LtkButton *button3 = ltk_create_button(window1, "I'm a button!", NULL);
       +    ltk_grid_widget(button3, grid1, 1, 0, 1, 1, sticky1);
       +    LtkButton *button4 = ltk_create_button(window1, "I'm a button!", NULL);
       +    ltk_grid_widget(button4, grid1, 1, 1, 1, 1, sticky1);
       +    ltk_mainloop();
       +}
   DIR diff --git a/theme.c b/theme.c
       t@@ -0,0 +1,64 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +LtkTheme *ltk_load_theme(const char *path)
       +{
       +    char *file_contents = ltk_read_file(path);
       +
       +    cJSON *json = cJSON_Parse(file_contents);
       +    if (!json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +        return NULL;
       +    }
       +    cJSON *button_json = cJSON_GetObjectItem(json, "button");
       +    if (!button_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +        return NULL;
       +    }
       +    cJSON *window_json = cJSON_GetObjectItem(json, "window");
       +    if (!window_json)
       +    {
       +        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +        return NULL;
       +    }
       +
       +    LtkTheme *theme = malloc(sizeof(LtkTheme));
       +    theme->button = ltk_parse_button_theme(button_json);
       +    theme->window = ltk_parse_window_theme(window_json);
       +
       +    free(file_contents);
       +    cJSON_Delete(json);
       +
       +    return theme;
       +}
       +
       +void ltk_destroy_theme(LtkTheme *theme)
       +{
       +    free(theme->button);
       +    free(theme->window);
       +    free(theme);
       +}
   DIR diff --git a/theme.h b/theme.h
       t@@ -0,0 +1,39 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_THEME_H_
       +#define _LTK_THEME_H_
       +
       +typedef struct LtkWindowTheme LtkWindowTheme;
       +typedef struct LtkButtonTheme LtkButtonTheme;
       +
       +typedef struct
       +{
       +    LtkWindowTheme *window;
       +    LtkButtonTheme *button;
       +} LtkTheme;
       +
       +LtkTheme *ltk_load_theme(const char *path);
       +void ltk_destroy_theme(LtkTheme *theme);
       +
       +#endif
   DIR diff --git a/themes/default.json b/themes/default.json
       t@@ -0,0 +1,34 @@
       +{
       +    "window": {
       +        "border-width": 0,
       +        "background": "#000000",
       +        "foreground": "#FFFFFF"
       +    },
       +    "button": {
       +        "normal": {
       +            "border-width": 2,
       +            "font-size": 20,
       +            "border-color": "#339999",
       +            "fill-color": "#113355",
       +            "padding-left": 5,
       +            "padding-right": 5,
       +            "padding-top": 5,
       +            "padding-bottom": 5
       +        },
       +        "hover": {
       +            "fill-color": "#738194"
       +        },
       +        "pressed": {
       +            "border-color": "#FFFFFF",
       +            "fill-color": "#738194"
       +        },
       +        "active": {
       +            "border-color": "#FFFFFF",
       +            "fill-color": "#113355"
       +        },
       +        "disabled": {
       +            "border-color": "#FFFFFF",
       +            "fill-color": "#292929"
       +        }
       +    }
       +}
   DIR diff --git a/uthash.h b/uthash.h
       t@@ -0,0 +1,1074 @@
       +/*
       +Copyright (c) 2003-2016, Troy D. Hanson     http://troydhanson.github.com/uthash/
       +All rights reserved.
       +
       +Redistribution and use in source and binary forms, with or without
       +modification, are permitted provided that the following conditions are met:
       +
       +    * Redistributions of source code must retain the above copyright
       +      notice, this list of conditions and the following disclaimer.
       +
       +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
       +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       +*/
       +
       +#ifndef UTHASH_H
       +#define UTHASH_H
       +
       +#define UTHASH_VERSION 2.0.1
       +
       +#include <string.h>   /* memcmp,strlen */
       +#include <stddef.h>   /* ptrdiff_t */
       +#include <stdlib.h>   /* exit() */
       +
       +/* These macros use decltype or the earlier __typeof GNU extension.
       +   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
       +   when compiling c++ source) this code uses whatever method is needed
       +   or, for VS2008 where neither is available, uses casting workarounds. */
       +#if defined(_MSC_VER)   /* MS compiler */
       +#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
       +#define DECLTYPE(x) (decltype(x))
       +#else                   /* VS2008 or older (or VS2010 in C mode) */
       +#define NO_DECLTYPE
       +#define DECLTYPE(x)
       +#endif
       +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
       +#define NO_DECLTYPE
       +#define DECLTYPE(x)
       +#else                   /* GNU, Sun and other compilers */
       +#define DECLTYPE(x) (__typeof(x))
       +#endif
       +
       +#ifdef NO_DECLTYPE
       +#define DECLTYPE_ASSIGN(dst,src)                                                 \
       +do {                                                                             \
       +  char **_da_dst = (char**)(&(dst));                                             \
       +  *_da_dst = (char*)(src);                                                       \
       +} while (0)
       +#else
       +#define DECLTYPE_ASSIGN(dst,src)                                                 \
       +do {                                                                             \
       +  (dst) = DECLTYPE(dst)(src);                                                    \
       +} while (0)
       +#endif
       +
       +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
       +#if defined(_WIN32)
       +#if defined(_MSC_VER) && _MSC_VER >= 1600
       +#include <stdint.h>
       +#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
       +#include <stdint.h>
       +#else
       +typedef unsigned int uint32_t;
       +typedef unsigned char uint8_t;
       +#endif
       +#elif defined(__GNUC__) && !defined(__VXWORKS__)
       +#include <stdint.h>
       +#else
       +typedef unsigned int uint32_t;
       +typedef unsigned char uint8_t;
       +#endif
       +
       +#ifndef uthash_fatal
       +#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
       +#endif
       +#ifndef uthash_malloc
       +#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
       +#endif
       +#ifndef uthash_free
       +#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
       +#endif
       +#ifndef uthash_strlen
       +#define uthash_strlen(s) strlen(s)
       +#endif
       +#ifndef uthash_memcmp
       +#define uthash_memcmp(a,b,n) memcmp(a,b,n)
       +#endif
       +
       +#ifndef uthash_noexpand_fyi
       +#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
       +#endif
       +#ifndef uthash_expand_fyi
       +#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
       +#endif
       +
       +/* initial number of buckets */
       +#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
       +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
       +#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */
       +
       +/* calculate the element whose hash handle address is hhp */
       +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
       +/* calculate the hash handle from element address elp */
       +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
       +
       +#define HASH_VALUE(keyptr,keylen,hashv)                                          \
       +do {                                                                             \
       +  HASH_FCN(keyptr, keylen, hashv);                                               \
       +} while (0)
       +
       +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
       +do {                                                                             \
       +  (out) = NULL;                                                                  \
       +  if (head) {                                                                    \
       +    unsigned _hf_bkt;                                                            \
       +    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
       +    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \
       +      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
       +    }                                                                            \
       +  }                                                                              \
       +} while (0)
       +
       +#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
       +do {                                                                             \
       +  unsigned _hf_hashv;                                                            \
       +  HASH_VALUE(keyptr, keylen, _hf_hashv);                                         \
       +  HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);               \
       +} while (0)
       +
       +#ifdef HASH_BLOOM
       +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
       +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
       +#define HASH_BLOOM_MAKE(tbl)                                                     \
       +do {                                                                             \
       +  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
       +  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
       +  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
       +  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
       +  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
       +} while (0)
       +
       +#define HASH_BLOOM_FREE(tbl)                                                     \
       +do {                                                                             \
       +  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
       +} while (0)
       +
       +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
       +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
       +
       +#define HASH_BLOOM_ADD(tbl,hashv)                                                \
       +  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
       +
       +#define HASH_BLOOM_TEST(tbl,hashv)                                               \
       +  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))
       +
       +#else
       +#define HASH_BLOOM_MAKE(tbl)
       +#define HASH_BLOOM_FREE(tbl)
       +#define HASH_BLOOM_ADD(tbl,hashv)
       +#define HASH_BLOOM_TEST(tbl,hashv) (1)
       +#define HASH_BLOOM_BYTELEN 0U
       +#endif
       +
       +#define HASH_MAKE_TABLE(hh,head)                                                 \
       +do {                                                                             \
       +  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
       +                  sizeof(UT_hash_table));                                        \
       +  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
       +  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
       +  (head)->hh.tbl->tail = &((head)->hh);                                          \
       +  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
       +  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
       +  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
       +  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
       +          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
       +  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
       +  memset((head)->hh.tbl->buckets, 0,                                             \
       +          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
       +  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
       +  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
       +} while (0)
       +
       +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
       +do {                                                                             \
       +  (replaced) = NULL;                                                             \
       +  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
       +  if (replaced) {                                                                \
       +     HASH_DELETE(hh, head, replaced);                                            \
       +  }                                                                              \
       +  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
       +} while (0)
       +
       +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
       +do {                                                                             \
       +  (replaced) = NULL;                                                             \
       +  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
       +  if (replaced) {                                                                \
       +     HASH_DELETE(hh, head, replaced);                                            \
       +  }                                                                              \
       +  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
       +} while (0)
       +
       +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
       +do {                                                                             \
       +  unsigned _hr_hashv;                                                            \
       +  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
       +  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
       +} while (0)
       +
       +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
       +do {                                                                             \
       +  unsigned _hr_hashv;                                                            \
       +  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
       +  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
       +} while (0)
       +
       +#define HASH_APPEND_LIST(hh, head, add)                                          \
       +do {                                                                             \
       +  (add)->hh.next = NULL;                                                         \
       +  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
       +  (head)->hh.tbl->tail->next = (add);                                            \
       +  (head)->hh.tbl->tail = &((add)->hh);                                           \
       +} while (0)
       +
       +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
       +do {                                                                             \
       +  unsigned _ha_bkt;                                                              \
       +  (add)->hh.hashv = (hashval);                                                   \
       +  (add)->hh.key = (char*) (keyptr);                                              \
       +  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
       +  if (!(head)) {                                                                 \
       +    (add)->hh.next = NULL;                                                       \
       +    (add)->hh.prev = NULL;                                                       \
       +    (head) = (add);                                                              \
       +    HASH_MAKE_TABLE(hh, head);                                                   \
       +  } else {                                                                       \
       +    struct UT_hash_handle *_hs_iter = &(head)->hh;                               \
       +    (add)->hh.tbl = (head)->hh.tbl;                                              \
       +    do {                                                                         \
       +      if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \
       +        break;                                                                   \
       +    } while ((_hs_iter = _hs_iter->next));                                       \
       +    if (_hs_iter) {                                                              \
       +      (add)->hh.next = _hs_iter;                                                 \
       +      if (((add)->hh.prev = _hs_iter->prev)) {                                   \
       +        HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add);              \
       +      } else {                                                                   \
       +        (head) = (add);                                                          \
       +      }                                                                          \
       +      _hs_iter->prev = (add);                                                    \
       +    } else {                                                                     \
       +      HASH_APPEND_LIST(hh, head, add);                                           \
       +    }                                                                            \
       +  }                                                                              \
       +  (head)->hh.tbl->num_items++;                                                   \
       +  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
       +  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
       +  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
       +  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
       +  HASH_FSCK(hh, head);                                                           \
       +} while (0)
       +
       +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
       +do {                                                                             \
       +  unsigned _hs_hashv;                                                            \
       +  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
       +  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
       +} while (0)
       +
       +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
       +  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
       +
       +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
       +  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
       +
       +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
       +do {                                                                             \
       +  unsigned _ha_bkt;                                                              \
       +  (add)->hh.hashv = (hashval);                                                   \
       +  (add)->hh.key = (char*) (keyptr);                                              \
       +  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
       +  if (!(head)) {                                                                 \
       +    (add)->hh.next = NULL;                                                       \
       +    (add)->hh.prev = NULL;                                                       \
       +    (head) = (add);                                                              \
       +    HASH_MAKE_TABLE(hh, head);                                                   \
       +  } else {                                                                       \
       +    (add)->hh.tbl = (head)->hh.tbl;                                              \
       +    HASH_APPEND_LIST(hh, head, add);                                             \
       +  }                                                                              \
       +  (head)->hh.tbl->num_items++;                                                   \
       +  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
       +  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh);                 \
       +  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
       +  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
       +  HASH_FSCK(hh, head);                                                           \
       +} while (0)
       +
       +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
       +do {                                                                             \
       +  unsigned _ha_hashv;                                                            \
       +  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
       +  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
       +} while (0)
       +
       +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
       +  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
       +
       +#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
       +  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
       +
       +#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
       +do {                                                                             \
       +  bkt = ((hashv) & ((num_bkts) - 1U));                                           \
       +} while (0)
       +
       +/* delete "delptr" from the hash table.
       + * "the usual" patch-up process for the app-order doubly-linked-list.
       + * The use of _hd_hh_del below deserves special explanation.
       + * These used to be expressed using (delptr) but that led to a bug
       + * if someone used the same symbol for the head and deletee, like
       + *  HASH_DELETE(hh,users,users);
       + * We want that to work, but by changing the head (users) below
       + * we were forfeiting our ability to further refer to the deletee (users)
       + * in the patch-up process. Solution: use scratch space to
       + * copy the deletee pointer, then the latter references are via that
       + * scratch pointer rather than through the repointed (users) symbol.
       + */
       +#define HASH_DELETE(hh,head,delptr)                                              \
       +do {                                                                             \
       +    struct UT_hash_handle *_hd_hh_del;                                           \
       +    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
       +        uthash_free((head)->hh.tbl->buckets,                                     \
       +                    (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
       +        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
       +        uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
       +        head = NULL;                                                             \
       +    } else {                                                                     \
       +        unsigned _hd_bkt;                                                        \
       +        _hd_hh_del = &((delptr)->hh);                                            \
       +        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
       +            (head)->hh.tbl->tail =                                               \
       +                (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +               \
       +                (head)->hh.tbl->hho);                                            \
       +        }                                                                        \
       +        if ((delptr)->hh.prev != NULL) {                                         \
       +            ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +                  \
       +                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
       +        } else {                                                                 \
       +            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
       +        }                                                                        \
       +        if (_hd_hh_del->next != NULL) {                                          \
       +            ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next +                     \
       +                    (head)->hh.tbl->hho))->prev =                                \
       +                    _hd_hh_del->prev;                                            \
       +        }                                                                        \
       +        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
       +        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
       +        (head)->hh.tbl->num_items--;                                             \
       +    }                                                                            \
       +    HASH_FSCK(hh,head);                                                          \
       +} while (0)
       +
       +
       +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
       +#define HASH_FIND_STR(head,findstr,out)                                          \
       +    HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
       +#define HASH_ADD_STR(head,strfield,add)                                          \
       +    HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add)
       +#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
       +    HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced)
       +#define HASH_FIND_INT(head,findint,out)                                          \
       +    HASH_FIND(hh,head,findint,sizeof(int),out)
       +#define HASH_ADD_INT(head,intfield,add)                                          \
       +    HASH_ADD(hh,head,intfield,sizeof(int),add)
       +#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
       +    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
       +#define HASH_FIND_PTR(head,findptr,out)                                          \
       +    HASH_FIND(hh,head,findptr,sizeof(void *),out)
       +#define HASH_ADD_PTR(head,ptrfield,add)                                          \
       +    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
       +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \
       +    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
       +#define HASH_DEL(head,delptr)                                                    \
       +    HASH_DELETE(hh,head,delptr)
       +
       +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
       + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
       + */
       +#ifdef HASH_DEBUG
       +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
       +#define HASH_FSCK(hh,head)                                                       \
       +do {                                                                             \
       +    struct UT_hash_handle *_thh;                                                 \
       +    if (head) {                                                                  \
       +        unsigned _bkt_i;                                                         \
       +        unsigned _count;                                                         \
       +        char *_prev;                                                             \
       +        _count = 0;                                                              \
       +        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
       +            unsigned _bkt_count = 0;                                             \
       +            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
       +            _prev = NULL;                                                        \
       +            while (_thh) {                                                       \
       +               if (_prev != (char*)(_thh->hh_prev)) {                            \
       +                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
       +                    _thh->hh_prev, _prev );                                      \
       +               }                                                                 \
       +               _bkt_count++;                                                     \
       +               _prev = (char*)(_thh);                                            \
       +               _thh = _thh->hh_next;                                             \
       +            }                                                                    \
       +            _count += _bkt_count;                                                \
       +            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
       +               HASH_OOPS("invalid bucket count %u, actual %u\n",                 \
       +                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
       +            }                                                                    \
       +        }                                                                        \
       +        if (_count != (head)->hh.tbl->num_items) {                               \
       +            HASH_OOPS("invalid hh item count %u, actual %u\n",                   \
       +                (head)->hh.tbl->num_items, _count );                             \
       +        }                                                                        \
       +        /* traverse hh in app order; check next/prev integrity, count */         \
       +        _count = 0;                                                              \
       +        _prev = NULL;                                                            \
       +        _thh =  &(head)->hh;                                                     \
       +        while (_thh) {                                                           \
       +           _count++;                                                             \
       +           if (_prev !=(char*)(_thh->prev)) {                                    \
       +              HASH_OOPS("invalid prev %p, actual %p\n",                          \
       +                    _thh->prev, _prev );                                         \
       +           }                                                                     \
       +           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
       +           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
       +                                  (head)->hh.tbl->hho) : NULL );                 \
       +        }                                                                        \
       +        if (_count != (head)->hh.tbl->num_items) {                               \
       +            HASH_OOPS("invalid app item count %u, actual %u\n",                  \
       +                (head)->hh.tbl->num_items, _count );                             \
       +        }                                                                        \
       +    }                                                                            \
       +} while (0)
       +#else
       +#define HASH_FSCK(hh,head)
       +#endif
       +
       +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
       + * the descriptor to which this macro is defined for tuning the hash function.
       + * The app can #include <unistd.h> to get the prototype for write(2). */
       +#ifdef HASH_EMIT_KEYS
       +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
       +do {                                                                             \
       +    unsigned _klen = fieldlen;                                                   \
       +    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
       +    write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                      \
       +} while (0)
       +#else
       +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
       +#endif
       +
       +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
       +#ifdef HASH_FUNCTION
       +#define HASH_FCN HASH_FUNCTION
       +#else
       +#define HASH_FCN HASH_JEN
       +#endif
       +
       +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
       +#define HASH_BER(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned _hb_keylen=(unsigned)keylen;                                          \
       +  const unsigned char *_hb_key=(const unsigned char*)(key);                      \
       +  (hashv) = 0;                                                                   \
       +  while (_hb_keylen-- != 0U) {                                                   \
       +      (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                         \
       +  }                                                                              \
       +} while (0)
       +
       +
       +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
       + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
       +#define HASH_SAX(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned _sx_i;                                                                \
       +  const unsigned char *_hs_key=(const unsigned char*)(key);                      \
       +  hashv = 0;                                                                     \
       +  for(_sx_i=0; _sx_i < keylen; _sx_i++) {                                        \
       +      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                     \
       +  }                                                                              \
       +} while (0)
       +/* FNV-1a variation */
       +#define HASH_FNV(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned _fn_i;                                                                \
       +  const unsigned char *_hf_key=(const unsigned char*)(key);                      \
       +  hashv = 2166136261U;                                                           \
       +  for(_fn_i=0; _fn_i < keylen; _fn_i++) {                                        \
       +      hashv = hashv ^ _hf_key[_fn_i];                                            \
       +      hashv = hashv * 16777619U;                                                 \
       +  }                                                                              \
       +} while (0)
       +
       +#define HASH_OAT(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned _ho_i;                                                                \
       +  const unsigned char *_ho_key=(const unsigned char*)(key);                      \
       +  hashv = 0;                                                                     \
       +  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
       +      hashv += _ho_key[_ho_i];                                                   \
       +      hashv += (hashv << 10);                                                    \
       +      hashv ^= (hashv >> 6);                                                     \
       +  }                                                                              \
       +  hashv += (hashv << 3);                                                         \
       +  hashv ^= (hashv >> 11);                                                        \
       +  hashv += (hashv << 15);                                                        \
       +} while (0)
       +
       +#define HASH_JEN_MIX(a,b,c)                                                      \
       +do {                                                                             \
       +  a -= b; a -= c; a ^= ( c >> 13 );                                              \
       +  b -= c; b -= a; b ^= ( a << 8 );                                               \
       +  c -= a; c -= b; c ^= ( b >> 13 );                                              \
       +  a -= b; a -= c; a ^= ( c >> 12 );                                              \
       +  b -= c; b -= a; b ^= ( a << 16 );                                              \
       +  c -= a; c -= b; c ^= ( b >> 5 );                                               \
       +  a -= b; a -= c; a ^= ( c >> 3 );                                               \
       +  b -= c; b -= a; b ^= ( a << 10 );                                              \
       +  c -= a; c -= b; c ^= ( b >> 15 );                                              \
       +} while (0)
       +
       +#define HASH_JEN(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned _hj_i,_hj_j,_hj_k;                                                    \
       +  unsigned const char *_hj_key=(unsigned const char*)(key);                      \
       +  hashv = 0xfeedbeefu;                                                           \
       +  _hj_i = _hj_j = 0x9e3779b9u;                                                   \
       +  _hj_k = (unsigned)(keylen);                                                    \
       +  while (_hj_k >= 12U) {                                                         \
       +    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
       +        + ( (unsigned)_hj_key[2] << 16 )                                         \
       +        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
       +    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
       +        + ( (unsigned)_hj_key[6] << 16 )                                         \
       +        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
       +    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
       +        + ( (unsigned)_hj_key[10] << 16 )                                        \
       +        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
       +                                                                                 \
       +     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
       +                                                                                 \
       +     _hj_key += 12;                                                              \
       +     _hj_k -= 12U;                                                               \
       +  }                                                                              \
       +  hashv += (unsigned)(keylen);                                                   \
       +  switch ( _hj_k ) {                                                             \
       +     case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */        \
       +     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */        \
       +     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */        \
       +     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */        \
       +     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */        \
       +     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */        \
       +     case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */        \
       +     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */        \
       +     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */        \
       +     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */        \
       +     case 1:  _hj_i += _hj_key[0];                                               \
       +  }                                                                              \
       +  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
       +} while (0)
       +
       +/* The Paul Hsieh hash function */
       +#undef get16bits
       +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
       +  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
       +#define get16bits(d) (*((const uint16_t *) (d)))
       +#endif
       +
       +#if !defined (get16bits)
       +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
       +                       +(uint32_t)(((const uint8_t *)(d))[0]) )
       +#endif
       +#define HASH_SFH(key,keylen,hashv)                                               \
       +do {                                                                             \
       +  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
       +  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
       +                                                                                 \
       +  unsigned _sfh_rem = _sfh_len & 3U;                                             \
       +  _sfh_len >>= 2;                                                                \
       +  hashv = 0xcafebabeu;                                                           \
       +                                                                                 \
       +  /* Main loop */                                                                \
       +  for (;_sfh_len > 0U; _sfh_len--) {                                             \
       +    hashv    += get16bits (_sfh_key);                                            \
       +    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \
       +    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
       +    _sfh_key += 2U*sizeof (uint16_t);                                            \
       +    hashv    += hashv >> 11;                                                     \
       +  }                                                                              \
       +                                                                                 \
       +  /* Handle end cases */                                                         \
       +  switch (_sfh_rem) {                                                            \
       +    case 3: hashv += get16bits (_sfh_key);                                       \
       +            hashv ^= hashv << 16;                                                \
       +            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \
       +            hashv += hashv >> 11;                                                \
       +            break;                                                               \
       +    case 2: hashv += get16bits (_sfh_key);                                       \
       +            hashv ^= hashv << 11;                                                \
       +            hashv += hashv >> 17;                                                \
       +            break;                                                               \
       +    case 1: hashv += *_sfh_key;                                                  \
       +            hashv ^= hashv << 10;                                                \
       +            hashv += hashv >> 1;                                                 \
       +  }                                                                              \
       +                                                                                 \
       +    /* Force "avalanching" of final 127 bits */                                  \
       +    hashv ^= hashv << 3;                                                         \
       +    hashv += hashv >> 5;                                                         \
       +    hashv ^= hashv << 4;                                                         \
       +    hashv += hashv >> 17;                                                        \
       +    hashv ^= hashv << 25;                                                        \
       +    hashv += hashv >> 6;                                                         \
       +} while (0)
       +
       +#ifdef HASH_USING_NO_STRICT_ALIASING
       +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
       + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
       + * MurmurHash uses the faster approach only on CPU's where we know it's safe.
       + *
       + * Note the preprocessor built-in defines can be emitted using:
       + *
       + *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
       + *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
       + */
       +#if (defined(__i386__) || defined(__x86_64__)  || defined(_M_IX86))
       +#define MUR_GETBLOCK(p,i) p[i]
       +#else /* non intel */
       +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
       +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
       +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
       +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
       +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
       +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
       +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
       +#define MUR_TWO_TWO(p)   ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
       +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >>  8))
       +#else /* assume little endian non-intel */
       +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
       +#define MUR_TWO_TWO(p)   ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
       +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) <<  8))
       +#endif
       +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) :           \
       +                            (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
       +                             (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) :  \
       +                                                      MUR_ONE_THREE(p))))
       +#endif
       +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
       +#define MUR_FMIX(_h) \
       +do {                 \
       +  _h ^= _h >> 16;    \
       +  _h *= 0x85ebca6bu; \
       +  _h ^= _h >> 13;    \
       +  _h *= 0xc2b2ae35u; \
       +  _h ^= _h >> 16;    \
       +} while (0)
       +
       +#define HASH_MUR(key,keylen,hashv)                                     \
       +do {                                                                   \
       +  const uint8_t *_mur_data = (const uint8_t*)(key);                    \
       +  const int _mur_nblocks = (int)(keylen) / 4;                          \
       +  uint32_t _mur_h1 = 0xf88D5353u;                                      \
       +  uint32_t _mur_c1 = 0xcc9e2d51u;                                      \
       +  uint32_t _mur_c2 = 0x1b873593u;                                      \
       +  uint32_t _mur_k1 = 0;                                                \
       +  const uint8_t *_mur_tail;                                            \
       +  const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
       +  int _mur_i;                                                          \
       +  for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) {                   \
       +    _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i);                        \
       +    _mur_k1 *= _mur_c1;                                                \
       +    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
       +    _mur_k1 *= _mur_c2;                                                \
       +                                                                       \
       +    _mur_h1 ^= _mur_k1;                                                \
       +    _mur_h1 = MUR_ROTL32(_mur_h1,13);                                  \
       +    _mur_h1 = (_mur_h1*5U) + 0xe6546b64u;                              \
       +  }                                                                    \
       +  _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4));          \
       +  _mur_k1=0;                                                           \
       +  switch((keylen) & 3U) {                                              \
       +    case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
       +    case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8;  /* FALLTHROUGH */ \
       +    case 1: _mur_k1 ^= (uint32_t)_mur_tail[0];                         \
       +    _mur_k1 *= _mur_c1;                                                \
       +    _mur_k1 = MUR_ROTL32(_mur_k1,15);                                  \
       +    _mur_k1 *= _mur_c2;                                                \
       +    _mur_h1 ^= _mur_k1;                                                \
       +  }                                                                    \
       +  _mur_h1 ^= (uint32_t)(keylen);                                       \
       +  MUR_FMIX(_mur_h1);                                                   \
       +  hashv = _mur_h1;                                                     \
       +} while (0)
       +#endif  /* HASH_USING_NO_STRICT_ALIASING */
       +
       +/* iterate over items in a known bucket to find desired item */
       +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
       +do {                                                                             \
       +  if ((head).hh_head != NULL) {                                                  \
       +    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
       +  } else {                                                                       \
       +    (out) = NULL;                                                                \
       +  }                                                                              \
       +  while ((out) != NULL) {                                                        \
       +    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
       +      if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) {                \
       +        break;                                                                   \
       +      }                                                                          \
       +    }                                                                            \
       +    if ((out)->hh.hh_next != NULL) {                                             \
       +      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
       +    } else {                                                                     \
       +      (out) = NULL;                                                              \
       +    }                                                                            \
       +  }                                                                              \
       +} while (0)
       +
       +/* add an item to a bucket  */
       +#define HASH_ADD_TO_BKT(head,addhh)                                              \
       +do {                                                                             \
       + head.count++;                                                                   \
       + (addhh)->hh_next = head.hh_head;                                                \
       + (addhh)->hh_prev = NULL;                                                        \
       + if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); }                \
       + (head).hh_head=addhh;                                                           \
       + if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH))          \
       +     && ((addhh)->tbl->noexpand != 1U)) {                                        \
       +       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
       + }                                                                               \
       +} while (0)
       +
       +/* remove an item from a given bucket */
       +#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
       +    (head).count--;                                                              \
       +    if ((head).hh_head == hh_del) {                                              \
       +      (head).hh_head = hh_del->hh_next;                                          \
       +    }                                                                            \
       +    if (hh_del->hh_prev) {                                                       \
       +        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
       +    }                                                                            \
       +    if (hh_del->hh_next) {                                                       \
       +        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
       +    }
       +
       +/* Bucket expansion has the effect of doubling the number of buckets
       + * and redistributing the items into the new buckets. Ideally the
       + * items will distribute more or less evenly into the new buckets
       + * (the extent to which this is true is a measure of the quality of
       + * the hash function as it applies to the key domain).
       + *
       + * With the items distributed into more buckets, the chain length
       + * (item count) in each bucket is reduced. Thus by expanding buckets
       + * the hash keeps a bound on the chain length. This bounded chain
       + * length is the essence of how a hash provides constant time lookup.
       + *
       + * The calculation of tbl->ideal_chain_maxlen below deserves some
       + * explanation. First, keep in mind that we're calculating the ideal
       + * maximum chain length based on the *new* (doubled) bucket count.
       + * In fractions this is just n/b (n=number of items,b=new num buckets).
       + * Since the ideal chain length is an integer, we want to calculate
       + * ceil(n/b). We don't depend on floating point arithmetic in this
       + * hash, so to calculate ceil(n/b) with integers we could write
       + *
       + *      ceil(n/b) = (n/b) + ((n%b)?1:0)
       + *
       + * and in fact a previous version of this hash did just that.
       + * But now we have improved things a bit by recognizing that b is
       + * always a power of two. We keep its base 2 log handy (call it lb),
       + * so now we can write this with a bit shift and logical AND:
       + *
       + *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
       + *
       + */
       +#define HASH_EXPAND_BUCKETS(tbl)                                                 \
       +do {                                                                             \
       +    unsigned _he_bkt;                                                            \
       +    unsigned _he_bkt_i;                                                          \
       +    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
       +    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
       +    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
       +             2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));            \
       +    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
       +    memset(_he_new_buckets, 0,                                                   \
       +            2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));             \
       +    tbl->ideal_chain_maxlen =                                                    \
       +       (tbl->num_items >> (tbl->log2_num_buckets+1U)) +                          \
       +       (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);        \
       +    tbl->nonideal_items = 0;                                                     \
       +    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
       +    {                                                                            \
       +        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
       +        while (_he_thh != NULL) {                                                \
       +           _he_hh_nxt = _he_thh->hh_next;                                        \
       +           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt);           \
       +           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
       +           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
       +             tbl->nonideal_items++;                                              \
       +             _he_newbkt->expand_mult = _he_newbkt->count /                       \
       +                                        tbl->ideal_chain_maxlen;                 \
       +           }                                                                     \
       +           _he_thh->hh_prev = NULL;                                              \
       +           _he_thh->hh_next = _he_newbkt->hh_head;                               \
       +           if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev =     \
       +                _he_thh; }                                                       \
       +           _he_newbkt->hh_head = _he_thh;                                        \
       +           _he_thh = _he_hh_nxt;                                                 \
       +        }                                                                        \
       +    }                                                                            \
       +    uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
       +    tbl->num_buckets *= 2U;                                                      \
       +    tbl->log2_num_buckets++;                                                     \
       +    tbl->buckets = _he_new_buckets;                                              \
       +    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
       +        (tbl->ineff_expands+1U) : 0U;                                            \
       +    if (tbl->ineff_expands > 1U) {                                               \
       +        tbl->noexpand=1;                                                         \
       +        uthash_noexpand_fyi(tbl);                                                \
       +    }                                                                            \
       +    uthash_expand_fyi(tbl);                                                      \
       +} while (0)
       +
       +
       +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
       +/* Note that HASH_SORT assumes the hash handle name to be hh.
       + * HASH_SRT was added to allow the hash handle name to be passed in. */
       +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
       +#define HASH_SRT(hh,head,cmpfcn)                                                 \
       +do {                                                                             \
       +  unsigned _hs_i;                                                                \
       +  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
       +  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
       +  if (head != NULL) {                                                            \
       +      _hs_insize = 1;                                                            \
       +      _hs_looping = 1;                                                           \
       +      _hs_list = &((head)->hh);                                                  \
       +      while (_hs_looping != 0U) {                                                \
       +          _hs_p = _hs_list;                                                      \
       +          _hs_list = NULL;                                                       \
       +          _hs_tail = NULL;                                                       \
       +          _hs_nmerges = 0;                                                       \
       +          while (_hs_p != NULL) {                                                \
       +              _hs_nmerges++;                                                     \
       +              _hs_q = _hs_p;                                                     \
       +              _hs_psize = 0;                                                     \
       +              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
       +                  _hs_psize++;                                                   \
       +                  _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?              \
       +                          ((void*)((char*)(_hs_q->next) +                        \
       +                          (head)->hh.tbl->hho)) : NULL);                         \
       +                  if (! (_hs_q) ) { break; }                                     \
       +              }                                                                  \
       +              _hs_qsize = _hs_insize;                                            \
       +              while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
       +                  if (_hs_psize == 0U) {                                         \
       +                      _hs_e = _hs_q;                                             \
       +                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
       +                              ((void*)((char*)(_hs_q->next) +                    \
       +                              (head)->hh.tbl->hho)) : NULL);                     \
       +                      _hs_qsize--;                                               \
       +                  } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) {           \
       +                      _hs_e = _hs_p;                                             \
       +                      if (_hs_p != NULL){                                        \
       +                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
       +                                ((void*)((char*)(_hs_p->next) +                  \
       +                                (head)->hh.tbl->hho)) : NULL);                   \
       +                       }                                                         \
       +                      _hs_psize--;                                               \
       +                  } else if ((                                                   \
       +                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
       +                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
       +                             ) <= 0) {                                           \
       +                      _hs_e = _hs_p;                                             \
       +                      if (_hs_p != NULL){                                        \
       +                        _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
       +                               ((void*)((char*)(_hs_p->next) +                   \
       +                               (head)->hh.tbl->hho)) : NULL);                    \
       +                       }                                                         \
       +                      _hs_psize--;                                               \
       +                  } else {                                                       \
       +                      _hs_e = _hs_q;                                             \
       +                      _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
       +                              ((void*)((char*)(_hs_q->next) +                    \
       +                              (head)->hh.tbl->hho)) : NULL);                     \
       +                      _hs_qsize--;                                               \
       +                  }                                                              \
       +                  if ( _hs_tail != NULL ) {                                      \
       +                      _hs_tail->next = ((_hs_e != NULL) ?                        \
       +                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
       +                  } else {                                                       \
       +                      _hs_list = _hs_e;                                          \
       +                  }                                                              \
       +                  if (_hs_e != NULL) {                                           \
       +                  _hs_e->prev = ((_hs_tail != NULL) ?                            \
       +                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
       +                  }                                                              \
       +                  _hs_tail = _hs_e;                                              \
       +              }                                                                  \
       +              _hs_p = _hs_q;                                                     \
       +          }                                                                      \
       +          if (_hs_tail != NULL){                                                 \
       +            _hs_tail->next = NULL;                                               \
       +          }                                                                      \
       +          if ( _hs_nmerges <= 1U ) {                                             \
       +              _hs_looping=0;                                                     \
       +              (head)->hh.tbl->tail = _hs_tail;                                   \
       +              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
       +          }                                                                      \
       +          _hs_insize *= 2U;                                                      \
       +      }                                                                          \
       +      HASH_FSCK(hh,head);                                                        \
       + }                                                                               \
       +} while (0)
       +
       +/* This function selects items from one hash into another hash.
       + * The end result is that the selected items have dual presence
       + * in both hashes. There is no copy of the items made; rather
       + * they are added into the new hash through a secondary hash
       + * hash handle that must be present in the structure. */
       +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
       +do {                                                                             \
       +  unsigned _src_bkt, _dst_bkt;                                                   \
       +  void *_last_elt=NULL, *_elt;                                                   \
       +  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
       +  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
       +  if (src != NULL) {                                                             \
       +    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
       +      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
       +          _src_hh != NULL;                                                       \
       +          _src_hh = _src_hh->hh_next) {                                          \
       +          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
       +          if (cond(_elt)) {                                                      \
       +            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
       +            _dst_hh->key = _src_hh->key;                                         \
       +            _dst_hh->keylen = _src_hh->keylen;                                   \
       +            _dst_hh->hashv = _src_hh->hashv;                                     \
       +            _dst_hh->prev = _last_elt;                                           \
       +            _dst_hh->next = NULL;                                                \
       +            if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; }             \
       +            if (dst == NULL) {                                                   \
       +              DECLTYPE_ASSIGN(dst,_elt);                                         \
       +              HASH_MAKE_TABLE(hh_dst,dst);                                       \
       +            } else {                                                             \
       +              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
       +            }                                                                    \
       +            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
       +            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
       +            (dst)->hh_dst.tbl->num_items++;                                      \
       +            _last_elt = _elt;                                                    \
       +            _last_elt_hh = _dst_hh;                                              \
       +          }                                                                      \
       +      }                                                                          \
       +    }                                                                            \
       +  }                                                                              \
       +  HASH_FSCK(hh_dst,dst);                                                         \
       +} while (0)
       +
       +#define HASH_CLEAR(hh,head)                                                      \
       +do {                                                                             \
       +  if (head != NULL) {                                                            \
       +    uthash_free((head)->hh.tbl->buckets,                                         \
       +                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
       +    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
       +    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
       +    (head)=NULL;                                                                 \
       +  }                                                                              \
       +} while (0)
       +
       +#define HASH_OVERHEAD(hh,head)                                                   \
       + ((head != NULL) ? (                                                             \
       + (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
       +          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
       +           sizeof(UT_hash_table)                                   +             \
       +           (HASH_BLOOM_BYTELEN))) : 0U)
       +
       +#ifdef NO_DECLTYPE
       +#define HASH_ITER(hh,head,el,tmp)                                                \
       +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
       +  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
       +#else
       +#define HASH_ITER(hh,head,el,tmp)                                                \
       +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
       +  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
       +#endif
       +
       +/* obtain a count of items in the hash */
       +#define HASH_COUNT(head) HASH_CNT(hh,head)
       +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
       +
       +typedef struct UT_hash_bucket {
       +   struct UT_hash_handle *hh_head;
       +   unsigned count;
       +
       +   /* expand_mult is normally set to 0. In this situation, the max chain length
       +    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
       +    * the bucket's chain exceeds this length, bucket expansion is triggered).
       +    * However, setting expand_mult to a non-zero value delays bucket expansion
       +    * (that would be triggered by additions to this particular bucket)
       +    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
       +    * (The multiplier is simply expand_mult+1). The whole idea of this
       +    * multiplier is to reduce bucket expansions, since they are expensive, in
       +    * situations where we know that a particular bucket tends to be overused.
       +    * It is better to let its chain length grow to a longer yet-still-bounded
       +    * value, than to do an O(n) bucket expansion too often.
       +    */
       +   unsigned expand_mult;
       +
       +} UT_hash_bucket;
       +
       +/* random signature used only to find hash tables in external analysis */
       +#define HASH_SIGNATURE 0xa0111fe1u
       +#define HASH_BLOOM_SIGNATURE 0xb12220f2u
       +
       +typedef struct UT_hash_table {
       +   UT_hash_bucket *buckets;
       +   unsigned num_buckets, log2_num_buckets;
       +   unsigned num_items;
       +   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
       +   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
       +
       +   /* in an ideal situation (all buckets used equally), no bucket would have
       +    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
       +   unsigned ideal_chain_maxlen;
       +
       +   /* nonideal_items is the number of items in the hash whose chain position
       +    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
       +    * hash distribution; reaching them in a chain traversal takes >ideal steps */
       +   unsigned nonideal_items;
       +
       +   /* ineffective expands occur when a bucket doubling was performed, but
       +    * afterward, more than half the items in the hash had nonideal chain
       +    * positions. If this happens on two consecutive expansions we inhibit any
       +    * further expansion, as it's not helping; this happens when the hash
       +    * function isn't a good fit for the key domain. When expansion is inhibited
       +    * the hash will still work, albeit no longer in constant time. */
       +   unsigned ineff_expands, noexpand;
       +
       +   uint32_t signature; /* used only to find hash tables in external analysis */
       +#ifdef HASH_BLOOM
       +   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
       +   uint8_t *bloom_bv;
       +   uint8_t bloom_nbits;
       +#endif
       +
       +} UT_hash_table;
       +
       +typedef struct UT_hash_handle {
       +   struct UT_hash_table *tbl;
       +   void *prev;                       /* prev element in app order      */
       +   void *next;                       /* next element in app order      */
       +   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
       +   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
       +   void *key;                        /* ptr to enclosing struct's key  */
       +   unsigned keylen;                  /* enclosing struct's key len     */
       +   unsigned hashv;                   /* result of hash-fcn(key)        */
       +} UT_hash_handle;
       +
       +#endif /* UTHASH_H */
   DIR diff --git a/widget.c b/widget.c
       t@@ -0,0 +1,23 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
   DIR diff --git a/widget.h b/widget.h
       t@@ -0,0 +1,68 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_WIDGET_H_
       +#define _LTK_WIDGET_H_
       +
       +#include "event.h"
       +
       +typedef struct LtkWindow LtkWindow;
       +
       +typedef struct LtkWidget
       +{
       +    /* The window the widget will be displayed on */
       +    LtkWindow *window;
       +    /* For container widgets; the widget that is currently active */
       +    void *active_widget;
       +    /* For container widgets; the widget that is currently highlighted */
       +    void *hover_widget;
       +    /* Parent widget */
       +    void *parent;
       +    /* Function to be called on a KeyPress or KeyRelease event */
       +    LTK_EVENT_FUNC key_func;
       +    /* Function to be called on a ButtonPress, ButtonRelease, or MotionNotify event */
       +    LTK_EVENT_FUNC mouse_func;
       +    /* For container widgets; function to be called when the widget is resized */
       +    void (*update_function)(void *);
       +    /* Function to draw the widget */
       +    void (*draw_function)(void *);
       +    /* State of the widget; NORMAL, PRESSED, ACTIVE, HOVER, or DISABLED */
       +    LtkWidgetState state;
       +    /* Function to destroy the widget; used by containers to destroy child widgets */
       +    void (*destroy_function)(void *);
       +    /* Position and size of the widget */
       +    LtkRect rect;
       +    /* Row of widget if gridded */
       +    unsigned int row;
       +    /* Column of widget if gridded */
       +    unsigned int column;
       +    /* Row span of widget if gridded */
       +    unsigned int row_span;
       +    /* Column span of widget if gridded */
       +    unsigned int column_span;
       +    /* Similar to sticky in tk */
       +    /* -y, +x, +y, -x */
       +    int sticky[4];
       +} LtkWidget;
       +
       +#endif
   DIR diff --git a/window.c b/window.c
       t@@ -0,0 +1,165 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#include "ltk.h"
       +
       +LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json)
       +{
       +    LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme));
       +    if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n");
       +    cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width");
       +    cJSON *fg = cJSON_GetObjectItem(window_json, "foreground");
       +    cJSON *bg = cJSON_GetObjectItem(window_json, "background");
       +    window_theme->border_width = border_width ? border_width->valueint : 0;
       +    window_theme->fg = ltk_create_xcolor(fg->valuestring);
       +    window_theme->bg = ltk_create_xcolor(bg->valuestring);
       +
       +    return window_theme;
       +}
       +
       +void ltk_redraw_window(LtkWindow *window)
       +{
       +    LtkWidget *ptr;
       +    if (!window)
       +    {
       +        return;
       +    }
       +    if (!window->root_widget)
       +    {
       +        return;
       +    }
       +    ptr = window->root_widget;
       +    ptr->draw_function(ptr);
       +}
       +
       +LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h)
       +{
       +    LtkWindow *window = malloc(sizeof(LtkWindow));
       +    if (!window) ltk_fatal("Not enough memory left for window!\n");
       +    LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */
       +    Display *display = ltk_global->display; /* For convenience */
       +    window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel);
       +    window->gc = XCreateGC(display, window->xwindow, 0, 0);
       +    XSetForeground(display, window->gc, wtheme->fg.pixel);
       +    XSetBackground(display, window->gc, wtheme->bg.pixel);
       +    XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL);
       +    window->root_widget = NULL;
       +
       +    window->key_func = &ltk_window_key_event;
       +    window->mouse_func = &ltk_window_mouse_event;
       +    window->other_func = &ltk_window_other_event;
       +
       +    window->rect.w = 0;
       +    window->rect.h = 0;
       +    window->rect.x = 0;
       +    window->rect.y = 0;
       +
       +    XClearWindow(display, window->xwindow);
       +    XMapRaised(display, window->xwindow);
       +    XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask);
       +
       +    HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
       +
       +    return window;
       +}
       +
       +void ltk_destroy_window(LtkWindow *window)
       +{
       +    LtkWidget *ptr = window->root_widget;
       +    if (ptr)
       +    {
       +        ptr->destroy_function(ptr);
       +    }
       +    free(window);
       +}
       +
       +void ltk_window_key_event(void *widget, XEvent event)
       +{
       +    LtkWindow *window = widget;
       +    LtkWidget *ptr = window->root_widget;
       +    if (ptr && ptr->key_func)
       +    {
       +        ptr->key_func(ptr, event);
       +    }
       +}
       +
       +void ltk_window_mouse_event(void *widget, XEvent event)
       +{
       +    LtkWindow *window = widget;
       +    LtkWidget *ptr = window->root_widget;
       +    if (ptr && ptr->mouse_func)
       +    {
       +        ptr->mouse_func(ptr, event);
       +    }
       +}
       +
       +void ltk_window_other_event(void *widget, XEvent event)
       +{
       +    LtkWindow *window = widget;
       +    LtkWidget *ptr = window->root_widget;
       +    if (event.type == ConfigureNotify)
       +    {
       +        unsigned int w, h;
       +        w = event.xconfigure.width;
       +        h = event.xconfigure.height;
       +        if (ptr && ptr->update_function && (window->rect.w != w || window->rect.h != h))
       +        {
       +            window->rect.w = w;
       +            window->rect.h = h;
       +            ptr->rect.w = w;
       +            ptr->rect.h = h;
       +            ptr->update_function(ptr);
       +            ltk_redraw_window(window);
       +        }
       +    }
       +    if (event.type == Expose && event.xexpose.count == 0)
       +    {
       +        ltk_redraw_window(window);
       +    }
       +}
       +
       +/*
       +void ltk_resize_window(Uint32 id, int w, int h)
       +{
       +    LtkWindow *window;
       +    LtkWidget *ptr;
       +    HASH_FIND_INT(ltk_window_hash, &id, window);
       +    if (!window)
       +    {
       +        return;
       +    }
       +    ptr = window->root_widget;
       +    if (!ptr)
       +    {
       +        return;
       +    }
       +    ptr->rect.w = w;
       +    ptr->rect.h = h;
       +    ptr->update_function(ptr);
       +}
       +
       +void ltk_window_set_root_widget(LtkWindow *window, void *root_widget)
       +{
       +    window->root_widget = root_widget;
       +}
       +*/
   DIR diff --git a/window.h b/window.h
       t@@ -0,0 +1,56 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * Permission is hereby granted, free of charge, to any person obtaining a copy
       + * of this software and associated documentation files (the "Software"), to deal
       + * in the Software without restriction, including without limitation the rights
       + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       + * copies of the Software, and to permit persons to whom the Software is
       + * furnished to do so, subject to the following conditions:
       + *
       + * The above copyright notice and this permission notice shall be included in all
       + * copies or substantial portions of the Software.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       + * SOFTWARE.
       + */
       +
       +#ifndef _LTK_WINDOW_H_
       +#define _LTK_WINDOW_H_
       +
       +#include <X11/Xlib.h>
       +#include "event.h"
       +
       +typedef struct LtkWindow
       +{
       +    Window xwindow;
       +    GC gc;
       +    void *root_widget;
       +    LTK_EVENT_FUNC key_func;           /* Called on any keyboard event */
       +    LTK_EVENT_FUNC mouse_func;         /* Called on any mouse event */
       +    LTK_EVENT_FUNC other_func;         /* Called on any other event */
       +    LtkRect rect;
       +    UT_hash_handle hh;
       +} LtkWindow;
       +
       +typedef struct LtkWindowTheme
       +{
       +    int border_width;
       +    XColor fg;
       +    XColor bg;
       +} LtkWindowTheme;
       +
       +LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
       +void ltk_redraw_window(LtkWindow *window);
       +void ltk_destroy_window(LtkWindow *window);
       +void ltk_window_key_event(void *widget, XEvent event);
       +void ltk_window_mouse_event(void *widget, XEvent event);
       +void ltk_window_other_event(void *widget, XEvent event);
       +
       +#endif