chiark / gitweb /
Prep v221: Update and clean up build system to sync with upstream
[elogind.git] / src / shared / json.c
diff --git a/src/shared/json.c b/src/shared/json.c
deleted file mode 100644 (file)
index be40a0d..0000000
+++ /dev/null
@@ -1,866 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2014 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-#include <math.h>
-#include "macro.h"
-#include "utf8.h"
-#include "json.h"
-
-int json_variant_new(JsonVariant **ret, JsonVariantType type) {
-        JsonVariant *v;
-
-        v = new0(JsonVariant, 1);
-        if (!v)
-                return -ENOMEM;
-        v->type = type;
-        *ret = v;
-        return 0;
-}
-
-static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
-        int r;
-
-        assert(ret);
-        assert(variant);
-
-        ret->type = variant->type;
-        ret->size = variant->size;
-
-        if (variant->type == JSON_VARIANT_STRING) {
-                ret->string = memdup(variant->string, variant->size+1);
-                if (!ret->string)
-                        return -ENOMEM;
-        } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
-                size_t i;
-
-                ret->objects = new0(JsonVariant, variant->size);
-                if (!ret->objects)
-                        return -ENOMEM;
-
-                for (i = 0; i < variant->size; ++i) {
-                        r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
-                        if (r < 0)
-                                return r;
-                }
-        } else
-                ret->value = variant->value;
-
-        return 0;
-}
-
-static JsonVariant *json_object_unref(JsonVariant *variant);
-
-static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
-        if (!variant)
-                return NULL;
-
-        if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
-                return json_object_unref(variant);
-        else if (variant->type == JSON_VARIANT_STRING)
-                free(variant->string);
-
-        return NULL;
-}
-
-static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
-        if (!variant)
-                return NULL;
-
-        for (size_t i = 0; i < size; ++i)
-                json_variant_unref_inner(&variant[i]);
-
-        free(variant);
-        return NULL;
-}
-
-static JsonVariant *json_object_unref(JsonVariant *variant) {
-        size_t i;
-
-        assert(variant);
-
-        if (!variant->objects)
-                return NULL;
-
-        for (i = 0; i < variant->size; ++i)
-                json_variant_unref_inner(&variant->objects[i]);
-
-        free(variant->objects);
-        return NULL;
-}
-
-static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
-        size_t i = 0;
-        JsonVariant *p = NULL;
-
-        if (!variant)
-                return NULL;
-
-        while((p = (variant[i++])) != NULL) {
-                if (p->type == JSON_VARIANT_STRING)
-                       free(p->string);
-                free(p);
-        }
-
-        free(variant);
-
-        return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
-
-JsonVariant *json_variant_unref(JsonVariant *variant) {
-        if (!variant)
-                return NULL;
-
-        if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
-                json_object_unref(variant);
-        else if (variant->type == JSON_VARIANT_STRING)
-                free(variant->string);
-
-        free(variant);
-
-        return NULL;
-}
-
-char *json_variant_string(JsonVariant *variant){
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_STRING);
-
-        return variant->string;
-}
-
-bool json_variant_bool(JsonVariant *variant) {
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_BOOLEAN);
-
-        return variant->value.boolean;
-}
-
-intmax_t json_variant_integer(JsonVariant *variant) {
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_INTEGER);
-
-        return variant->value.integer;
-}
-
-double json_variant_real(JsonVariant *variant) {
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_REAL);
-
-        return variant->value.real;
-}
-
-JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
-        assert(index < variant->size);
-        assert(variant->objects);
-
-        return &variant->objects[index];
-}
-
-JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
-        size_t i;
-
-        assert(variant);
-        assert(variant->type == JSON_VARIANT_OBJECT);
-        assert(variant->objects);
-
-        for (i = 0; i < variant->size; i += 2) {
-                JsonVariant *p = &variant->objects[i];
-                if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
-                        return &variant->objects[i + 1];
-        }
-
-        return NULL;
-}
-
-static void inc_lines(unsigned *line, const char *s, size_t n) {
-        const char *p = s;
-
-        if (!line)
-                return;
-
-        for (;;) {
-                const char *f;
-
-                f = memchr(p, '\n', n);
-                if (!f)
-                        return;
-
-                n -= (f - p) + 1;
-                p = f + 1;
-                (*line)++;
-        }
-}
-
-static int unhex_ucs2(const char *c, uint16_t *ret) {
-        int aa, bb, cc, dd;
-        uint16_t x;
-
-        assert(c);
-        assert(ret);
-
-        aa = unhexchar(c[0]);
-        if (aa < 0)
-                return -EINVAL;
-
-        bb = unhexchar(c[1]);
-        if (bb < 0)
-                return -EINVAL;
-
-        cc = unhexchar(c[2]);
-        if (cc < 0)
-                return -EINVAL;
-
-        dd = unhexchar(c[3]);
-        if (dd < 0)
-                return -EINVAL;
-
-        x =     ((uint16_t) aa << 12) |
-                ((uint16_t) bb << 8) |
-                ((uint16_t) cc << 4) |
-                ((uint16_t) dd);
-
-        if (x <= 0)
-                return -EINVAL;
-
-        *ret = x;
-
-        return 0;
-}
-
-static int json_parse_string(const char **p, char **ret) {
-        _cleanup_free_ char *s = NULL;
-        size_t n = 0, allocated = 0;
-        const char *c;
-
-        assert(p);
-        assert(*p);
-        assert(ret);
-
-        c = *p;
-
-        if (*c != '"')
-                return -EINVAL;
-
-        c++;
-
-        for (;;) {
-                int len;
-
-                /* Check for EOF */
-                if (*c == 0)
-                        return -EINVAL;
-
-                /* Check for control characters 0x00..0x1f */
-                if (*c > 0 && *c < ' ')
-                        return -EINVAL;
-
-                /* Check for control character 0x7f */
-                if (*c == 0x7f)
-                        return -EINVAL;
-
-                if (*c == '"') {
-                        if (!s) {
-                                s = strdup("");
-                                if (!s)
-                                        return -ENOMEM;
-                        } else
-                                s[n] = 0;
-
-                        *p = c + 1;
-
-                        *ret = s;
-                        s = NULL;
-                        return JSON_STRING;
-                }
-
-                if (*c == '\\') {
-                        char ch = 0;
-                        c++;
-
-                        if (*c == 0)
-                                return -EINVAL;
-
-                        if (IN_SET(*c, '"', '\\', '/'))
-                                ch = *c;
-                        else if (*c == 'b')
-                                ch = '\b';
-                        else if (*c == 'f')
-                                ch = '\f';
-                        else if (*c == 'n')
-                                ch = '\n';
-                        else if (*c == 'r')
-                                ch = '\r';
-                        else if (*c == 't')
-                                ch = '\t';
-                        else if (*c == 'u') {
-                                uint16_t x;
-                                int r;
-
-                                r = unhex_ucs2(c + 1, &x);
-                                if (r < 0)
-                                        return r;
-
-                                c += 5;
-
-                                if (!GREEDY_REALLOC(s, allocated, n + 4))
-                                        return -ENOMEM;
-
-                                if (!utf16_is_surrogate(x))
-                                        n += utf8_encode_unichar(s + n, x);
-                                else if (utf16_is_trailing_surrogate(x))
-                                        return -EINVAL;
-                                else {
-                                        uint16_t y;
-
-                                        if (c[0] != '\\' || c[1] != 'u')
-                                                return -EINVAL;
-
-                                        r = unhex_ucs2(c + 2, &y);
-                                        if (r < 0)
-                                                return r;
-
-                                        c += 6;
-
-                                        if (!utf16_is_trailing_surrogate(y))
-                                                return -EINVAL;
-
-                                        n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
-                                }
-
-                                continue;
-                        } else
-                                return -EINVAL;
-
-                        if (!GREEDY_REALLOC(s, allocated, n + 2))
-                                return -ENOMEM;
-
-                        s[n++] = ch;
-                        c ++;
-                        continue;
-                }
-
-                len = utf8_encoded_valid_unichar(c);
-                if (len < 0)
-                        return len;
-
-                if (!GREEDY_REALLOC(s, allocated, n + len + 1))
-                        return -ENOMEM;
-
-                memcpy(s + n, c, len);
-                n += len;
-                c += len;
-        }
-}
-
-static int json_parse_number(const char **p, union json_value *ret) {
-        bool negative = false, exponent_negative = false, is_double = false;
-        double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
-        intmax_t i = 0;
-        const char *c;
-
-        assert(p);
-        assert(*p);
-        assert(ret);
-
-        c = *p;
-
-        if (*c == '-') {
-                negative = true;
-                c++;
-        }
-
-        if (*c == '0')
-                c++;
-        else {
-                if (!strchr("123456789", *c) || *c == 0)
-                        return -EINVAL;
-
-                do {
-                        if (!is_double) {
-                                int64_t t;
-
-                                t = 10 * i + (*c - '0');
-                                if (t < i) /* overflow */
-                                        is_double = false;
-                                else
-                                        i = t;
-                        }
-
-                        x = 10.0 * x + (*c - '0');
-                        c++;
-                } while (strchr("0123456789", *c) && *c != 0);
-        }
-
-        if (*c == '.') {
-                is_double = true;
-                c++;
-
-                if (!strchr("0123456789", *c) || *c == 0)
-                        return -EINVAL;
-
-                do {
-                        y = 10.0 * y + (*c - '0');
-                        shift = 10.0 * shift;
-                        c++;
-                } while (strchr("0123456789", *c) && *c != 0);
-        }
-
-        if (*c == 'e' || *c == 'E') {
-                is_double = true;
-                c++;
-
-                if (*c == '-') {
-                        exponent_negative = true;
-                        c++;
-                } else if (*c == '+')
-                        c++;
-
-                if (!strchr("0123456789", *c) || *c == 0)
-                        return -EINVAL;
-
-                do {
-                        exponent = 10.0 * exponent + (*c - '0');
-                        c++;
-                } while (strchr("0123456789", *c) && *c != 0);
-        }
-
-        *p = c;
-
-        if (is_double) {
-                ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
-                return JSON_REAL;
-        } else {
-                ret->integer = negative ? -i : i;
-                return JSON_INTEGER;
-        }
-}
-
-int json_tokenize(
-                const char **p,
-                char **ret_string,
-                union json_value *ret_value,
-                void **state,
-                unsigned *line) {
-
-        const char *c;
-        int t;
-        int r;
-
-        enum {
-                STATE_NULL,
-                STATE_VALUE,
-                STATE_VALUE_POST,
-        };
-
-        assert(p);
-        assert(*p);
-        assert(ret_string);
-        assert(ret_value);
-        assert(state);
-
-        t = PTR_TO_INT(*state);
-        c = *p;
-
-        if (t == STATE_NULL) {
-                if (line)
-                        *line = 1;
-                t = STATE_VALUE;
-        }
-
-        for (;;) {
-                const char *b;
-
-                b = c + strspn(c, WHITESPACE);
-                if (*b == 0)
-                        return JSON_END;
-
-                inc_lines(line, c, b - c);
-                c = b;
-
-                switch (t) {
-
-                case STATE_VALUE:
-
-                        if (*c == '{') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE);
-                                return JSON_OBJECT_OPEN;
-
-                        } else if (*c == '}') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_OBJECT_CLOSE;
-
-                        } else if (*c == '[') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE);
-                                return JSON_ARRAY_OPEN;
-
-                        } else if (*c == ']') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_ARRAY_CLOSE;
-
-                        } else if (*c == '"') {
-                                r = json_parse_string(&c, ret_string);
-                                if (r < 0)
-                                        return r;
-
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return r;
-
-                        } else if (strchr("-0123456789", *c)) {
-                                r = json_parse_number(&c, ret_value);
-                                if (r < 0)
-                                        return r;
-
-                                *ret_string = NULL;
-                                *p = c;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return r;
-
-                        } else if (startswith(c, "true")) {
-                                *ret_string = NULL;
-                                ret_value->boolean = true;
-                                *p = c + 4;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_BOOLEAN;
-
-                        } else if (startswith(c, "false")) {
-                                *ret_string = NULL;
-                                ret_value->boolean = false;
-                                *p = c + 5;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_BOOLEAN;
-
-                        } else if (startswith(c, "null")) {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 4;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_NULL;
-
-                        } else
-                                return -EINVAL;
-
-                case STATE_VALUE_POST:
-
-                        if (*c == ':') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE);
-                                return JSON_COLON;
-                        } else if (*c == ',') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE);
-                                return JSON_COMMA;
-                        } else if (*c == '}') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_OBJECT_CLOSE;
-                        } else if (*c == ']') {
-                                *ret_string = NULL;
-                                *ret_value = JSON_VALUE_NULL;
-                                *p = c + 1;
-                                *state = INT_TO_PTR(STATE_VALUE_POST);
-                                return JSON_ARRAY_CLOSE;
-                        } else
-                                return -EINVAL;
-                }
-
-        }
-}
-
-static bool json_is_value(JsonVariant *var) {
-        assert(var);
-
-        return var->type != JSON_VARIANT_CONTROL;
-}
-
-static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
-        bool arr = scope->type == JSON_VARIANT_ARRAY;
-        int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
-        size_t allocated = 0, size = 0;
-        JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
-        enum {
-                STATE_KEY,
-                STATE_COLON,
-                STATE_COMMA,
-                STATE_VALUE
-        } state = arr ? STATE_VALUE : STATE_KEY;
-
-        assert(tokens);
-        assert(i);
-        assert(scope);
-
-        while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
-                bool stopper;
-                int r;
-
-                stopper = !json_is_value(var) && var->value.integer == terminator;
-
-                if (stopper) {
-                        if (state != STATE_COMMA && size > 0)
-                                goto error;
-
-                        goto out;
-                }
-
-                if (state == STATE_KEY) {
-                        if (var->type != JSON_VARIANT_STRING)
-                                goto error;
-                        else {
-                                key = var;
-                                state = STATE_COLON;
-                        }
-                }
-                else if (state == STATE_COLON) {
-                        if (key == NULL)
-                                goto error;
-
-                        if (json_is_value(var))
-                                goto error;
-
-                        if (var->value.integer != JSON_COLON)
-                                goto error;
-
-                        state = STATE_VALUE;
-                }
-                else if (state == STATE_VALUE) {
-                        _cleanup_json_variant_unref_ JsonVariant *v = NULL;
-                        size_t toadd = arr ? 1 : 2;
-
-                        if (!json_is_value(var)) {
-                                int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
-
-                                r = json_variant_new(&v, type);
-                                if (r < 0)
-                                        goto error;
-
-                                r = json_scoped_parse(tokens, i, n, v);
-                                if (r < 0)
-                                        goto error;
-
-                                value = v;
-                        }
-                        else
-                                value = var;
-
-                        if(!GREEDY_REALLOC(items, allocated, size + toadd))
-                                goto error;
-
-                        if (arr) {
-                                r = json_variant_deep_copy(&items[size], value);
-                                if (r < 0)
-                                        goto error;
-                        } else {
-                                r = json_variant_deep_copy(&items[size], key);
-                                if (r < 0)
-                                        goto error;
-
-                                r = json_variant_deep_copy(&items[size+1], value);
-                                if (r < 0)
-                                        goto error;
-                        }
-
-                        size += toadd;
-                        state = STATE_COMMA;
-                }
-                else if (state == STATE_COMMA) {
-                        if (json_is_value(var))
-                                goto error;
-
-                        if (var->value.integer != JSON_COMMA)
-                                goto error;
-
-                        key = NULL;
-                        value = NULL;
-
-                        state = arr ? STATE_VALUE : STATE_KEY;
-                }
-        }
-
-error:
-        json_raw_unref(items, size);
-        return -EBADMSG;
-
-out:
-        scope->size = size;
-        scope->objects = items;
-
-        return scope->type;
-}
-
-static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
-        size_t it = 0;
-        int r;
-        JsonVariant *e;
-        _cleanup_json_variant_unref_ JsonVariant *p = NULL;
-
-        assert(tokens);
-        assert(ntokens);
-
-        e = tokens[it++];
-        r = json_variant_new(&p, JSON_VARIANT_OBJECT);
-        if (r < 0)
-                return r;
-
-        if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
-                return -EBADMSG;
-
-        r = json_scoped_parse(tokens, &it, ntokens, p);
-        if (r < 0)
-                return r;
-
-        *rv = p;
-        p = NULL;
-
-        return 0;
-}
-
-static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
-        _cleanup_free_ char *buf = NULL;
-        _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
-        union json_value v = {};
-        void *json_state = NULL;
-        const char *p;
-        int t, r;
-        size_t allocated = 0, s = 0;
-
-        assert(string);
-        assert(n);
-
-        if (size <= 0)
-                return -EBADMSG;
-
-        buf = strndup(string, size);
-        if (!buf)
-                return -ENOMEM;
-
-        p = buf;
-        for (;;) {
-                _cleanup_json_variant_unref_ JsonVariant *var = NULL;
-                _cleanup_free_ char *rstr = NULL;
-
-                t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
-
-                if (t < 0)
-                        return t;
-                else if (t == JSON_END)
-                        break;
-
-                if (t <= JSON_ARRAY_CLOSE) {
-                        r = json_variant_new(&var, JSON_VARIANT_CONTROL);
-                        if (r < 0)
-                                return r;
-                        var->value.integer = t;
-                } else {
-                        switch (t) {
-                        case JSON_STRING:
-                                r = json_variant_new(&var, JSON_VARIANT_STRING);
-                                if (r < 0)
-                                        return r;
-                                var->size = strlen(rstr);
-                                var->string = strdup(rstr);
-                                if (!var->string) {
-                                        return -ENOMEM;
-                                }
-                                break;
-                        case JSON_INTEGER:
-                                r = json_variant_new(&var, JSON_VARIANT_INTEGER);
-                                if (r < 0)
-                                        return r;
-                                var->value = v;
-                                break;
-                        case JSON_REAL:
-                                r = json_variant_new(&var, JSON_VARIANT_REAL);
-                                if (r < 0)
-                                        return r;
-                                var->value = v;
-                                break;
-                        case JSON_BOOLEAN:
-                                r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
-                                if (r < 0)
-                                        return r;
-                                var->value = v;
-                                break;
-                        case JSON_NULL:
-                                r = json_variant_new(&var, JSON_VARIANT_NULL);
-                                if (r < 0)
-                                        return r;
-                                break;
-                        }
-                }
-
-                if (!GREEDY_REALLOC(items, allocated, s+2))
-                        return -ENOMEM;
-
-                items[s++] = var;
-                items[s] = NULL;
-                var = NULL;
-        }
-
-        *n = s;
-        *tokens = items;
-        items = NULL;
-
-        return 0;
-}
-
-int json_parse(const char *string, JsonVariant **rv) {
-        _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
-        JsonVariant *v = NULL;
-        size_t n = 0;
-        int r;
-
-        assert(string);
-        assert(rv);
-
-        r = json_tokens(string, strlen(string), &s, &n);
-        if (r < 0)
-                return r;
-
-        r = json_parse_tokens(s, n, &v);
-        if (r < 0)
-                return r;
-
-        *rv = v;
-        return 0;
-}