diff --git a/Knot.files b/Knot.files index e8cc427c72a635eae244f181edda1138b86d3fe3..6dc06cdc9494139f7dc618bcc3fc093c2eb98748 100644 --- a/Knot.files +++ b/Knot.files @@ -23,6 +23,8 @@ src/contrib/files.c src/contrib/files.h src/contrib/getline.c src/contrib/getline.h +src/contrib/json.c +src/contrib/json.h src/contrib/libbpf/bpf/bpf.c src/contrib/libbpf/bpf/bpf.h src/contrib/libbpf/bpf/bpf_core_read.h diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc index 32fb30750c5c602ea088c141907b768f1f1641ef..eb5a545f4cee5f6de1ec61005cae558df3176123 100644 --- a/src/contrib/Makefile.inc +++ b/src/contrib/Makefile.inc @@ -37,6 +37,8 @@ libcontrib_la_SOURCES = \ contrib/files.h \ contrib/getline.c \ contrib/getline.h \ + contrib/json.c \ + contrib/json.h \ contrib/macros.h \ contrib/mempattern.c \ contrib/mempattern.h \ diff --git a/src/contrib/json.c b/src/contrib/json.c new file mode 100644 index 0000000000000000000000000000000000000000..814c87d044c0f46372663a043a86e10646bc32ad --- /dev/null +++ b/src/contrib/json.c @@ -0,0 +1,215 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "contrib/json.h" + +#define MAX_DEPTH 8 + +enum { + BLOCK_INVALID = 0, + BLOCK_OBJECT, + BLOCK_LIST, +}; + +/*! One indented block of JSON. */ +struct block { + /*! Block type. */ + int type; + /*! Number of elements written. */ + int count; +}; + +struct jsonw { + /*! Output file stream. */ + FILE *out; + /*! Indentaiton string. */ + const char *indent; + /*! List to be used as a stack of blocks in progress. */ + struct block stack[MAX_DEPTH]; + /*! Index pointing to the top of the stack. */ + int top; +}; + +static const char *DEFAULT_INDENT = "\t"; + +static void start_block(jsonw_t *w, int type) +{ + assert(w->top > 0); + + struct block b = { + .type = type, + .count = 0, + }; + + w->top -= 1; + w->stack[w->top] = b; +} + +static struct block *cur_block(jsonw_t *w) +{ + if (w && w->top < MAX_DEPTH) { + return &w->stack[w->top]; + } + return NULL; +} + +/*! Insert new line and indent for the next write. */ +static void wrap(jsonw_t *w) +{ + fputc('\n', w->out); + int level = MAX_DEPTH - w->top; + for (int i = 0; i < level; i++) { + fprintf(w->out, "%s", w->indent); + } +} + +static void end_block(jsonw_t *w) +{ + assert(w->top < MAX_DEPTH); + + w->top += 1; +} + +static void escaped_print(jsonw_t *w, const char *str) +{ + fputc('"', w->out); + for (const char *pos = str; *pos != '\0'; pos++) { + char c = *pos; + if (c == '\\' || c == '\"') { + fputc('\\', w->out); + } + fputc(c, w->out); + } + fputc('"', w->out); +} + +static void align_key(jsonw_t *w, const char *key) +{ + struct block *top = cur_block(w); + if (top && top->count++) { + fputc(',', w->out); + } + + wrap(w); + + if (key && key[0]) { + escaped_print(w, key); + fprintf(w->out, ": "); + } +} + +jsonw_t *jsonw_new(FILE *out, const char *indent) +{ + assert(out); + + jsonw_t *w = calloc(1, sizeof(*w)); + if (w == NULL) { + return w; + } + + w->out = out; + w->indent = indent ? indent : DEFAULT_INDENT; + w->top = MAX_DEPTH; + + return w; +} + +void jsonw_free(jsonw_t **w) +{ + if (w == NULL) { + return; + } + + free(*w); + *w = NULL; +} + +void jsonw_object(jsonw_t *w, const char *key) +{ + assert(w); + + align_key(w, key); + fprintf(w->out, "{"); + start_block(w, BLOCK_OBJECT); +} + +void jsonw_list(jsonw_t *w, const char *key) +{ + assert(w); + + align_key(w, key); + fprintf(w->out, "["); + start_block(w, BLOCK_LIST); +} + +void jsonw_str(jsonw_t *w, const char *key, const char *value) +{ + assert(w); + + align_key(w, key); + escaped_print(w, value); +} + +void jsonw_ulong(jsonw_t *w, const char *key, unsigned long value) +{ + assert(w); + + align_key(w, key); + fprintf(w->out, "%lu", value); +} + +void jsonw_int(jsonw_t *w, const char *key, int value) +{ + assert(w); + + align_key(w, key); + fprintf(w->out, "%d", value); +} + + +void jsonw_bool(jsonw_t *w, const char *key, bool value) +{ + assert(w); + + align_key(w, key); + fprintf(w->out, "%s", value ? "true" : "false"); +} + +void jsonw_end(jsonw_t *w) +{ + assert(w); + + struct block *top = cur_block(w); + if (top == NULL) { + return; + } + + end_block(w); + wrap(w); + + switch (top->type) { + case BLOCK_OBJECT: + fprintf(w->out, "}"); + break; + case BLOCK_LIST: + fprintf(w->out, "]"); + break; + } +} diff --git a/src/contrib/json.h b/src/contrib/json.h new file mode 100644 index 0000000000000000000000000000000000000000..d57b5976f1ab72239252eacfbf1d9a2e7f548195 --- /dev/null +++ b/src/contrib/json.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdio.h> +#include <stdbool.h> + +/*! + * Simple pretty JSON writer. + */ +struct jsonw; +typedef struct jsonw jsonw_t; + +/*! + * Create new JSON writer. + * + * @param out Output file stream. + * @param indent Indentation string. + * + * @return JSON writer or NULL for allocation error. + */ +jsonw_t *jsonw_new(FILE *out, const char *indent); + +/*! + * Free JSON writer created with jsonw_new. + */ +void jsonw_free(jsonw_t **w); + +/*! + * Start writing a new object. + * + * The following writes will represent key and value pairs respectively until + * jsonw_end is called. + */ +void jsonw_object(jsonw_t *w, const char *key); + +/*! + * Start writing a new list. + * + * The following writes will represent values until jsonw_end is called. + */ +void jsonw_list(jsonw_t *w, const char *key); + +/*! + * Write string as JSON. The string will be escaped properly. + */ +void jsonw_str(jsonw_t *w, const char *key, const char *value); + +/*! + * Write unsigned long value as JSON. + */ +void jsonw_ulong(jsonw_t *w, const char *key, unsigned long value); + +/*! + * Write integer as JSON. + */ +void jsonw_int(jsonw_t *w, const char *key, int value); + +/*! + * Write boolean value as JSON. + */ +void jsonw_bool(jsonw_t *w, const char *key, bool value); + +/*! + * Terminate in-progress object or list. + */ +void jsonw_end(jsonw_t *w);