Initial commit
This commit is contained in:
3
.clang-format
Normal file
3
.clang-format
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
BasedOnStyle: WebKit
|
||||||
|
PointerAlignment: Left
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.o
|
||||||
|
obj/
|
||||||
|
bin/
|
||||||
31
Makefile
Normal file
31
Makefile
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
CC=gcc
|
||||||
|
CFLAGS=-ggdb -O0
|
||||||
|
CFLAGS+=-Wextra -Wall -Wpedantic
|
||||||
|
CFLAGS+=-fsanitize=address -fsanitize=undefined
|
||||||
|
CFLAGS+=-fanalyzer
|
||||||
|
CFLAGS+=-rdynamic
|
||||||
|
CFLAGS+=-Iinclude
|
||||||
|
|
||||||
|
LDFLAGS=
|
||||||
|
LDLIBS=
|
||||||
|
|
||||||
|
_OBJS=main.o parse.o json_obj.o util.o
|
||||||
|
OBJS=$(patsubst %,.obj/%,$(_OBJS))
|
||||||
|
|
||||||
|
all: bin/parse
|
||||||
|
|
||||||
|
bin/parse: $(OBJS) | bin
|
||||||
|
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
.obj/main.o: src/main.c | .obj
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
||||||
|
|
||||||
|
.obj/%.o: src/%.c include/%.h | .obj
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -c $< -o $@
|
||||||
|
|
||||||
|
bin:
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
.obj:
|
||||||
|
mkdir -p $@
|
||||||
4
compile_flags.txt
Normal file
4
compile_flags.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
-Iinclude
|
||||||
|
-Wall
|
||||||
|
-Werror
|
||||||
|
-Wpedantic
|
||||||
22
include/json_obj.h
Normal file
22
include/json_obj.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
#ifndef _obj_H
|
||||||
|
#define _obj_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define OBJ_SIZE 1024
|
||||||
|
|
||||||
|
typedef struct obj_entry {
|
||||||
|
char const* key;
|
||||||
|
struct json_value* val;
|
||||||
|
struct obj_entry* next;
|
||||||
|
} * __p_obj_entry;
|
||||||
|
|
||||||
|
typedef __p_obj_entry obj_t[OBJ_SIZE];
|
||||||
|
|
||||||
|
void* obj_at(obj_t m, char* const key);
|
||||||
|
bool obj_insert(obj_t m, char* const key, struct json_value* value);
|
||||||
|
void obj_delete(obj_t m);
|
||||||
|
|
||||||
|
#endif
|
||||||
32
include/parse.h
Normal file
32
include/parse.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
#ifndef _PARSE_H
|
||||||
|
#define _PARSE_H
|
||||||
|
|
||||||
|
#include "json_obj.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
enum json_type { object,
|
||||||
|
array,
|
||||||
|
string,
|
||||||
|
number,
|
||||||
|
boolean,
|
||||||
|
null };
|
||||||
|
|
||||||
|
struct json_value {
|
||||||
|
enum json_type type;
|
||||||
|
union {
|
||||||
|
obj_t* object;
|
||||||
|
struct json_value** array;
|
||||||
|
char* string;
|
||||||
|
bool boolean;
|
||||||
|
int64_t number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct json_value parse_json_value(FILE* fp);
|
||||||
|
|
||||||
|
void print_json(struct json_value val, int indent);
|
||||||
|
|
||||||
|
#endif
|
||||||
12
include/util.h
Normal file
12
include/util.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#ifndef _UTIL_H
|
||||||
|
#define _UTIL_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void* malloc_or_die(size_t size);
|
||||||
|
void* realloc_or_die(void* ptr, size_t size);
|
||||||
|
void* calloc_or_die(size_t nmemb, size_t size);
|
||||||
|
void print_trace();
|
||||||
|
|
||||||
|
#endif
|
||||||
22
sample.json
Normal file
22
sample.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"glossary": {
|
||||||
|
"title": "example glossary",
|
||||||
|
"GlossDiv": {
|
||||||
|
"title": "S",
|
||||||
|
"GlossList": {
|
||||||
|
"GlossEntry": {
|
||||||
|
"ID": "SGML",
|
||||||
|
"SortAs": "SGML",
|
||||||
|
"GlossTerm": "Standard Generalized Markup Language",
|
||||||
|
"Acronym": "SGML",
|
||||||
|
"Abbrev": "ISO 8879:1986",
|
||||||
|
"GlossDef": {
|
||||||
|
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||||
|
"GlossSeeAlso": ["GML", "XML"]
|
||||||
|
},
|
||||||
|
"GlossSee": "markup"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/json_obj.c
Normal file
106
src/json_obj.c
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "json_obj.h"
|
||||||
|
|
||||||
|
/* djb2 string hash
|
||||||
|
credits: Daniel J. Bernstein */
|
||||||
|
size_t obj_hash(char const* str)
|
||||||
|
{
|
||||||
|
size_t hash = 5381;
|
||||||
|
unsigned int c;
|
||||||
|
|
||||||
|
while ((c = *str++) != '\0')
|
||||||
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||||
|
|
||||||
|
return hash % OBJ_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Value at index `key`
|
||||||
|
|
||||||
|
m - obj to retrieve from
|
||||||
|
key - index to read
|
||||||
|
|
||||||
|
returns value of index if exists, or NULL
|
||||||
|
if key is not in obj
|
||||||
|
*/
|
||||||
|
void* obj_at(obj_t m, char* const key)
|
||||||
|
{
|
||||||
|
struct obj_entry* hit = m[obj_hash(key)];
|
||||||
|
|
||||||
|
/* traverse linked list to end or until key is found */
|
||||||
|
while (hit != NULL && strcmp(hit->key, key))
|
||||||
|
hit = hit->next;
|
||||||
|
|
||||||
|
return hit ? hit->val : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert `value` at index `key`
|
||||||
|
|
||||||
|
m - obj to insert to
|
||||||
|
key - key to insert at
|
||||||
|
val - value to insert
|
||||||
|
val_size - size of value in bytes
|
||||||
|
|
||||||
|
returns true if successful
|
||||||
|
returns false if key already exists */
|
||||||
|
bool obj_insert(obj_t m, char* const key, struct json_value* value)
|
||||||
|
{
|
||||||
|
size_t i = obj_hash(key);
|
||||||
|
struct obj_entry* cur = m[i];
|
||||||
|
|
||||||
|
if (value == NULL)
|
||||||
|
err(EINVAL, "value cannot be NULL");
|
||||||
|
|
||||||
|
if (key == NULL)
|
||||||
|
err(EINVAL, "key cannot be NULL");
|
||||||
|
|
||||||
|
/* traverse linked list to end or until key is found */
|
||||||
|
while (cur != NULL) {
|
||||||
|
if (cur->key == NULL)
|
||||||
|
err(EXIT_FAILURE, "entry without key");
|
||||||
|
|
||||||
|
if (strncmp(cur->key, key, strlen(key)) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fail if key already exists */
|
||||||
|
if (cur != NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* populate new entry */
|
||||||
|
cur = malloc(sizeof(struct obj_entry));
|
||||||
|
cur->key = strdup(key);
|
||||||
|
cur->val = value;
|
||||||
|
cur->next = m[i];
|
||||||
|
|
||||||
|
/* insert newest entry as head */
|
||||||
|
m[i] = cur;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free memory allocated for obj */
|
||||||
|
void obj_delete(obj_t m)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
||||||
|
if (m[i] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
struct obj_entry *e = m[i], *tmp;
|
||||||
|
|
||||||
|
while (e != NULL) {
|
||||||
|
tmp = e;
|
||||||
|
free((char*)e->key);
|
||||||
|
free(e->val);
|
||||||
|
e = e->next;
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main.c
Normal file
20
src/main.c
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
#include "json_obj.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <stdlib.h> // atexit
|
||||||
|
#include <unistd.h> // _exit
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
atexit(print_trace);
|
||||||
|
|
||||||
|
FILE* fp = fopen("sample.json", "r");
|
||||||
|
|
||||||
|
volatile struct json_value x = parse_json_value(fp);
|
||||||
|
|
||||||
|
print_json(x, 1);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
340
src/parse.c
Normal file
340
src/parse.c
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
#include <ctype.h> // isalpha
|
||||||
|
#include <err.h> // err, warn
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
|
||||||
|
#include <string.h> // strdup
|
||||||
|
|
||||||
|
#include "json_obj.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define EARLY_EOF 202
|
||||||
|
#define MALLOC_DIE 201
|
||||||
|
#define UNEXPECTED_CHAR 200
|
||||||
|
|
||||||
|
char* read_string(FILE* fp);
|
||||||
|
obj_t* read_object(FILE* fp);
|
||||||
|
void discard_whitespace(FILE* fp);
|
||||||
|
bool read_boolean(FILE* fp);
|
||||||
|
void read_null(FILE* fp);
|
||||||
|
int64_t read_number(FILE* fp);
|
||||||
|
struct json_value** read_array(FILE* fp);
|
||||||
|
|
||||||
|
void print_object(obj_t obj, int cur_indent, int indent_amount);
|
||||||
|
void print_json_value(struct json_value val, int cur_indent, int indent_amount);
|
||||||
|
void print_array(struct json_value** arr, int cur_indent, int indent_amount);
|
||||||
|
|
||||||
|
char* read_string(FILE* fp)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
size_t i = 0, result_size = 16 * sizeof(char);
|
||||||
|
char* result = malloc_or_die(result_size);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (i + 1 >= result_size) {
|
||||||
|
result_size *= 2;
|
||||||
|
result = realloc_or_die(result, result_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c = fgetc(fp)) {
|
||||||
|
default:
|
||||||
|
result[i++] = c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
result[i++] = '\0';
|
||||||
|
return realloc_or_die(result, i);
|
||||||
|
|
||||||
|
case '\\':
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EOF:
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard_whitespace(FILE* fp)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while (isspace(c = fgetc(fp)))
|
||||||
|
if (c == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
ungetc(c, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_t* read_object(FILE* fp)
|
||||||
|
{
|
||||||
|
obj_t* result = calloc_or_die(1, sizeof(obj_t));
|
||||||
|
char* key;
|
||||||
|
struct json_value* val = calloc_or_die(1, sizeof(struct json_value));
|
||||||
|
int c;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
discard_whitespace(fp);
|
||||||
|
|
||||||
|
if ((c = fgetc(fp)) == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
if (c == '"')
|
||||||
|
key = read_string(fp);
|
||||||
|
else if (c == '}')
|
||||||
|
return result;
|
||||||
|
|
||||||
|
discard_whitespace(fp);
|
||||||
|
|
||||||
|
if ((c = fgetc(fp)) == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
if (c != ':')
|
||||||
|
errx(UNEXPECTED_CHAR, "(%s) expected separator (':') at index %zu",
|
||||||
|
__func__, ftell(fp));
|
||||||
|
|
||||||
|
discard_whitespace(fp);
|
||||||
|
|
||||||
|
*val = parse_json_value(fp);
|
||||||
|
|
||||||
|
bool ok = obj_insert(*result, key, val);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
err(EXIT_FAILURE, "failed to insert pair (%s, %p)", key, (void*)val);
|
||||||
|
|
||||||
|
discard_whitespace(fp);
|
||||||
|
|
||||||
|
if ((c = fgetc(fp)) == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
if (c == ',')
|
||||||
|
continue;
|
||||||
|
else if (c == '}')
|
||||||
|
return result;
|
||||||
|
else
|
||||||
|
errx(UNEXPECTED_CHAR, "(%s) expected ',' or '}' at index %zu", __func__,
|
||||||
|
ftell(fp));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_value** read_array(FILE* fp)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
size_t i = 0, output_size = 16 * sizeof(struct json_value*);
|
||||||
|
struct json_value** output = malloc_or_die(output_size);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
c = fgetc(fp);
|
||||||
|
|
||||||
|
if (c == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
if (c == ']')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (c == ',')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ungetc(c, fp);
|
||||||
|
|
||||||
|
if (i > output_size) {
|
||||||
|
output_size *= 2;
|
||||||
|
output = realloc_or_die(output, output_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
output[i] = malloc_or_die(sizeof(struct json_value));
|
||||||
|
*output[i] = parse_json_value(fp);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
output[i] = NULL;
|
||||||
|
|
||||||
|
return realloc_or_die(output, i * sizeof(void*));
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_null(FILE* fp)
|
||||||
|
{
|
||||||
|
static const char ok[] = { 'n', 'u', 'l', 'l' };
|
||||||
|
char buf[sizeof(ok)];
|
||||||
|
|
||||||
|
size_t n_read = fread(buf, sizeof(char), sizeof(ok), fp);
|
||||||
|
|
||||||
|
if (n_read != sizeof(ok))
|
||||||
|
err(EXIT_FAILURE, "(%s) read failure at index %zu", __func__, ftell(fp));
|
||||||
|
|
||||||
|
if (strncmp(buf, ok, sizeof(ok)) != 0)
|
||||||
|
errx(UNEXPECTED_CHAR, "(%s) unexpected symbol at index %zu", __func__,
|
||||||
|
ftell(fp));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool read_boolean(FILE* fp)
|
||||||
|
{
|
||||||
|
static const char t[] = { 't', 'r', 'u', 'e' };
|
||||||
|
static const char f[] = { 'f', 'a', 'l', 's', 'e' };
|
||||||
|
|
||||||
|
char buf[sizeof(f)] = { 0 };
|
||||||
|
|
||||||
|
size_t n_read = fread(buf, sizeof(char), sizeof(f), fp);
|
||||||
|
|
||||||
|
if (n_read != sizeof(f))
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (strncmp(buf, t, sizeof(t)) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (strncmp(buf, f, sizeof(f)) == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
errx(UNEXPECTED_CHAR, "(%s) unexpected symbol at index %zu", __func__,
|
||||||
|
ftell(fp) - n_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fix int overflow
|
||||||
|
int64_t read_number(FILE* fp)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
int64_t sum = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
c = fgetc(fp);
|
||||||
|
|
||||||
|
if (c == EOF)
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
|
||||||
|
sum *= 10;
|
||||||
|
sum += c - '0';
|
||||||
|
} while (isdigit(c));
|
||||||
|
|
||||||
|
ungetc(c, fp);
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_value parse_json_value(FILE* fp)
|
||||||
|
{
|
||||||
|
discard_whitespace(fp);
|
||||||
|
int c = fgetc(fp);
|
||||||
|
struct json_value result = { 0 };
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case EOF:
|
||||||
|
err(EARLY_EOF, "(%s) unexpected EOF", __func__);
|
||||||
|
case '{':
|
||||||
|
result.type = object;
|
||||||
|
result.object = read_object(fp);
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
result.type = string;
|
||||||
|
result.string = read_string(fp);
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
result.type = array;
|
||||||
|
result.array = read_array(fp);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
case 'f':
|
||||||
|
ungetc(c, fp);
|
||||||
|
result.type = boolean;
|
||||||
|
result.boolean = read_boolean(fp);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
ungetc(c, fp);
|
||||||
|
read_null(fp);
|
||||||
|
result.type = null;
|
||||||
|
result.number = 0L;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (isdigit(c)) {
|
||||||
|
result.type = number;
|
||||||
|
result.number = read_number(fp);
|
||||||
|
} else {
|
||||||
|
errx(UNEXPECTED_CHAR, "(%s) unexpected symbol %c at index %zu", __func__,
|
||||||
|
c, ftell(fp) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_indent(int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
putchar(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_object(obj_t obj, int cur_indent, int indent_amount)
|
||||||
|
{
|
||||||
|
printf("{");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < OBJ_SIZE; i++) {
|
||||||
|
struct obj_entry* e = obj[i];
|
||||||
|
|
||||||
|
if (e == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (e != NULL) {
|
||||||
|
putchar('\n');
|
||||||
|
add_indent(cur_indent);
|
||||||
|
printf("\"%s\": ", e->key);
|
||||||
|
print_json_value(*(e->val), cur_indent + indent_amount, indent_amount);
|
||||||
|
putchar(',');
|
||||||
|
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\b"); // undo last comma
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
add_indent(cur_indent - indent_amount * 2);
|
||||||
|
printf("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_array(struct json_value** arr, int cur_indent, int indent_amount)
|
||||||
|
{
|
||||||
|
putchar('[');
|
||||||
|
|
||||||
|
for (size_t i = 0; arr[i] != NULL; i++) {
|
||||||
|
print_json_value(*arr[i], cur_indent + indent_amount, indent_amount);
|
||||||
|
putchar(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
putchar(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_json_value(struct json_value val, int cur_indent,
|
||||||
|
int indent_amount)
|
||||||
|
{
|
||||||
|
switch (val.type) {
|
||||||
|
case string:
|
||||||
|
printf("\"%s\"", val.string);
|
||||||
|
break;
|
||||||
|
case number:
|
||||||
|
printf("%zu", val.number);
|
||||||
|
break;
|
||||||
|
case boolean:
|
||||||
|
printf("%s", val.boolean ? "true" : "false");
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
printf("null");
|
||||||
|
break;
|
||||||
|
case object:
|
||||||
|
print_object(*val.object, cur_indent + indent_amount, indent_amount);
|
||||||
|
break;
|
||||||
|
case array:
|
||||||
|
print_array(val.array, cur_indent + indent_amount, indent_amount);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("<unknown>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_json(struct json_value val, int indent)
|
||||||
|
{
|
||||||
|
print_json_value(val, 0, indent);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
58
src/util.c
Normal file
58
src/util.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void* malloc_or_die(size_t size)
|
||||||
|
{
|
||||||
|
void* result = malloc(size);
|
||||||
|
|
||||||
|
if (result == NULL)
|
||||||
|
err(errno, "malloc_or_die failed");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* realloc_or_die(void* ptr, size_t size)
|
||||||
|
{
|
||||||
|
ptr = realloc(ptr, size);
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
err(errno, "realloc_or_die failed");
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* calloc_or_die(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void* ptr = calloc(nmemb, size);
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
err(errno, "calloc_or_die failed");
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from the glibc man pages
|
||||||
|
// https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
|
||||||
|
void print_trace()
|
||||||
|
{
|
||||||
|
void* array[500];
|
||||||
|
char** strings;
|
||||||
|
int size, i;
|
||||||
|
|
||||||
|
size = backtrace(array, 500);
|
||||||
|
strings = backtrace_symbols(array, size);
|
||||||
|
|
||||||
|
if (strings != NULL) {
|
||||||
|
printf("Obtained %d stack frames.\n", size);
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
printf("%s\n", strings[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(strings);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user