Skip to content

Commit 1b78587

Browse files
committed
lib/json.c: add json printing stuff
1 parent 6baa761 commit 1b78587

8 files changed

Lines changed: 408 additions & 1 deletion

File tree

include/xbps/json.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* SPDX-FileCopyrightText: Copyright 2023 Duncan Overbruck <mail@duncano.de> */
2+
/* SPDX-License-Identifier: BSD-2-Clause */
3+
#ifndef _XBPS_JSON_H_
4+
#define _XBPS_JSON_H_
5+
6+
#include <stdio.h>
7+
#include <stdint.h>
8+
9+
#include <xbps/xbps_array.h>
10+
#include <xbps/xbps_bool.h>
11+
#include <xbps/xbps_dictionary.h>
12+
#include <xbps/xbps_number.h>
13+
#include <xbps/xbps_object.h>
14+
#include <xbps/xbps_string.h>
15+
16+
struct xbps_json_printer {
17+
FILE *file;
18+
unsigned depth;
19+
uint8_t indent;
20+
bool compact;
21+
};
22+
23+
int xbps_json_print_escape(struct xbps_json_printer *p, const char *s);
24+
int xbps_json_print_quote(struct xbps_json_printer *p, const char *s);
25+
int xbps_json_print_bool(struct xbps_json_printer *p, bool b);
26+
27+
int xbps_json_print_xbps_string(struct xbps_json_printer *p, xbps_string_t str);
28+
int xbps_json_print_xbps_number(struct xbps_json_printer *p, xbps_number_t num);
29+
int xbps_json_print_xbps_boolean(struct xbps_json_printer *p, xbps_bool_t b);
30+
int xbps_json_print_xbps_array(struct xbps_json_printer *p, xbps_array_t array);
31+
int xbps_json_print_xbps_dictionary(struct xbps_json_printer *p, xbps_dictionary_t dict);
32+
int xbps_json_print_xbps_object(struct xbps_json_printer *p, xbps_object_t obj);
33+
34+
#endif /* !_XBPS_JSON_H_ */

lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ OBJS += plist_remove.o plist_fetch.o util.o util_path.o util_hash.o
5151
OBJS += repo.o repo_sync.o
5252
OBJS += rpool.o cb_util.o proplib_wrapper.o
5353
OBJS += package_alternatives.o
54-
OBJS += conf.o log.o format.o
54+
OBJS += conf.o log.o format.o json.o
5555
OBJS += $(EXTOBJS) $(COMPAT_OBJS)
5656
# unnecessary unless pkgdb format changes
5757
# OBJS += pkgdb_conversion.o

lib/json.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/* SPDX-FileCopyrightText: Copyright 2023 Duncan Overbruck <mail@duncano.de> */
2+
/* SPDX-License-Identifier: BSD-2-Clause */
3+
4+
#include <sys/types.h>
5+
6+
#include <errno.h>
7+
#include <inttypes.h>
8+
#include <stdarg.h>
9+
#include <stdbool.h>
10+
#include <stdint.h>
11+
#include <stdio.h>
12+
13+
#include "xbps/xbps_array.h"
14+
#include "xbps/xbps_bool.h"
15+
#include "xbps/xbps_dictionary.h"
16+
#include "xbps/xbps_number.h"
17+
#include "xbps/xbps_object.h"
18+
#include "xbps/xbps_string.h"
19+
20+
#include "xbps/json.h"
21+
22+
static int __attribute__ ((format (printf, 2, 3)))
23+
xbps_json_printf(struct xbps_json_printer *p, const char *fmt, ...)
24+
{
25+
va_list ap;
26+
int r = 0;
27+
va_start(ap, fmt);
28+
if (vfprintf(p->file, fmt, ap) < 0)
29+
r = errno ? -errno : -EIO;
30+
va_end(ap);
31+
return r;
32+
}
33+
34+
int
35+
xbps_json_print_escape(struct xbps_json_printer *p, const char *s)
36+
{
37+
int r = 0;
38+
for (; r >= 0 && *s; s++) {
39+
switch (*s) {
40+
case '"': r = xbps_json_printf(p, "\\\""); break;
41+
case '\\': r = xbps_json_printf(p, "\\\\"); break;
42+
case '\b': r = xbps_json_printf(p, "\\b"); break;
43+
case '\f': r = xbps_json_printf(p, "\\f"); break;
44+
case '\n': r = xbps_json_printf(p, "\\n"); break;
45+
case '\r': r = xbps_json_printf(p, "\\r"); break;
46+
case '\t': r = xbps_json_printf(p, "\\t"); break;
47+
default:
48+
if ((unsigned)*s < 0x20) {
49+
r = xbps_json_printf(p, "\\u%04x", *s);
50+
} else {
51+
r = xbps_json_printf(p, "%c", *s);
52+
}
53+
}
54+
}
55+
return r;
56+
}
57+
58+
int
59+
xbps_json_print_quote(struct xbps_json_printer *p, const char *s)
60+
{
61+
int r;
62+
if ((r = xbps_json_printf(p, "\"")) < 0)
63+
return r;
64+
if ((r = xbps_json_print_escape(p, s)) < 0)
65+
return r;
66+
return xbps_json_printf(p, "\"");
67+
}
68+
69+
int
70+
xbps_json_print_bool(struct xbps_json_printer *p, bool b)
71+
{
72+
return xbps_json_printf(p, b ? "true" : "false");
73+
}
74+
75+
int
76+
xbps_json_print_xbps_string(struct xbps_json_printer *p, xbps_string_t str)
77+
{
78+
return xbps_json_print_quote(p, xbps_string_cstring_nocopy(str));
79+
}
80+
81+
int
82+
xbps_json_print_xbps_number(struct xbps_json_printer *p, xbps_number_t num)
83+
{
84+
if (xbps_number_unsigned(num)) {
85+
return xbps_json_printf(p, "%" PRIu64, xbps_number_unsigned_integer_value(num));
86+
} else {
87+
return xbps_json_printf(p, "%" PRId64, xbps_number_integer_value(num));
88+
}
89+
return 0;
90+
}
91+
92+
int
93+
xbps_json_print_xbps_boolean(struct xbps_json_printer *p, xbps_bool_t b)
94+
{
95+
return xbps_json_print_bool(p, xbps_bool_true(b));
96+
}
97+
98+
int
99+
xbps_json_print_xbps_array(struct xbps_json_printer *p, xbps_array_t array)
100+
{
101+
const char *item_sep = p->compact ? "," : ", ";
102+
int indent = 0;
103+
unsigned i = 0;
104+
int r;
105+
p->depth++;
106+
if (!p->compact && p->indent > 0) {
107+
indent = p->indent*p->depth;
108+
item_sep = ",\n";
109+
}
110+
if ((r = xbps_json_printf(p, "[")) < 0)
111+
return r;
112+
for (; i < xbps_array_count(array); i++) {
113+
if (i == 0) {
114+
if (indent > 0 && (r = xbps_json_printf(p, "\n%*s", indent, "")) < 0)
115+
return r;
116+
} else if ((r = xbps_json_printf(p, "%s%*s", item_sep, indent, "")) < 0) {
117+
return r;
118+
}
119+
if ((r = xbps_json_print_xbps_object(p, xbps_array_get(array, i))) < 0)
120+
return r;
121+
}
122+
123+
p->depth--;
124+
if (indent > 0 && i > 0)
125+
return xbps_json_printf(p, "\n%*s]", p->indent*p->depth, "");
126+
return xbps_json_printf(p, "]");
127+
}
128+
129+
int
130+
xbps_json_print_xbps_dictionary(struct xbps_json_printer *p, xbps_dictionary_t dict)
131+
{
132+
xbps_object_iterator_t iter;
133+
xbps_object_t keysym;
134+
const char *item_sep = p->compact ? "," : ", ";
135+
const char *key_sep = p->compact ? ":": ": ";
136+
bool first = true;
137+
int indent = 0;
138+
int r;
139+
140+
iter = xbps_dictionary_iterator(dict);
141+
if (!iter)
142+
return errno ? -errno : -ENOMEM;
143+
144+
p->depth++;
145+
if (!p->compact && p->indent > 0) {
146+
indent = p->depth*p->indent;
147+
item_sep = ",\n";
148+
}
149+
150+
if ((r = xbps_json_printf(p, "{")) < 0)
151+
goto err;
152+
153+
while ((keysym = xbps_object_iterator_next(iter))) {
154+
xbps_object_t obj;
155+
const char *key;
156+
157+
if (first) {
158+
first = false;
159+
if (p->indent > 0 && (r = xbps_json_printf(p, "\n%*s", indent, "")) < 0) {
160+
goto err;
161+
}
162+
} else if ((r = xbps_json_printf(p, "%s%*s", item_sep, indent, "")) < 0) {
163+
goto err;
164+
}
165+
166+
key = xbps_dictionary_keysym_cstring_nocopy(keysym);
167+
if ((r = xbps_json_print_quote(p, key)) < 0)
168+
goto err;
169+
if ((r = xbps_json_printf(p, "%s", key_sep)) < 0)
170+
goto err;
171+
172+
obj = xbps_dictionary_get_keysym(dict, keysym);
173+
if ((r = xbps_json_print_xbps_object(p, obj)) < 0)
174+
goto err;
175+
}
176+
177+
xbps_object_iterator_release(iter);
178+
p->depth--;
179+
if (indent > 0 && !first)
180+
return xbps_json_printf(p, "\n%*s}", p->indent*p->depth, "");
181+
return xbps_json_printf(p, "}");
182+
183+
err:
184+
xbps_object_iterator_release(iter);
185+
return r;
186+
}
187+
188+
int
189+
xbps_json_print_xbps_object(struct xbps_json_printer *p, xbps_object_t obj)
190+
{
191+
if (!obj) return xbps_json_printf(p, "null");
192+
switch (xbps_object_type(obj)) {
193+
case XBPS_TYPE_ARRAY: return xbps_json_print_xbps_array(p, obj);
194+
case XBPS_TYPE_BOOL: return xbps_json_print_xbps_boolean(p, obj);
195+
case XBPS_TYPE_DATA: return xbps_json_printf(p, "true");
196+
case XBPS_TYPE_DICTIONARY: return xbps_json_print_xbps_dictionary(p, obj);
197+
case XBPS_TYPE_DICT_KEYSYM: return -EINVAL;
198+
case XBPS_TYPE_NUMBER: return xbps_json_print_xbps_number(p, obj);
199+
case XBPS_TYPE_STRING: return xbps_json_print_xbps_string(p, obj);
200+
case XBPS_TYPE_UNKNOWN: return -EINVAL;
201+
}
202+
return -EINVAL;
203+
}

tests/xbps/libxbps/Kyuafile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ include('find_pkg_orphans/Kyuafile')
1313
include('pkgdb/Kyuafile')
1414
include('shell/Kyuafile')
1515
include('fmt/Kyuafile')
16+
include('json/Kyuafile')

tests/xbps/libxbps/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ SUBDIRS += pkgdb
1313
SUBDIRS += config
1414
SUBDIRS += shell
1515
SUBDIRS += fmt
16+
SUBDIRS += json
1617

1718
include ../../../mk/subdir.mk

tests/xbps/libxbps/json/Kyuafile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
syntax("kyuafile", 1)
2+
3+
test_suite("libxbps")
4+
5+
atf_test_program{name="json_test"}

tests/xbps/libxbps/json/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
TOPDIR = ../../../..
2+
-include $(TOPDIR)/config.mk
3+
4+
TESTSSUBDIR = xbps/libxbps/json
5+
TEST = json_test
6+
EXTRA_FILES = Kyuafile
7+
8+
include $(TOPDIR)/mk/test.mk

0 commit comments

Comments
 (0)