1260710Savg/*
2260710Savg * This file and its contents are supplied under the terms of the
3260710Savg * Common Development and Distribution License ("CDDL"), version 1.0.
4260710Savg * You may only use this file in accordance with the terms of version
5260710Savg * 1.0 of the CDDL.
6260710Savg *
7260710Savg * A full copy of the text of the CDDL should have accompanied this
8260710Savg * source.  A copy of the CDDL is also available via the Internet at
9260710Savg * http://www.illumos.org/license/CDDL.
10260710Savg */
11260710Savg/*
12275552Sdelphij * Copyright (c) 2014, Joyent, Inc.
13260710Savg */
14260710Savg
15260710Savg#include <stdio.h>
16260710Savg#include <stdlib.h>
17260710Savg#include <strings.h>
18260710Savg#include <wchar.h>
19260710Savg#include <sys/debug.h>
20260710Savg
21260710Savg#include "libnvpair.h"
22260710Savg
23275552Sdelphij#define	FPRINTF(fp, ...)				\
24275552Sdelphij	do {						\
25275552Sdelphij		if (fprintf(fp, __VA_ARGS__) < 0)	\
26275552Sdelphij			return (-1);			\
27275552Sdelphij	} while (0)
28260710Savg
29260710Savg/*
30260710Savg * When formatting a string for JSON output we must escape certain characters,
31260710Savg * as described in RFC4627.  This applies to both member names and
32260710Savg * DATA_TYPE_STRING values.
33260710Savg *
34260710Savg * This function will only operate correctly if the following conditions are
35260710Savg * met:
36260710Savg *
37260710Savg *       1. The input String is encoded in the current locale.
38260710Savg *
39260710Savg *       2. The current locale includes the Basic Multilingual Plane (plane 0)
40260710Savg *          as defined in the Unicode standard.
41260710Savg *
42260710Savg * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all
43260710Savg * representable Unicode characters included in their escaped numeric form.
44260710Savg */
45260710Savgstatic int
46260710Savgnvlist_print_json_string(FILE *fp, const char *input)
47260710Savg{
48260710Savg	mbstate_t mbr;
49260710Savg	wchar_t c;
50260710Savg	size_t sz;
51260710Savg
52260710Savg	bzero(&mbr, sizeof (mbr));
53260710Savg
54260710Savg	FPRINTF(fp, "\"");
55260710Savg	while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
56260710Savg		switch (c) {
57260710Savg		case '"':
58260710Savg			FPRINTF(fp, "\\\"");
59260710Savg			break;
60260710Savg		case '\n':
61260710Savg			FPRINTF(fp, "\\n");
62260710Savg			break;
63260710Savg		case '\r':
64260710Savg			FPRINTF(fp, "\\r");
65260710Savg			break;
66260710Savg		case '\\':
67260710Savg			FPRINTF(fp, "\\\\");
68260710Savg			break;
69260710Savg		case '\f':
70260710Savg			FPRINTF(fp, "\\f");
71260710Savg			break;
72260710Savg		case '\t':
73260710Savg			FPRINTF(fp, "\\t");
74260710Savg			break;
75260710Savg		case '\b':
76260710Savg			FPRINTF(fp, "\\b");
77260710Savg			break;
78260710Savg		default:
79260710Savg			if ((c >= 0x00 && c <= 0x1f) ||
80260710Savg			    (c > 0x7f && c <= 0xffff)) {
81260710Savg				/*
82260710Savg				 * Render both Control Characters and Unicode
83260710Savg				 * characters in the Basic Multilingual Plane
84260710Savg				 * as JSON-escaped multibyte characters.
85260710Savg				 */
86260710Savg				FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
87260710Savg			} else if (c >= 0x20 && c <= 0x7f) {
88260710Savg				/*
89260710Savg				 * Render other 7-bit ASCII characters directly
90260710Savg				 * and drop other, unrepresentable characters.
91260710Savg				 */
92260710Savg				FPRINTF(fp, "%c", (int)(0xff & c));
93260710Savg			}
94260710Savg			break;
95260710Savg		}
96260710Savg		input += sz;
97260710Savg	}
98260710Savg
99260710Savg	if (sz == (size_t)-1 || sz == (size_t)-2) {
100260710Savg		/*
101260710Savg		 * We last read an invalid multibyte character sequence,
102260710Savg		 * so return an error.
103260710Savg		 */
104260710Savg		return (-1);
105260710Savg	}
106260710Savg
107260710Savg	FPRINTF(fp, "\"");
108260710Savg	return (0);
109260710Savg}
110260710Savg
111260710Savg/*
112260710Savg * Dump a JSON-formatted representation of an nvlist to the provided FILE *.
113260710Savg * This routine does not output any new-lines or additional whitespace other
114260710Savg * than that contained in strings, nor does it call fflush(3C).
115260710Savg */
116260710Savgint
117260710Savgnvlist_print_json(FILE *fp, nvlist_t *nvl)
118260710Savg{
119260710Savg	nvpair_t *curr;
120260710Savg	boolean_t first = B_TRUE;
121260710Savg
122260710Savg	FPRINTF(fp, "{");
123260710Savg
124260710Savg	for (curr = nvlist_next_nvpair(nvl, NULL); curr;
125260710Savg	    curr = nvlist_next_nvpair(nvl, curr)) {
126260710Savg		data_type_t type = nvpair_type(curr);
127260710Savg
128260710Savg		if (!first)
129260710Savg			FPRINTF(fp, ",");
130260710Savg		else
131260710Savg			first = B_FALSE;
132260710Savg
133260710Savg		if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
134260710Savg			return (-1);
135260710Savg		FPRINTF(fp, ":");
136260710Savg
137260710Savg		switch (type) {
138260710Savg		case DATA_TYPE_STRING: {
139260710Savg			char *string = fnvpair_value_string(curr);
140260710Savg			if (nvlist_print_json_string(fp, string) == -1)
141260710Savg				return (-1);
142260710Savg			break;
143260710Savg		}
144260710Savg
145260710Savg		case DATA_TYPE_BOOLEAN: {
146260710Savg			FPRINTF(fp, "true");
147260710Savg			break;
148260710Savg		}
149260710Savg
150260710Savg		case DATA_TYPE_BOOLEAN_VALUE: {
151260710Savg			FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
152260710Savg			    B_TRUE ? "true" : "false");
153260710Savg			break;
154260710Savg		}
155260710Savg
156260710Savg		case DATA_TYPE_BYTE: {
157260710Savg			FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
158260710Savg			break;
159260710Savg		}
160260710Savg
161260710Savg		case DATA_TYPE_INT8: {
162260710Savg			FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
163260710Savg			break;
164260710Savg		}
165260710Savg
166260710Savg		case DATA_TYPE_UINT8: {
167260710Savg			FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr));
168260710Savg			break;
169260710Savg		}
170260710Savg
171260710Savg		case DATA_TYPE_INT16: {
172260710Savg			FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
173260710Savg			break;
174260710Savg		}
175260710Savg
176260710Savg		case DATA_TYPE_UINT16: {
177260710Savg			FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
178260710Savg			break;
179260710Savg		}
180260710Savg
181260710Savg		case DATA_TYPE_INT32: {
182260710Savg			FPRINTF(fp, "%d", fnvpair_value_int32(curr));
183260710Savg			break;
184260710Savg		}
185260710Savg
186260710Savg		case DATA_TYPE_UINT32: {
187260710Savg			FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
188260710Savg			break;
189260710Savg		}
190260710Savg
191260710Savg		case DATA_TYPE_INT64: {
192260710Savg			FPRINTF(fp, "%lld",
193260710Savg			    (long long)fnvpair_value_int64(curr));
194260710Savg			break;
195260710Savg		}
196260710Savg
197260710Savg		case DATA_TYPE_UINT64: {
198260710Savg			FPRINTF(fp, "%llu",
199260710Savg			    (unsigned long long)fnvpair_value_uint64(curr));
200260710Savg			break;
201260710Savg		}
202260710Savg
203260710Savg		case DATA_TYPE_HRTIME: {
204260710Savg			hrtime_t val;
205260710Savg			VERIFY0(nvpair_value_hrtime(curr, &val));
206260710Savg			FPRINTF(fp, "%llu", (unsigned long long)val);
207260710Savg			break;
208260710Savg		}
209260710Savg
210260710Savg		case DATA_TYPE_DOUBLE: {
211260710Savg			double val;
212260710Savg			VERIFY0(nvpair_value_double(curr, &val));
213260710Savg			FPRINTF(fp, "%f", val);
214260710Savg			break;
215260710Savg		}
216260710Savg
217260710Savg		case DATA_TYPE_NVLIST: {
218260710Savg			if (nvlist_print_json(fp,
219260710Savg			    fnvpair_value_nvlist(curr)) == -1)
220260710Savg				return (-1);
221260710Savg			break;
222260710Savg		}
223260710Savg
224260710Savg		case DATA_TYPE_STRING_ARRAY: {
225260710Savg			char **val;
226260710Savg			uint_t valsz, i;
227260710Savg			VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
228260710Savg			FPRINTF(fp, "[");
229260710Savg			for (i = 0; i < valsz; i++) {
230260710Savg				if (i > 0)
231260710Savg					FPRINTF(fp, ",");
232260710Savg				if (nvlist_print_json_string(fp, val[i]) == -1)
233260710Savg					return (-1);
234260710Savg			}
235260710Savg			FPRINTF(fp, "]");
236260710Savg			break;
237260710Savg		}
238260710Savg
239260710Savg		case DATA_TYPE_NVLIST_ARRAY: {
240260710Savg			nvlist_t **val;
241260710Savg			uint_t valsz, i;
242260710Savg			VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
243260710Savg			FPRINTF(fp, "[");
244260710Savg			for (i = 0; i < valsz; i++) {
245260710Savg				if (i > 0)
246260710Savg					FPRINTF(fp, ",");
247260710Savg				if (nvlist_print_json(fp, val[i]) == -1)
248260710Savg					return (-1);
249260710Savg			}
250260710Savg			FPRINTF(fp, "]");
251260710Savg			break;
252260710Savg		}
253260710Savg
254260710Savg		case DATA_TYPE_BOOLEAN_ARRAY: {
255260710Savg			boolean_t *val;
256260710Savg			uint_t valsz, i;
257260710Savg			VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
258260710Savg			FPRINTF(fp, "[");
259260710Savg			for (i = 0; i < valsz; i++) {
260260710Savg				if (i > 0)
261260710Savg					FPRINTF(fp, ",");
262260710Savg				FPRINTF(fp, val[i] == B_TRUE ?
263260710Savg				    "true" : "false");
264260710Savg			}
265260710Savg			FPRINTF(fp, "]");
266260710Savg			break;
267260710Savg		}
268260710Savg
269260710Savg		case DATA_TYPE_BYTE_ARRAY: {
270260710Savg			uchar_t *val;
271260710Savg			uint_t valsz, i;
272260710Savg			VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
273260710Savg			FPRINTF(fp, "[");
274260710Savg			for (i = 0; i < valsz; i++) {
275260710Savg				if (i > 0)
276260710Savg					FPRINTF(fp, ",");
277260710Savg				FPRINTF(fp, "%hhu", val[i]);
278260710Savg			}
279260710Savg			FPRINTF(fp, "]");
280260710Savg			break;
281260710Savg		}
282260710Savg
283260710Savg		case DATA_TYPE_UINT8_ARRAY: {
284260710Savg			uint8_t *val;
285260710Savg			uint_t valsz, i;
286260710Savg			VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
287260710Savg			FPRINTF(fp, "[");
288260710Savg			for (i = 0; i < valsz; i++) {
289260710Savg				if (i > 0)
290260710Savg					FPRINTF(fp, ",");
291260710Savg				FPRINTF(fp, "%hhu", val[i]);
292260710Savg			}
293260710Savg			FPRINTF(fp, "]");
294260710Savg			break;
295260710Savg		}
296260710Savg
297260710Savg		case DATA_TYPE_INT8_ARRAY: {
298260710Savg			int8_t *val;
299260710Savg			uint_t valsz, i;
300260710Savg			VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
301260710Savg			FPRINTF(fp, "[");
302260710Savg			for (i = 0; i < valsz; i++) {
303260710Savg				if (i > 0)
304260710Savg					FPRINTF(fp, ",");
305275552Sdelphij				FPRINTF(fp, "%hhd", val[i]);
306260710Savg			}
307260710Savg			FPRINTF(fp, "]");
308260710Savg			break;
309260710Savg		}
310260710Savg
311260710Savg		case DATA_TYPE_UINT16_ARRAY: {
312260710Savg			uint16_t *val;
313260710Savg			uint_t valsz, i;
314260710Savg			VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
315260710Savg			FPRINTF(fp, "[");
316260710Savg			for (i = 0; i < valsz; i++) {
317260710Savg				if (i > 0)
318260710Savg					FPRINTF(fp, ",");
319260710Savg				FPRINTF(fp, "%hu", val[i]);
320260710Savg			}
321260710Savg			FPRINTF(fp, "]");
322260710Savg			break;
323260710Savg		}
324260710Savg
325260710Savg		case DATA_TYPE_INT16_ARRAY: {
326260710Savg			int16_t *val;
327260710Savg			uint_t valsz, i;
328260710Savg			VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
329260710Savg			FPRINTF(fp, "[");
330260710Savg			for (i = 0; i < valsz; i++) {
331260710Savg				if (i > 0)
332260710Savg					FPRINTF(fp, ",");
333275552Sdelphij				FPRINTF(fp, "%hd", val[i]);
334260710Savg			}
335260710Savg			FPRINTF(fp, "]");
336260710Savg			break;
337260710Savg		}
338260710Savg
339260710Savg		case DATA_TYPE_UINT32_ARRAY: {
340260710Savg			uint32_t *val;
341260710Savg			uint_t valsz, i;
342260710Savg			VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
343260710Savg			FPRINTF(fp, "[");
344260710Savg			for (i = 0; i < valsz; i++) {
345260710Savg				if (i > 0)
346260710Savg					FPRINTF(fp, ",");
347260710Savg				FPRINTF(fp, "%u", val[i]);
348260710Savg			}
349260710Savg			FPRINTF(fp, "]");
350260710Savg			break;
351260710Savg		}
352260710Savg
353260710Savg		case DATA_TYPE_INT32_ARRAY: {
354260710Savg			int32_t *val;
355260710Savg			uint_t valsz, i;
356260710Savg			VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
357260710Savg			FPRINTF(fp, "[");
358260710Savg			for (i = 0; i < valsz; i++) {
359260710Savg				if (i > 0)
360260710Savg					FPRINTF(fp, ",");
361260710Savg				FPRINTF(fp, "%d", val[i]);
362260710Savg			}
363260710Savg			FPRINTF(fp, "]");
364260710Savg			break;
365260710Savg		}
366260710Savg
367260710Savg		case DATA_TYPE_UINT64_ARRAY: {
368260710Savg			uint64_t *val;
369260710Savg			uint_t valsz, i;
370260710Savg			VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
371260710Savg			FPRINTF(fp, "[");
372260710Savg			for (i = 0; i < valsz; i++) {
373260710Savg				if (i > 0)
374260710Savg					FPRINTF(fp, ",");
375260710Savg				FPRINTF(fp, "%llu",
376260710Savg				    (unsigned long long)val[i]);
377260710Savg			}
378260710Savg			FPRINTF(fp, "]");
379260710Savg			break;
380260710Savg		}
381260710Savg
382260710Savg		case DATA_TYPE_INT64_ARRAY: {
383260710Savg			int64_t *val;
384260710Savg			uint_t valsz, i;
385260710Savg			VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
386260710Savg			FPRINTF(fp, "[");
387260710Savg			for (i = 0; i < valsz; i++) {
388260710Savg				if (i > 0)
389260710Savg					FPRINTF(fp, ",");
390260710Savg				FPRINTF(fp, "%lld", (long long)val[i]);
391260710Savg			}
392260710Savg			FPRINTF(fp, "]");
393260710Savg			break;
394260710Savg		}
395260710Savg
396260710Savg		case DATA_TYPE_UNKNOWN:
397260710Savg			return (-1);
398260710Savg		}
399260710Savg	}
400260710Savg
401260710Savg	FPRINTF(fp, "}");
402260710Savg	return (0);
403260710Savg}
404