summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua M. Clulow <jmc@joyent.com>2014-07-14 17:49:36 -0700
committerJoshua M. Clulow <jmc@joyent.com>2014-07-15 00:49:36 +0000
commitcb5171ae960e29ee19acdd046c573f8ade43c752 (patch)
treeab616e9ccbfc8fec6855de4ba441fde70cd5f1b5
parent909cf22c12f3c9fac948df639bc983d462fc5304 (diff)
downloadillumos-joyent-OS-3068.tar.gz
OS-3068 libnvpair JSON output broken by lint fixesOS-3068
OS-3204 libnvpair JSON cannot print int16 arrays Reviewed by: Robert Mustacchi <rm@joyent.com>
-rw-r--r--usr/src/lib/libnvpair/Makefile.com2
-rw-r--r--usr/src/lib/libnvpair/nvpair_json.c10
-rw-r--r--usr/src/test/test-runner/cmd/Makefile4
-rw-r--r--usr/src/test/test-runner/cmd/run.py2
-rw-r--r--usr/src/test/util-tests/runfiles/default.run5
-rw-r--r--usr/src/test/util-tests/tests/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/Makefile76
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_00_blank.ksh17
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_01_boolean.ksh32
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_02_numbers.ksh50
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_03_empty_arrays.ksh49
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_04_number_arrays.ksh51
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_05_strings.ksh51
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_06_nested.ksh54
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_07_nested_arrays.ksh82
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_common.ksh16
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/print_json.c827
17 files changed, 1323 insertions, 7 deletions
diff --git a/usr/src/lib/libnvpair/Makefile.com b/usr/src/lib/libnvpair/Makefile.com
index 1e5d247371..f996e31054 100644
--- a/usr/src/lib/libnvpair/Makefile.com
+++ b/usr/src/lib/libnvpair/Makefile.com
@@ -60,10 +60,12 @@ LINTFLAGS += -erroff=E_BAD_FORMAT_STR2
LINTFLAGS += -erroff=E_INVALID_TOKEN_IN_DEFINE_MACRO
LINTFLAGS += -erroff=E_RET_INT_IMPLICITLY
LINTFLAGS += -erroff=E_FUNC_USED_VAR_ARG2
+LINTFLAGS += -erroff=E_CONSTANT_CONDITION
LINTFLAGS64 += -erroff=E_BAD_FORMAT_STR2
LINTFLAGS64 += -erroff=E_INVALID_TOKEN_IN_DEFINE_MACRO
LINTFLAGS64 += -erroff=E_RET_INT_IMPLICITLY
LINTFLAGS64 += -erroff=E_FUNC_USED_VAR_ARG2
+LINTFLAGS64 += -erroff=E_CONSTANT_CONDITION
CERRWARN += -_gcc=-Wno-type-limits
CERRWARN += -_gcc=-Wno-parentheses
diff --git a/usr/src/lib/libnvpair/nvpair_json.c b/usr/src/lib/libnvpair/nvpair_json.c
index 39f036fbc3..0eeec458d4 100644
--- a/usr/src/lib/libnvpair/nvpair_json.c
+++ b/usr/src/lib/libnvpair/nvpair_json.c
@@ -20,9 +20,11 @@
#include "libnvpair.h"
-#define FPRINTF(fp, ...) \
- if (fprintf(fp, __VA_ARGS__) < 0) \
- return (-1) \
+#define FPRINTF(fp, ...) \
+ do { \
+ if (fprintf(fp, __VA_ARGS__) < 0) \
+ return (-1); \
+ } while (0)
/*
* When formatting a string for JSON output we must escape certain characters,
@@ -328,7 +330,7 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
for (i = 0; i < valsz; i++) {
if (i > 0)
FPRINTF(fp, ",");
- FPRINTF(fp, "%hhd", val[i]);
+ FPRINTF(fp, "%hd", val[i]);
}
FPRINTF(fp, "]");
break;
diff --git a/usr/src/test/test-runner/cmd/Makefile b/usr/src/test/test-runner/cmd/Makefile
index 33e7a61275..948aea9ed8 100644
--- a/usr/src/test/test-runner/cmd/Makefile
+++ b/usr/src/test/test-runner/cmd/Makefile
@@ -34,4 +34,6 @@ $(ROOTBIN):
$(INS.dir)
$(ROOTBIN)/%: %.py
- $(INS.rename)
+ $(RM) $@
+ $(SED.py) $< > $@
+ $(CHMOD) 0555 $@
diff --git a/usr/src/test/test-runner/cmd/run.py b/usr/src/test/test-runner/cmd/run.py
index da0bca1d26..c1d73a1184 100644
--- a/usr/src/test/test-runner/cmd/run.py
+++ b/usr/src/test/test-runner/cmd/run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.6
+#!ON_PYTHON_26
#
# This file and its contents are supplied under the terms of the
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index d78cdbf77b..3f7498e23b 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -26,3 +26,8 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/allowed-ips]
[/opt/util-tests/tests/xargs_test]
+
+[/opt/util-tests/tests/libnvpair_json]
+tests = ['json_00_blank', 'json_01_boolean', 'json_02_numbers',
+ 'json_03_empty_arrays', 'json_04_number_arrays', 'json_05_strings',
+ 'json_06_nested', 'json_07_nested_arrays']
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index 892b9ce57c..151ff11b6a 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -14,6 +14,6 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
#
-SUBDIRS = dladm printf xargs
+SUBDIRS = dladm libnvpair_json printf xargs
include $(SRC)/test/Makefile.com
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/Makefile b/usr/src/test/util-tests/tests/libnvpair_json/Makefile
new file mode 100644
index 0000000000..4ceb5b6d0e
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/Makefile
@@ -0,0 +1,76 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2014 Joyent, Inc. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests
+TESTDIR = $(ROOTOPTPKG)/tests/libnvpair_json
+ROOTBINDIR = $(ROOTOPTPKG)/bin
+
+PROG = print_json
+
+SCRIPTS = \
+ json_00_blank \
+ json_01_boolean \
+ json_02_numbers \
+ json_03_empty_arrays \
+ json_04_number_arrays \
+ json_05_strings \
+ json_06_nested \
+ json_07_nested_arrays \
+ json_common
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+OBJS = $(PROG:%=%.o)
+SRCS = $(OBJS:%.o=%.c)
+
+CMDS = $(PROG:%=$(ROOTBINDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+LDLIBS += -lnvpair
+
+LINTFLAGS += -erroff=E_FUNC_ARG_UNUSED
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(CMDS)
+
+lint: lint_SRCS
+
+clobber: clean
+ -$(RM) $(PROG)
+
+clean:
+ -$(RM) $(OBJS)
+
+$(CMDS): $(TESTDIR) $(PROG)
+
+$(ROOTBINDIR):
+ $(INS.dir)
+
+$(ROOTBINDIR)/%: %
+ $(INS.file)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_00_blank.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_00_blank.ksh
new file mode 100644
index 0000000000..861a10601d
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_00_blank.ksh
@@ -0,0 +1,17 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+/*
+ * Emit a blank object.
+ */
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_01_boolean.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_01_boolean.ksh
new file mode 100644
index 0000000000..35c4233339
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_01_boolean.ksh
@@ -0,0 +1,32 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"bool0":true,\
+"a fact":true,\
+"a fiction":false,\
+"1":true,\
+" ":true\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+/*
+ * add_boolean calls nvlist_add_boolean(), which the JSON formatter
+ * will emit as a true-valued boolean.
+ */
+add_boolean "bool0";
+add_boolean_value "a fact" "true";
+add_boolean_value "a fiction" "false";
+add_boolean "1";
+
+/*
+ * Test a key with a whitespace-only name:
+ */
+add_boolean " ";
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_02_numbers.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_02_numbers.ksh
new file mode 100644
index 0000000000..ae9f432b8c
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_02_numbers.ksh
@@ -0,0 +1,50 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"byte":255,\
+"uint8_0":0,\
+"uint8_100":100,\
+"uint8_255":255,\
+"uint16":12345,\
+"uint32":23423423,\
+"uint64":19850709000000,\
+"int16_small":-32768,\
+"int8_neg":-128,\
+"int8_pos":127,\
+"int16_big":32767,\
+"int32":-1270000,\
+"int64":-12700000000001,\
+"double_small":0.000023,\
+"double_big":2342300000000.000000\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_byte "byte" "0";
+add_byte "byte" "255";
+
+add_uint8 "uint8_0" "0";
+add_uint8 "uint8_100" "100";
+add_uint8 "uint8_255" "255";
+
+add_uint16 "uint16" "12345";
+add_uint32 "uint32" "23423423";
+add_uint64 "uint64" "19850709000000";
+
+add_int16 "int16_small" "-32768";
+add_int8 "int8_neg" "-128";
+add_int8 "int8_pos" "127";
+add_int16 "int16_big" "32767";
+
+add_int32 "int32" "-1270000";
+add_int64 "int64" "-12700000000001";
+
+add_double "double_small" "0.000023423";
+add_double "double_big" "0.000023423e17";
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_03_empty_arrays.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_03_empty_arrays.ksh
new file mode 100644
index 0000000000..31edf2cc82
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_03_empty_arrays.ksh
@@ -0,0 +1,49 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"boolean_array":[],\
+"byte_array":[],\
+"uint8_array":[],\
+"uint16_array":[],\
+"uint32_array":[],\
+"uint64_array":[],\
+"int8_array":[],\
+"int16_array":[],\
+"int32_array":[],\
+"int64_array":[],\
+"string_array":[],\
+"object_array":[{}]\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_boolean_array "boolean_array";
+
+add_byte_array "byte_array";
+
+add_uint8_array "uint8_array";
+add_uint16_array "uint16_array";
+add_uint32_array "uint32_array";
+add_uint64_array "uint64_array";
+
+add_int8_array "int8_array";
+add_int16_array "int16_array";
+add_int32_array "int32_array";
+add_int64_array "int64_array";
+
+add_string_array "string_array";
+
+/*
+ * The testing DSL does not presently support the generation of a completely
+ * empty object array. Thus, the following directive will produce an array
+ * with a single keyless object:
+ */
+add_object_array "object_array";
+end;
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_04_number_arrays.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_04_number_arrays.ksh
new file mode 100644
index 0000000000..1846e3d5b0
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_04_number_arrays.ksh
@@ -0,0 +1,51 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"byte_array":[0,1,2,10,15,100,103,127,128,254,255],\
+"uint8_array":[128,254,255,10,15,100,103,127,0,1,2],\
+"uint16_array":[0,1000,2000,3210,4321,5432,10000,15000,16384,\
+17992,35012,65535,0],\
+"uint32_array":[0,4294967295,4026531855,1,2,1000,501],\
+"uint64_array":[19850907,0,18446744073709551615],\
+"int8_array":[39,39,39,39,39,39,39,-128,-127,0,127],\
+"int16_array":[7532,-32768,0,32767,0,-32768,100],\
+"int32_array":[-2147483648,0,32767,-32768,2147483647],\
+"int64_array":[0,0,9223372036854775807,1,1,1,-9223372036854775808,0]\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_byte_array "byte_array"
+ "0" "1" "2" "10" "15" "100" "103" "127" "128" "254" "255";
+
+add_uint8_array "uint8_array"
+ "128" "254" "255" "10" "15" "100" "103" "127" "0" "1" "2";
+
+add_uint16_array "uint16_array"
+ "0" "1000" "2000" "3210" "4321" "5432" "10000" "15000" "16384"
+ "17992" "35012" "65535" "0";
+
+add_uint32_array "uint32_array"
+ "0" "4294967295" "4026531855" "1" "2" "1000" "501";
+
+add_uint64_array "uint64_array"
+ "19850907" "0" "18446744073709551615";
+
+add_int8_array "int8_array"
+ "39" "39" "39" "39" "39" "39" "39" "-128" "-127" "0" "127";
+
+add_int16_array "int16_array"
+ "7532" "-32768" "0" "32767" "0" "-32768" "100";
+
+add_int32_array "int32_array"
+ "-2147483648" "0" "32767" "-32768" "2147483647";
+
+add_int64_array "int64_array"
+ "0" "0" "9223372036854775807" "1" "1" "1" "-9223372036854775808" "0";
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_05_strings.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_05_strings.ksh
new file mode 100644
index 0000000000..1f3b56380a
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_05_strings.ksh
@@ -0,0 +1,51 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+#
+# This test checks UTF-8 parsing behaviour
+#
+export LC_ALL="en_US.UTF-8"
+export LANG="${LANG}"
+
+BASELINE="$(cat <<EOF
+{\
+"blank":"",\
+"":"blank key",\
+" ":"whitespace key",\
+"\ttab\t":"tab key",\
+"escapes":"escape \u001b newline \n cr \r backslash \\\\ quote \"",\
+"escape array":[\
+"escape \u001b",\
+"alarm \u0007",\
+"backspace \b",\
+"formfeed \f",\
+"newline \n",\
+"return \r",\
+"tab \t",\
+"vertical tab \u000b",\
+"black circle (UTF-8) \u25cf"\
+]\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_string "blank" "";
+add_string "" "blank key";
+add_string " " "whitespace key";
+add_string " tab " "tab key";
+add_string "escapes" "escape \x1b newline \n cr \r backslash \\ quote \"";
+add_string_array "escape array"
+ "escape \x1b"
+ "alarm \a"
+ "backspace \b"
+ "formfeed \f"
+ "newline \n"
+ "return \r"
+ "tab \t"
+ "vertical tab \v"
+ "black circle (UTF-8) \xe2\x97\x8f";
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_06_nested.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_06_nested.ksh
new file mode 100644
index 0000000000..6044093d27
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_06_nested.ksh
@@ -0,0 +1,54 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"a":{},\
+"b":{\
+"name":"Roger","age":35\
+},\
+"c":{\
+"d":{\
+"name":"Stephen","age":27},\
+"e":{\
+"name":"Roberta","age":43,"pet":{\
+"name":"Mister Bumberscratch",\
+"species":"cat",\
+"alive":true,\
+"available_legs":[1,2,3,4]\
+}\
+}\
+}\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_object "a";
+end;
+
+add_object "b";
+ add_string "name" "Roger";
+ add_uint16 "age" "35";
+end;
+
+add_object "c";
+ add_object "d";
+ add_string "name" "Stephen";
+ add_uint16 "age" "27";
+ end;
+ add_object "e";
+ add_string "name" "Roberta";
+ add_uint16 "age" "43";
+ add_object "pet";
+ add_string "name" "Mister Bumberscratch";
+ add_string "species" "cat";
+ add_boolean_value "alive" "true";
+ add_uint8_array "available_legs" "1" "2" "3" "4";
+ end;
+ end;
+end;
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_07_nested_arrays.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_07_nested_arrays.ksh
new file mode 100644
index 0000000000..003f499b6b
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_07_nested_arrays.ksh
@@ -0,0 +1,82 @@
+#!/bin/ksh
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"event_store":{\
+"name":"Occurences",\
+"events":[\
+{"time":489715200,"desc":"inception"},\
+{"time":1057708800,"desc":"maturation"},\
+{"time":1344816000,"desc":"migration"},\
+{"time":1405296000,"desc":"integration"},\
+{}\
+]\
+},\
+"first level":[\
+{"second_level_0":[{\
+"sl0_a":true,\
+"sl0_b":"aaaa"\
+},\
+{"x":1234}\
+],\
+"second_level_1":[{}],\
+"second_level_2":[\
+{"alpha":"a"},\
+{"beta":"b"},\
+{"gamma":"c"},\
+{"delta":"d"},\
+{"order":["a","b","c","d"]}\
+]\
+}\
+]\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<'EOF'
+add_object "event_store";
+ add_string "name" "Occurences";
+ add_object_array "events";
+ add_uint32 "time" "489715200";
+ add_string "desc" "inception";
+ next;
+
+ add_uint32 "time" "1057708800";
+ add_string "desc" "maturation";
+ next;
+
+ add_uint32 "time" "1344816000";
+ add_string "desc" "migration";
+ next;
+
+ add_uint32 "time" "1405296000";
+ add_string "desc" "integration";
+ next;
+ end;
+end;
+add_object_array "first level";
+ add_object_array "second_level_0";
+ add_boolean "sl0_a";
+ add_string "sl0_b" "aaaa";
+ next;
+ add_int32 "x" "1234";
+ end;
+ add_object_array "second_level_1";
+ end;
+ add_object_array "second_level_2";
+ add_string "alpha" "a";
+ next;
+ add_string "beta" "b";
+ next;
+ add_string "gamma" "c";
+ next;
+ add_string "delta" "d";
+ next;
+ add_string_array "order" "a" "b" "c" "d";
+ end;
+end;
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_common.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_common.ksh
new file mode 100644
index 0000000000..1f99a333d6
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_common.ksh
@@ -0,0 +1,16 @@
+#!/bin/ksh
+
+function complete {
+ if [[ "${PRINT_OUTPUT}" ]]; then
+ printf "%s\n" "${OUTPUT}"
+ exit 0
+ elif [[ "${OUTPUT}" == "${BASELINE}" ]]; then
+ printf "TEST PASS: %s\n" "$(basename $0)"
+ exit 0
+ else
+ printf "TEST FAIL: %s\n" "$(basename $0)"
+ printf "EXPECTED: %s\n" "${BASELINE}"
+ printf "ACTUAL: %s\n" "${OUTPUT}"
+ exit 1
+ fi
+}
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/print_json.c b/usr/src/test/util-tests/tests/libnvpair_json/print_json.c
new file mode 100644
index 0000000000..e34ae8f7b1
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/print_json.c
@@ -0,0 +1,827 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2014 Joyent, Inc.
+ */
+
+/*
+ * This program implements a small domain-specific language (DSL) for the
+ * generation of nvlists, and subsequent printing in JSON-formatted output.
+ * The test suite uses this tool to drive the JSON formatting routines in
+ * libnvpair(3LIB) for testing.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <locale.h>
+
+#include <libnvpair.h>
+
+#define MAX_ARGS 100
+#define CMD_NAME_LEN 50
+
+/*
+ * As we are parsing a language that allows the creation of arbitrarily nested
+ * state, i.e. both nested nvlists and arrays of nested nvlists, we store that
+ * state in a stack. The top frame in the stack represents the nested nvlist
+ * (or nvlists, for an array) that we are currently building.
+ *
+ * When creating an array, the "next" directive advances lw_pos and allocates a
+ * new nvlist. The "end" directive commits either the nvlist, or array of
+ * nvlists, into the parent nvlist. It then pops and frees the stack frame
+ * before returning control to the parser.
+ */
+
+typedef struct list_wrap {
+ nvlist_t *lw_nvl[MAX_ARGS];
+ char *lw_name;
+ int lw_pos;
+ boolean_t lw_array;
+ struct list_wrap *lw_next;
+} list_wrap_t;
+
+int
+list_wrap_depth(list_wrap_t *lw)
+{
+ int d = 0;
+
+ while (lw != NULL) {
+ d++;
+ lw = lw->lw_next;
+ }
+
+ return (d);
+}
+
+list_wrap_t *
+list_wrap_alloc(list_wrap_t *next)
+{
+ list_wrap_t *out = calloc(1, sizeof (list_wrap_t));
+
+ if (out == NULL)
+ abort();
+
+ out->lw_next = next;
+
+ return (out);
+}
+
+list_wrap_t *
+list_wrap_pop_and_free(list_wrap_t *lw)
+{
+ list_wrap_t *next = lw->lw_next;
+
+ free(lw->lw_name);
+ free(lw);
+
+ return (next);
+}
+
+/*
+ * Generic integer and floating point parsing routines:
+ */
+
+int
+parse_int(char *in, int64_t *val, int64_t min, int64_t max)
+{
+ int64_t t;
+ char *end = NULL;
+
+ errno = 0;
+ t = strtoll(in, &end, 10);
+ if (errno != 0 || end == in || *end != '\0') {
+ if (errno == ERANGE) {
+ (void) fprintf(stderr, "ERROR: integer %s not in "
+ "range [%lld,%lld]\n", in, min, max);
+ return (-1);
+ }
+ (void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
+ "signed integer (%s)\n", in, strerror(errno));
+ return (-1);
+ }
+
+ if (t < min || t > max) {
+ (void) fprintf(stderr, "ERROR: integer %lld not in range "
+ "[%lld,%lld]\n", t, min, max);
+ return (-1);
+ }
+
+ *val = t;
+ return (0);
+}
+
+int
+parse_uint(char *in, uint64_t *val, uint64_t min, uint64_t max)
+{
+ uint64_t t;
+ char *end = NULL;
+
+ errno = 0;
+ t = strtoull(in, &end, 10);
+ if (errno != 0 || end == in || *end != '\0') {
+ if (errno == ERANGE) {
+ (void) fprintf(stderr, "ERROR: integer %s not in "
+ "range [%llu,%llu]\n", in, min, max);
+ return (-1);
+ }
+ (void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
+ "unsigned integer (%s)\n", in, strerror(errno));
+ return (-1);
+ }
+
+ if (t < min || t > max) {
+ (void) fprintf(stderr, "ERROR: integer %llu not in range "
+ "[%llu,%llu]\n", t, min, max);
+ return (-1);
+ }
+
+ *val = t;
+ return (0);
+}
+
+int
+parse_double(char *in, double *val)
+{
+ double t;
+ char *end = NULL;
+
+ errno = 0;
+ t = strtod(in, &end);
+ if (errno != 0 || end == in || *end != '\0') {
+ (void) fprintf(stderr, "ERROR: could not parse \"%s\" as "
+ "double\n", in);
+ return (-1);
+ }
+
+ *val = t;
+ return (0);
+}
+
+/*
+ * Command-specific handlers for directives specified in the DSL input:
+ */
+
+typedef int (*command_handler_t)(list_wrap_t **, boolean_t, int,
+ char **);
+
+static int
+ch_add_string(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ if (array) {
+ if (nvlist_add_string_array(nvl, argv[0], &argv[1],
+ argc - 1) != 0) {
+ (void) fprintf(stderr, "fail at "
+ "nvlist_add_string_array\n");
+ return (-1);
+ }
+ } else {
+ if (nvlist_add_string(nvl, argv[0], argv[1]) != 0) {
+ (void) fprintf(stderr, "fail at nvlist_add_string\n");
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ch_add_boolean(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ if (array)
+ abort();
+
+ if (nvlist_add_boolean(nvl, argv[0]) != 0) {
+ (void) fprintf(stderr, "fail at nvlist_add_boolean\n");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+ch_add_boolean_value(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ int i;
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+ boolean_t arrval[MAX_ARGS];
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "true") == 0) {
+ arrval[i - 1] = B_TRUE;
+ } else if (strcmp(argv[i], "false") == 0) {
+ arrval[i - 1] = B_FALSE;
+ } else {
+ (void) fprintf(stderr, "invalid boolean value: %s\n",
+ argv[i]);
+ return (-1);
+ }
+ }
+
+ if (array) {
+ if (nvlist_add_boolean_array(nvl, argv[0], arrval,
+ argc - 1) != 0) {
+ (void) fprintf(stderr, "fail at "
+ "nvlist_add_boolean_array\n");
+ return (-1);
+ }
+ } else {
+ if (nvlist_add_boolean_value(nvl, argv[0], arrval[0]) != 0) {
+ (void) fprintf(stderr, "fail at "
+ "nvlist_add_boolean_value\n");
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * The confluence of a strongly typed C API for libnvpair(3LIB) and the
+ * combinatorial explosion of both sizes and signedness is unfortunate. Rather
+ * than reproduce the same code over and over, this macro parses an integer,
+ * checks applicable bounds based on size and signedness, and stores the value
+ * (or array of values).
+ */
+#define DO_CMD_NUMBER(typ, nam, min, max, ptyp, func) \
+ ptyp val; \
+ typ ## _t arrval[MAX_ARGS]; \
+ int i; \
+ for (i = 1; i < argc; i++) { \
+ if (func(argv[i], &val, min, max) != 0) { \
+ return (-1); \
+ } \
+ arrval[i - 1] = (typ ## _t) val; \
+ } \
+ if (array) { \
+ if (nvlist_add_ ## nam ## _array(nvl, argv[0], \
+ arrval, argc - 1) != 0) { \
+ (void) fprintf(stderr, "fail at " \
+ "nvlist_add_" #nam "_array\n"); \
+ return (-1); \
+ } \
+ } else { \
+ if (nvlist_add_ ## nam(nvl, argv[0], \
+ arrval[0]) == -1) { \
+ (void) fprintf(stderr, "fail at " \
+ "nvlist_add_" #nam "\n"); \
+ return (-1); \
+ } \
+ } \
+ return (0);
+
+static int
+ch_add_byte(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(uchar, byte, 0, UCHAR_MAX, uint64_t, parse_uint)
+}
+
+static int
+ch_add_int8(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(int8, int8, INT8_MIN, INT8_MAX, int64_t, parse_int)
+}
+
+static int
+ch_add_uint8(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(uint8, uint8, 0, UINT8_MAX, uint64_t, parse_uint)
+}
+
+static int
+ch_add_int16(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(int16, int16, INT16_MIN, INT16_MAX, int64_t, parse_int)
+}
+
+static int
+ch_add_uint16(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(uint16, uint16, 0, UINT16_MAX, uint64_t, parse_uint)
+}
+
+static int
+ch_add_int32(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(int32, int32, INT32_MIN, INT32_MAX, int64_t, parse_int)
+}
+
+static int
+ch_add_uint32(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(uint32, uint32, 0, UINT32_MAX, uint64_t, parse_uint)
+}
+
+static int
+ch_add_int64(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(int64, int64, INT64_MIN, INT64_MAX, int64_t, parse_int)
+}
+
+static int
+ch_add_uint64(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+
+ DO_CMD_NUMBER(uint64, uint64, 0, UINT64_MAX, uint64_t, parse_uint)
+}
+
+static int
+ch_add_double(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *nvl = (*lw)->lw_nvl[(*lw)->lw_pos];
+ double val;
+
+ if (array)
+ abort();
+
+ if (parse_double(argv[1], &val) != 0) {
+ return (-1);
+ }
+
+ if (nvlist_add_double(nvl, argv[0], val) != 0) {
+ (void) fprintf(stderr, "fail at nvlist_add_double_value\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+ch_end(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ nvlist_t *parent;
+ char *name;
+
+ if (list_wrap_depth(*lw) < 2) {
+ (void) fprintf(stderr, "ERROR: not nested, cannot end.\n");
+ return (-1);
+ }
+
+ parent = (*lw)->lw_next->lw_nvl[(*lw)->lw_next->lw_pos];
+ name = (*lw)->lw_name;
+ if ((*lw)->lw_array) {
+ /*
+ * This was an array of objects.
+ */
+ nvlist_t **children = (*lw)->lw_nvl;
+ int nelems = (*lw)->lw_pos + 1;
+
+ if (nvlist_add_nvlist_array(parent, name, children,
+ nelems) != 0) {
+ (void) fprintf(stderr, "fail at "
+ "nvlist_add_nvlist_array\n");
+ return (-1);
+ }
+ } else {
+ /*
+ * This was a single object.
+ */
+ nvlist_t *child = (*lw)->lw_nvl[0];
+
+ if ((*lw)->lw_pos != 0)
+ abort();
+
+ if (nvlist_add_nvlist(parent, name, child) != 0) {
+ (void) fprintf(stderr, "fail at nvlist_add_nvlist\n");
+ return (-1);
+ }
+ }
+
+ *lw = list_wrap_pop_and_free(*lw);
+
+ return (0);
+}
+
+static int
+ch_next(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ if (!(*lw)->lw_array) {
+ (void) fprintf(stderr, "ERROR: cannot use 'next' outside an "
+ "object array.\n");
+ return (-1);
+ }
+
+ if ((*lw)->lw_pos++ >= MAX_ARGS) {
+ (void) fprintf(stderr, "ERROR: object array too long\n");
+ return (-1);
+ }
+
+ if (nvlist_alloc(&(*lw)->lw_nvl[(*lw)->lw_pos], NV_UNIQUE_NAME,
+ 0) != 0) {
+ (void) fprintf(stderr, "ERROR: failed at nvlist_alloc\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+ch_add_object(list_wrap_t **lw, boolean_t array, int argc, char **argv)
+{
+ *lw = list_wrap_alloc(*lw);
+
+ (*lw)->lw_name = strdup(argv[0]);
+ (*lw)->lw_array = array;
+
+ if (nvlist_alloc(&(*lw)->lw_nvl[0], NV_UNIQUE_NAME, 0) != 0) {
+ (void) fprintf(stderr, "fail at nvlist_alloc\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+typedef struct command {
+ char cmd_name[CMD_NAME_LEN];
+ command_handler_t cmd_func;
+ int cmd_min_args;
+ int cmd_max_args;
+ boolean_t cmd_array_mode;
+} command_t;
+
+/*
+ * These are the commands we support in the testing DSL, and their
+ * handling functions:
+ */
+command_t command_handlers[] = {
+ { "add_boolean", ch_add_boolean, 1, 1, B_FALSE },
+ { "add_boolean_value", ch_add_boolean_value, 2, 2, B_FALSE },
+ { "add_byte", ch_add_byte, 2, 2, B_FALSE },
+ { "add_int8", ch_add_int8, 2, 2, B_FALSE },
+ { "add_uint8", ch_add_uint8, 2, 2, B_FALSE },
+ { "add_int16", ch_add_int16, 2, 2, B_FALSE },
+ { "add_uint16", ch_add_uint16, 2, 2, B_FALSE },
+ { "add_int32", ch_add_int32, 2, 2, B_FALSE },
+ { "add_uint32", ch_add_uint32, 2, 2, B_FALSE },
+ { "add_int64", ch_add_int64, 2, 2, B_FALSE },
+ { "add_uint64", ch_add_uint64, 2, 2, B_FALSE },
+ { "add_double", ch_add_double, 2, 2, B_FALSE },
+ { "add_string", ch_add_string, 2, 2, B_FALSE },
+ { "add_object", ch_add_object, 1, 1, B_FALSE },
+ { "add_boolean_array", ch_add_boolean_value, 1, MAX_ARGS, B_TRUE },
+ { "add_byte_array", ch_add_byte, 1, MAX_ARGS, B_TRUE },
+ { "add_int8_array", ch_add_int8, 1, MAX_ARGS, B_TRUE },
+ { "add_uint8_array", ch_add_uint8, 1, MAX_ARGS, B_TRUE },
+ { "add_int16_array", ch_add_int16, 1, MAX_ARGS, B_TRUE },
+ { "add_uint16_array", ch_add_uint16, 1, MAX_ARGS, B_TRUE },
+ { "add_int32_array", ch_add_int32, 1, MAX_ARGS, B_TRUE },
+ { "add_uint32_array", ch_add_uint32, 1, MAX_ARGS, B_TRUE },
+ { "add_int64_array", ch_add_int64, 1, MAX_ARGS, B_TRUE },
+ { "add_uint64_array", ch_add_uint64, 1, MAX_ARGS, B_TRUE },
+ { "add_string_array", ch_add_string, 1, MAX_ARGS, B_TRUE },
+ { "add_object_array", ch_add_object, 1, 1, B_TRUE },
+ { "end", ch_end, 0, 0, B_FALSE },
+ { "next", ch_next, 0, 0, B_FALSE },
+ { 0 }
+};
+
+/*
+ * This function determines which command we are executing, checks argument
+ * counts, and dispatches to the appropriate handler:
+ */
+static int
+command_call(list_wrap_t **lw, char *command, int argc, char **argv)
+{
+ int ch;
+
+ for (ch = 0; command_handlers[ch].cmd_name[0] != '\0'; ch++) {
+ if (strcmp(command, command_handlers[ch].cmd_name) != 0)
+ continue;
+
+ if (argc > command_handlers[ch].cmd_max_args ||
+ argc < command_handlers[ch].cmd_min_args) {
+
+ (void) fprintf(stderr, "ERROR: command \"%s\""
+ " expects between %d and %d arguments,"
+ " but %d were provided.\n", command,
+ command_handlers[ch].cmd_min_args,
+ command_handlers[ch].cmd_max_args,
+ argc);
+
+ return (-1);
+ }
+
+ return (command_handlers[ch].cmd_func(lw,
+ command_handlers[ch].cmd_array_mode, argc, argv));
+ }
+
+ (void) fprintf(stderr, "ERROR: invalid command: \"%s\"\n", command);
+
+ return (-1);
+}
+
+/*
+ * The primary state machine for parsing the input DSL is implemented in
+ * this function:
+ */
+
+typedef enum state {
+ STATE_REST = 1,
+ STATE_COMMAND,
+ STATE_ARG_FIND,
+ STATE_ARG,
+ STATE_ARG_ESCAPE,
+ STATE_ARG_ESCAPE_HEX,
+ STATE_C_COMMENT_0,
+ STATE_C_COMMENT_1,
+ STATE_C_COMMENT_2
+} state_t;
+
+int
+parse(FILE *in, list_wrap_t **lw)
+{
+ char b[8192];
+ int bp;
+ state_t st = STATE_REST;
+ int argc = 0;
+ char *argv[MAX_ARGS];
+ int line = 1;
+ char hex[3];
+ int nhex = 0;
+
+ b[0] = '\0';
+ bp = 0;
+
+ for (;;) {
+ int c = fgetc(in);
+
+ /*
+ * Signal an error if the file ends part way through a
+ * construct:
+ */
+ if (st != STATE_REST && c == EOF) {
+ (void) fprintf(stderr, "ERROR: unexpected end of "
+ "file\n");
+ return (-1);
+ } else if (c == EOF) {
+ return (0);
+ }
+
+ if (c == '\n')
+ line++;
+
+ switch (st) {
+ case STATE_REST:
+ if (isalpha(c) || c == '_') {
+ argc = 0;
+ bp = 0;
+ b[bp++] = c;
+ b[bp] = '\0';
+ st = STATE_COMMAND;
+ continue;
+ } else if (c == ' ' || c == '\t' || c == '\n') {
+ /*
+ * Ignore whitespace.
+ */
+ continue;
+ } else if (c == '/') {
+ st = STATE_C_COMMENT_0;
+ continue;
+ } else {
+ goto unexpected;
+ }
+
+ case STATE_C_COMMENT_0:
+ if (c != '*') {
+ goto unexpected;
+ }
+ st = STATE_C_COMMENT_1;
+ continue;
+
+ case STATE_C_COMMENT_1:
+ if (c == '*') {
+ st = STATE_C_COMMENT_2;
+ }
+ continue;
+
+ case STATE_C_COMMENT_2:
+ if (c == '/') {
+ st = STATE_REST;
+ } else if (c != '*') {
+ st = STATE_C_COMMENT_1;
+ }
+ continue;
+
+ case STATE_COMMAND:
+ if (isalnum(c) || c == '_') {
+ b[bp++] = c;
+ b[bp] = '\0';
+ st = STATE_COMMAND;
+
+ continue;
+
+ } else if (isspace(c)) {
+ /*
+ * Start collecting arguments into 'b'
+ * after the command.
+ */
+ st = STATE_ARG_FIND;
+ bp++;
+
+ continue;
+ } else if (c == ';') {
+ /*
+ * This line was _just_ a command,
+ * so break out and process now:
+ */
+ goto execute;
+ } else {
+ goto unexpected;
+ }
+
+ case STATE_ARG_FIND:
+ if (isspace(c)) {
+ /*
+ * Whitespace, ignore.
+ */
+ continue;
+
+ } else if (c == ';') {
+ /*
+ * Break out to process command.
+ */
+ goto execute;
+
+ } else if (c == '"') {
+ st = STATE_ARG;
+
+ argv[argc] = &b[++bp];
+ b[bp] = '\0';
+
+ continue;
+ } else {
+ goto unexpected;
+ }
+
+ case STATE_ARG:
+ if (c == '"') {
+ if (argc++ >= MAX_ARGS) {
+ (void) fprintf(stderr, "ERROR: too "
+ "many args\n");
+ return (-1);
+ }
+ st = STATE_ARG_FIND;
+ continue;
+ } else if (c == '\n') {
+ (void) fprintf(stderr, "ERROR: line not "
+ "finished\n");
+ return (-1);
+ } else if (c == '\\') {
+ st = STATE_ARG_ESCAPE;
+ continue;
+ } else {
+ b[bp++] = c;
+ b[bp] = '\0';
+ continue;
+ }
+
+ case STATE_ARG_ESCAPE:
+ if (c == 'a') {
+ c = '\a';
+ } else if (c == 'b') {
+ c = '\b';
+ } else if (c == 'f') {
+ c = '\f';
+ } else if (c == 'n') {
+ c = '\n';
+ } else if (c == 'r') {
+ c = '\r';
+ } else if (c == 't') {
+ c = '\t';
+ } else if (c == 'v') {
+ c = '\v';
+ } else if (c == 'x') {
+ st = STATE_ARG_ESCAPE_HEX;
+ hex[0] = hex[1] = hex[2] = '\0';
+ nhex = 0;
+ continue;
+ } else if (c != '\\' && c != '"') {
+ goto unexpected;
+ }
+
+ b[bp++] = c;
+ b[bp] = '\0';
+ st = STATE_ARG;
+ continue;
+
+ case STATE_ARG_ESCAPE_HEX:
+ if (!isxdigit(c)) {
+ goto unexpected;
+ }
+ hex[nhex] = c;
+ if (nhex++ >= 1) {
+ /*
+ * The hex escape pair is complete, parse
+ * the integer and insert it as a character:
+ */
+ int x;
+ errno = 0;
+ if ((x = strtol(hex, NULL, 16)) == 0 ||
+ errno != 0) {
+ goto unexpected;
+ }
+ b[bp++] = (char)x;
+ b[bp] = '\0';
+ st = STATE_ARG;
+ }
+ continue;
+ }
+
+ /*
+ * We do not ever expect to break out of the switch block
+ * above. If we do, it's a programmer error.
+ */
+ abort();
+
+execute:
+ if (command_call(lw, b, argc, argv) == -1)
+ return (-1);
+
+ st = STATE_REST;
+ continue;
+
+unexpected:
+ (void) fprintf(stderr, "ERROR: (line %d) unexpected "
+ "character: %c\n", line, c);
+ return (-1);
+ }
+}
+
+/*
+ * Entry point:
+ */
+int
+main(int argc, char **argv)
+{
+ int rc = EXIT_FAILURE;
+ list_wrap_t *lw;
+
+ /*
+ * Be locale-aware. The JSON output functions will process multibyte
+ * characters in the current locale, and emit a correct JSON encoding
+ * for unprintable characters.
+ */
+ if (setlocale(LC_ALL, "") == NULL) {
+ (void) fprintf(stderr, "Could not set locale: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ lw = list_wrap_alloc(NULL);
+
+ if (nvlist_alloc(&lw->lw_nvl[0], NV_UNIQUE_NAME, 0) != 0)
+ goto out;
+
+ /*
+ * Generate the list from the commands passed to us on stdin:
+ */
+ if (parse(stdin, &lw) != 0)
+ goto out;
+
+ /*
+ * Print the resultant list, and a terminating newline:
+ */
+ if (nvlist_print_json(stdout, lw->lw_nvl[0]) != 0 ||
+ fprintf(stdout, "\n") < 0)
+ goto out;
+
+ rc = EXIT_SUCCESS;
+
+out:
+ (void) list_wrap_pop_and_free(lw);
+
+ return (rc);
+}