]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
client: built-in JSON support
authorVincent Bernat <vincent@bernat.im>
Mon, 23 Jan 2017 16:28:33 +0000 (17:28 +0100)
committerVincent Bernat <vincent@bernat.im>
Sun, 29 Jan 2017 08:23:58 +0000 (09:23 +0100)
Instead of relying on two different libs to serialize JSON, we provide
our own built-in serializer. This reduces the amount of build-related
code (detection of the correct library), as well as the amount of C code
with the exception of UTF-8 handling which was stolen from CCAN.

This will also make the life of some users easier.

The JSON format stays the same: quite verbose with --enable-json0
flag. And inconsistent without it.

Fix #188.
Fix #220.

19 files changed:
.travis.yml
NEWS
README.md
configure.ac
debian/control
debian/copyright
debian/rules
m4/jansson.m4 [deleted file]
m4/json-c.m4 [deleted file]
redhat/lldpd.spec
src/client/Makefile.am
src/client/jansson_writer.c [deleted file]
src/client/json_writer.c [new file with mode: 0644]
src/client/jsonc_writer.c [deleted file]
src/client/lldpcli.c
src/client/utf8.c [new file with mode: 0644]
src/client/writer.h
src/version.c
tests/ci/install.sh

index e35746e1fd69c065f3737d6658803812bdbbeeb9..1ba6ca11cba4df116fe67a312e28a30cfd2114f4 100644 (file)
@@ -16,12 +16,11 @@ env:
     - LLDPD_CONFIG_ARGS="--with-snmp --disable-lldpmed --disable-dot1 --disable-dot3 --disable-custom"
     - LLDPD_CONFIG_ARGS="--enable-oldies"
     - LLDPD_CONFIG_ARGS="--with-seccomp"
-    - LLDPD_CONFIG_ARGS="--with-json=json-c"
 matrix:
   include:
     - os: linux
       compiler: clang
-      env: LLDPD_CONFIG_ARGS="--with-snmp --with-xml --with-json"
+      env: LLDPD_CONFIG_ARGS="--with-snmp --with-xml"
     - os: osx
       compiler: clang
-      env: LLDPD_CONFIG_ARGS="--with-snmp --with-xml --with-json"
+      env: LLDPD_CONFIG_ARGS="--with-snmp --with-xml"
diff --git a/NEWS b/NEWS
index 2b02fb64040e34850a59dbf36d21b9424825342e..9b23fe296f1ca5012612e4b63e955c10381a36bf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 lldpd (0.9.7)
   * Changes:
+    + JSON support is now built-in and unconditionally enabled. Use
+      --enable-json0 to keep the pre-0.9.2 json-c format.
     + When logging to syslog and daemonizing, don't log to stderr.
 
 lldpd (0.9.6)
index 344c35e27cfa425e82a249224219340a7b65d692..28af58db31dd6737c2c6c4f439399bcc0c961014 100644 (file)
--- a/README.md
+++ b/README.md
@@ -77,7 +77,7 @@ simpler alternatives:
  
         mkdir build && cd build
         ../configure --prefix=/usr/local --localstatedir=/var --sysconfdir=/private/etc --with-embedded-libevent \
-            --without-json --without-snmp
+            --without-snmp
         make -C osx pkg
 
     If you want to compile for an older version of OS X, you need
@@ -86,7 +86,7 @@ simpler alternatives:
         SDK=/Developer/SDKs/MacOSX10.6.sdk
         mkdir build && cd build
         ../configure --prefix=/usr/local --localstatedir=/var --sysconfdir=/private/etc --with-embedded-libevent \
-           --without-json --without-snmp \
+           --without-snmp \
            CFLAGS="-mmacosx-version-min=10.6 -isysroot $SDK" \
            LDFLAGS="-mmacosx-version-min=10.6 -isysroot $SDK"
         make -C osx pkg
@@ -97,7 +97,7 @@ simpler alternatives:
 
         mkdir build && cd build
         ../configure --prefix=/usr/local --localstatedir=/var --sysconfdir=/private/etc --with-embedded-libevent \
-           --without-json --without-snmp \
+           --without-snmp \
            CFLAGS="-mmacosx-version-min=10.9" \
            LDFLAGS="-mmacosx-version-min=10.9"
         make -C osx pkg
index 2e81f5b58cc86dc1bdf84ee73ed8107a724722a8..013eca0248d791c6289334c5e4422c767cc9c431 100644 (file)
@@ -269,28 +269,7 @@ AC_ARG_WITH([xml],
   [with_xml=auto])
 lldp_CHECK_XML2
 
-# JSON
-AC_ARG_WITH([json],
-  AS_HELP_STRING(
-    [--with-json],
-    [Enable JSON output via janson or json-c @<:@default=auto@:>@]),
-  [],
-  [with_json=auto])
-if test x"$with_json" = x"yes" -o x"$with_json" = x"auto"; then
-   _with_json="$with_json"
-   with_json=auto
-   lldp_CHECK_JANSSON
-   if test x"$with_json" = x"no"; then
-     with_json=auto
-     lldp_CHECK_JSONC
-   fi
-   if test x"$with_json" = x"no" -a x"$_with_json" = x"yes"; then
-     AC_MSG_FAILURE([*** no JSON support found])
-   fi
-else
-   lldp_CHECK_JANSSON
-   lldp_CHECK_JSONC
-fi
+# JSON (built-in)
 lldp_ARG_ENABLE([json0], [use of pre-0.9.2 JSON/json-c format], [no])
 
 # Seccomp
@@ -389,9 +368,6 @@ AC_SUBST([LLDP_BIN_LDFLAGS])
 AM_CONDITIONAL([HAVE_CHECK], [test x"$have_check" = x"yes"])
 AM_CONDITIONAL([USE_SNMP], [test x"$with_snmp" = x"yes"])
 AM_CONDITIONAL([USE_XML], [test x"$with_xml" = x"yes"])
-AM_CONDITIONAL([USE_JSON], [test x"$with_json" = x"json-c" || test -x"$with_json" = x"jansson"])
-AM_CONDITIONAL([USE_JANSSON], [test x"$with_json" = x"jansson"])
-AM_CONDITIONAL([USE_JSONC], [test x"$with_json" = x"json-c"])
 AM_CONDITIONAL([USE_SECCOMP], [test x"$with_seccomp" = x"yes"])
 dnl If old default of AR_FLAGS is otherwise being used (because of older automake),
 dnl replace it with one without 'u'
@@ -428,7 +404,6 @@ cat <<EOF
   DOT3...........: $enable_dot3
   CUSTOM.........: $enable_custom
   XML output.....: ${with_xml-no}
-  JSON output....: ${with_json-no}
   Oldies support.: $enable_oldies
   seccomp........: ${with_seccomp-no}
 
index 2fbdf85fe25224aa83b3953d56611b9079ae5226..4412be50b172d51b28de521b1259b707b69e2bc5 100644 (file)
@@ -8,7 +8,6 @@ Build-Depends: debhelper (>= 5),
                dh-autoreconf,
                libsnmp-dev,
                libxml2-dev,
-               libjansson-dev | libjson-c-dev | libjson0-dev (>= 0.10),
                libevent-dev (>= 2.0.5),
                libreadline-dev,
                libbsd-dev,
index 0535461cadcb9f124c3e12a253d983e50a8c384f..e2aa1986435bda9628ab6f9b475037e2c3b1fc9d 100644 (file)
@@ -30,6 +30,10 @@ Copyright: Copyright 2001 Niels Provos <provos@citi.umich.edu>
            Copyright (c) 2002 Matthieu Herrb
 License: BSD-2-clause
 
+Files: src/client/utf8.c
+Copyright: Copyright (c) 2011 Joseph A. Adams
+License: Expat
+
 Files: m4/ax_cflags_gcc_option.m4
 Copyright: Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
 License: GPL-2+ with Autoconf exception
@@ -216,3 +220,22 @@ License: GPL-2+ with Autoconf exception
  Macro released by the Autoconf Archive. When you make and distribute a
  modified version of the Autoconf Macro, you may extend this special
  exception to the GPL to apply to your modified version as well.
+
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
index 42a8ce025e78efb9fe05799f2428eeed4d0fd54e..148bf5826eb9e81a224566b6f20f6d855fbb77c5 100755 (executable)
@@ -4,5 +4,5 @@ include /usr/share/cdbs/1/rules/debhelper.mk
 include /usr/share/cdbs/1/rules/autoreconf.mk
 include /usr/share/cdbs/1/class/autotools.mk
 
-DEB_CONFIGURE_EXTRA_FLAGS = --with-snmp --with-xml --with-json --enable-pie
+DEB_CONFIGURE_EXTRA_FLAGS = --with-snmp --with-xml --enable-pie
 DEB_CONFIGURE_EXTRA_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system
diff --git a/m4/jansson.m4 b/m4/jansson.m4
deleted file mode 100644 (file)
index 291a177..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# lldp_CHECK_JANSSON
-#
-
-AC_DEFUN([lldp_CHECK_JANSSON], [
- if test x"$with_json" = x"auto" -o x"$with_json" = x"jansson"; then
-   PKG_CHECK_MODULES([JANSSON], [jansson >= 2], [
-     AC_SUBST([JANSSON_LIBS])
-     AC_SUBST([JANSSON_CFLAGS])
-     AC_DEFINE_UNQUOTED([USE_JSON], 1, [Define to indicate to enable JSON support])
-     AC_DEFINE_UNQUOTED([USE_JANSSON], 1, [Define to indicate to enable JSON support through jansson])
-     with_json=jansson
-   ],[
-     if test x"$with_json" = x"jansson"; then
-       AC_MSG_ERROR([*** unable to find libjansson])
-     fi
-     with_json=no
-   ])
- fi
-])
diff --git a/m4/json-c.m4 b/m4/json-c.m4
deleted file mode 100644 (file)
index bb9d647..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# lldp_CHECK_JSONC
-#
-
-AC_DEFUN([lldp_CHECK_JSONC], [
- if test x"$with_json" = x"auto" -o x"$with_json" = x"json-c"; then
-   PKG_CHECK_MODULES([JSONC], [json-c], [
-     AC_SUBST([JSONC_LIBS])
-     AC_SUBST([JSONC_CFLAGS])
-     AC_DEFINE_UNQUOTED([USE_JSON], 1, [Define to indicate to enable JSON support])
-     AC_DEFINE_UNQUOTED([USE_JSONC], 1, [Define to indicate to enable JSON via json-c support])
-     with_json=json-c
-   ],[
-     PKG_CHECK_MODULES([JSONC], [json >= 0.10], [
-       AC_SUBST([JSONC_LIBS])
-       AC_SUBST([JSONC_CFLAGS])
-       AC_DEFINE_UNQUOTED([USE_JSON], 1, [Define to indicate to enable JSON support])
-       AC_DEFINE_UNQUOTED([USE_JSONC], 1, [Define to indicate to enable JSON via json-c support])
-       with_json=json-c
-     ],[
-       if test x"$with_json" = x"json-c"; then
-         AC_MSG_ERROR([*** unable to find json-c])
-       fi
-       with_json=no
-     ])
-   ])
- fi
-])
index 062155bf5083bda85815c3ffab073bb5f9ef076c..e7b7a169b50c4ae08d8d41a475b695b82616de36 100644 (file)
 %bcond_with oldies
 %endif
 
-# On RHEL and SLES, compile without JSON support
-%if (0%{?rhel_version} > 0) || (0%{?centos_version} > 0 && 0%{?centos_version} < 600) || (0%{?suse_version} > 0)
-%bcond_with json
-%else
-%bcond_without json
-%endif
-
 # On RHEL < 7, disable systemd
 # On SuSE < 12, disable systemd
 %if (0%{?rhel_version} > 0 && 0%{?rhel_version} < 700) || (0%{?centos_version} > 0 && 0%{?centos_version} < 700) || (0%{?suse_version} > 0 && 0%{?suse_version} < 1210)
@@ -73,9 +66,6 @@ BuildRequires: openssl-devel
 %if %{with xml}
 BuildRequires: libxml2-devel
 %endif
-%if %{with json}
-BuildRequires: json-c-devel
-%endif
 %if %{with systemd}
 %if 0%{?suse_version}
 BuildRequires: systemd-rpm-macros
index eb476c00cf752c12d4d9b6a7383dd5e66cd2311f..6f7ccd4ecd7e9da9aec8753baee3aec678f6238c 100644 (file)
@@ -17,7 +17,8 @@ lldpcli_SOURCES  = client.h lldpcli.c display.c \
        conf-lldp.c conf-system.c \
        commands.c show.c \
        misc.c tokenizer.c \
-       writer.h text_writer.c kv_writer.c
+       utf8.c \
+       writer.h text_writer.c kv_writer.c json_writer.c
 lldpcli_LDADD    = \
        $(top_builddir)/src/libcommon-daemon-client.la \
        $(top_builddir)/src/lib/liblldpctl.la \
@@ -31,18 +32,6 @@ lldpcli_CFLAGS  += @XML2_CFLAGS@
 lldpcli_LDADD   += @XML2_LIBS@
 endif
 
-if USE_JANSSON
-lldpcli_SOURCES += jansson_writer.c
-lldpcli_CFLAGS  += @JANSSON_CFLAGS@
-lldpcli_LDADD   += @JANSSON_LIBS@
-endif
-
-if USE_JSONC
-lldpcli_SOURCES += jsonc_writer.c
-lldpcli_CFLAGS  += @JSONC_CFLAGS@
-lldpcli_LDADD   += @JSONC_LIBS@
-endif
-
 # Completions
 bashcompletiondir = $(datadir)/bash-completion/completions
 dist_bashcompletion_DATA = completion/lldpcli
diff --git a/src/client/jansson_writer.c b/src/client/jansson_writer.c
deleted file mode 100644 (file)
index 15d4539..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/* -*- mode: c; c-file-style: "openbsd" -*- */
-/*
- * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#if HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <jansson.h>
-#include <sys/queue.h>
-
-#include "writer.h"
-#include "../compat/compat.h"
-#include "../log.h"
-
-/* This list is used as a queue. The queue does not hold reference to the json_t
- * element except the first one. */
-struct json_element {
-       TAILQ_ENTRY(json_element) next;
-       json_t *el;
-};
-TAILQ_HEAD(json_element_list, json_element);
-struct json_writer_private {
-       FILE *fh;
-       struct json_element_list els;
-};
-
-static void
-jansson_start(struct writer *w, const char *tag, const char *descr)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       struct json_element *new;
-       json_t *exist;
-
-       /* Try to find if a similar object exists. */
-       exist = json_object_get(current->el, tag);
-       if (!exist) {
-               exist = json_array();
-               json_object_set_new(current->el, tag, exist);
-       }
-
-       /* Queue the new element. */
-       new = malloc(sizeof(*new));
-       if (new == NULL) fatal(NULL, NULL);
-       new->el = json_object();
-       json_array_append_new(exist, new->el);
-       TAILQ_INSERT_TAIL(&p->els, new, next);
-}
-
-static void
-jansson_attr(struct writer *w, const char *tag,
-    const char *descr, const char *value)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       json_t *jvalue;
-       if (value && (!strcmp(value, "yes") || !strcmp(value, "on")))
-               jvalue = json_true();
-       else if (value && (!strcmp(value, "no") || !strcmp(value, "off")))
-               jvalue = json_false();
-       else
-               jvalue = json_string(value?value:"");
-       json_object_set_new(current->el, tag, jvalue);
-}
-
-static void
-jansson_data(struct writer *w, const char *data)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       json_object_set_new(current->el, "value",
-           json_string(data?data:""));
-}
-
-/* When an array has only one member, just remove the array. When an object has
- * `value` as the only key, remove the object. Moreover, for an object, move the
- * `name` key outside (inside a new object). This is a recursive function. We
- * think the depth will be limited. */
-static json_t*
-jansson_cleanup(json_t *el)
-{
-       if (el == NULL) return NULL;
-#ifndef ENABLE_JSON0
-       json_t *new;
-       if (json_is_array(el) && json_array_size(el) == 1) {
-               new = json_array_get(el, 0);
-               return jansson_cleanup(new);
-       }
-       if (json_is_array(el)) {
-               int i = json_array_size(el);
-               new = json_array();
-               while (i > 0) {
-                       json_array_insert_new(new, 0,
-                           jansson_cleanup(json_array_get(el, --i)));
-               }
-               return new;
-       }
-       if (json_is_object(el) && json_object_size(el) == 1) {
-               new = json_object_get(el, "value");
-               if (new) {
-                       json_incref(new);
-                       return new; /* This is a string or a boolean, no need to
-                                    * cleanup */
-               }
-       }
-       if (json_is_object(el)) {
-               json_t *value;
-               json_t *name = NULL;
-               void *iter = json_object_iter(el);
-               new = json_object();
-               while (iter) {
-                       const char *key;
-                       key   = json_object_iter_key(iter);
-                       value = jansson_cleanup(json_object_iter_value(iter));
-                       if (strcmp(key, "name") || !json_is_string(value)) {
-                               json_object_set_new(new, key, value);
-                       } else {
-                               name = value;
-                       }
-                       iter  = json_object_iter_next(el, iter);
-               }
-               if (name) {
-                       /* Embed the current object into a new one with the name
-                        * as key. */
-                       new = json_pack("{s: o}", /* o: stolen reference */
-                           json_string_value(name), new);
-                       json_decref(name);
-               }
-               return new;
-       }
-#endif
-       json_incref(el);
-       return el;
-}
-
-static void
-jansson_end(struct writer *w)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       if (current == NULL) {
-               log_warnx("lldpctl", "unbalanced tags");
-               return;
-       }
-       TAILQ_REMOVE(&p->els, current, next);
-       free(current);
-
-       /* Display current object if last one */
-       if (TAILQ_NEXT(TAILQ_FIRST(&p->els), next) == NULL) {
-               struct json_element *root = TAILQ_FIRST(&p->els);
-               json_t *export = jansson_cleanup(root->el);
-               if (json_dumpf(export,
-                       p->fh,
-                       JSON_INDENT(2) | JSON_PRESERVE_ORDER) == -1)
-                       log_warnx("lldpctl", "unable to output JSON");
-               fprintf(p->fh,"\n");
-               fflush(p->fh);
-               json_decref(export);
-               json_decref(root->el);
-               root->el = json_object();
-               if (root->el == NULL)
-                       fatalx("lldpctl", "cannot create JSON root object");
-       }
-}
-
-static void
-jansson_finish(struct writer *w)
-{
-       struct json_writer_private *p = w->priv;
-       if (TAILQ_EMPTY(&p->els)) {
-               log_warnx("lldpctl", "nothing to output");
-       } else if (TAILQ_NEXT(TAILQ_FIRST(&p->els), next) != NULL) {
-               log_warnx("lldpctl", "unbalanced tags");
-               /* memory will leak... */
-       } else {
-               struct json_element *root = TAILQ_FIRST(&p->els);
-               json_decref(root->el);
-               TAILQ_REMOVE(&p->els, root, next);
-               free(root);
-       }
-       free(p);
-       free(w);
-}
-
-struct writer*
-jansson_init(FILE *fh)
-{
-       struct writer *result;
-       struct json_writer_private *priv;
-       struct json_element *root;
-
-       priv = malloc(sizeof(*priv));
-       root = malloc(sizeof(*root));
-       if (priv == NULL || root == NULL) fatal(NULL, NULL);
-
-       priv->fh = fh;
-       TAILQ_INIT(&priv->els);
-       TAILQ_INSERT_TAIL(&priv->els, root, next);
-       root->el = json_object();
-       if (root->el == NULL)
-               fatalx("lldpctl", "cannot create JSON root object");
-
-       result = malloc(sizeof(*result));
-       if (result == NULL) fatal(NULL, NULL);
-
-       result->priv   = priv;
-       result->start  = jansson_start;
-       result->attr   = jansson_attr;
-       result->data   = jansson_data;
-       result->end    = jansson_end;
-       result->finish = jansson_finish;
-
-       return result;
-}
diff --git a/src/client/json_writer.c b/src/client/json_writer.c
new file mode 100644 (file)
index 0000000..7ab6d48
--- /dev/null
@@ -0,0 +1,371 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2017 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include "writer.h"
+#include "../compat/compat.h"
+#include "../log.h"
+
+enum tag {
+       STRING,
+       BOOL,
+       ARRAY,
+       OBJECT
+};
+
+struct element {
+       struct element *parent;    /* Parent (if any) */
+       TAILQ_ENTRY(element) next; /* Sibling (if any) */
+       char *key;                 /* Key if parent is an object */
+       enum tag tag;              /* Kind of element */
+       union {
+               char *string;   /* STRING */
+               int boolean;    /* BOOL */
+               TAILQ_HEAD(, element) children; /* ARRAY or OBJECT */
+       };
+};
+
+struct json_writer_private {
+       FILE *fh;
+       struct element *root;
+       struct element *current; /* should always be an object */
+};
+
+/* Create a new element. If a parent is provided, it will also be attached to
+ * the parent. */
+static struct element*
+json_element_new(struct element *parent, const char *key, enum tag tag)
+{
+       struct element *child = malloc(sizeof(*child));
+       if (child == NULL) fatal(NULL, NULL);
+       child->parent = parent;
+       child->key = key?strdup(key):NULL;
+       child->tag = tag;
+       TAILQ_INIT(&child->children);
+       if (parent) TAILQ_INSERT_TAIL(&parent->children, child, next);
+       return child;
+}
+
+/* Free the element content (but not the element itself) */
+static void
+json_element_free(struct element *current)
+{
+       struct element *el, *el_next;
+       switch (current->tag) {
+       case STRING:
+               free(current->string);
+               break;
+       case BOOL:
+               break;
+       case ARRAY:
+       case OBJECT:
+               for (el = TAILQ_FIRST(&current->children);
+                    el != NULL;
+                    el = el_next) {
+                       el_next = TAILQ_NEXT(el, next);
+                       json_element_free(el);
+                       TAILQ_REMOVE(&current->children, el, next);
+                       if (current->tag == OBJECT) free(el->key);
+                       free(el);
+               }
+               break;
+       }
+}
+
+static void
+json_free(struct json_writer_private *p)
+{
+       json_element_free(p->root);
+       free(p->root);
+}
+
+static void
+json_string_dump(FILE *fh, const char *s)
+{
+       fprintf(fh, "\"");
+       while (*s != '\0') {
+               unsigned int c = *s;
+               size_t len;
+               switch (c) {
+               case '"': fprintf(fh, "\\\""); s++; break;
+               case '\\': fprintf(fh, "\\\\"); s++; break;
+               case '\b': fprintf(fh, "\\\b"); s++; break;
+               case '\f': fprintf(fh, "\\\f"); s++; break;
+               case '\n': fprintf(fh, "\\\n"); s++; break;
+               case '\r': fprintf(fh, "\\\r"); s++; break;
+               case '\t': fprintf(fh, "\\\t"); s++; break;
+               default:
+                       len = utf8_validate_cz(s);
+                       if (len == 0) {
+                               /* Not a valid UTF-8 char, use a
+                                * replacement character */
+                               fprintf(fh, "\\uFFFD");
+                               s++;
+                       } else if (c < 0x1f) {
+                               /* 7-bit ASCII character */
+                               fprintf(fh, "\\u%04X", c);
+                               s++;
+                       } else {
+                               /* UTF-8, write as is */
+                               while (len--) fprintf(fh, "%c", *s++);
+                       }
+                       break;
+               }
+       }
+       fprintf(fh, "\"");
+}
+
+/* Dump an element to the specified file handle. */
+static void
+json_element_dump(FILE *fh, struct element *current, int indent)
+{
+       static const char pairs[2][2] = { "{}", "[]" };
+       struct element *el;
+       switch (current->tag) {
+       case STRING:
+               json_string_dump(fh, current->string);
+               break;
+       case BOOL:
+               fprintf(fh, current->boolean?"true":"false");
+               break;
+       case ARRAY:
+       case OBJECT:
+               fprintf(fh, "%c\n%*s", pairs[(current->tag == ARRAY)][0],
+                   indent + 2, "");
+               TAILQ_FOREACH(el, &current->children, next) {
+                       if (current->tag == OBJECT)
+                               fprintf(fh, "\"%s\": ", el->key);
+                       json_element_dump(fh, el, indent + 2);
+                       if (TAILQ_NEXT(el, next))
+                               fprintf(fh, ",\n%*s", indent + 2, "");
+               }
+               fprintf(fh, "\n%*c", indent + 1,
+                   pairs[(current->tag == ARRAY)][1]);
+               break;
+       }
+}
+
+static void
+json_dump(struct json_writer_private *p)
+{
+       json_element_dump(p->fh, p->root, 0);
+       fprintf(p->fh, "\n");
+}
+
+static void
+json_start(struct writer *w, const char *tag,
+    const char *descr)
+{
+       struct json_writer_private *p = w->priv;
+       struct element *child;
+       struct element *new;
+
+       /* Look for the tag in the current object. */
+       TAILQ_FOREACH(child, &p->current->children, next) {
+               if (!strcmp(child->key, tag)) break;
+       }
+       if (!child)
+               child = json_element_new(p->current, tag, ARRAY);
+
+       /* Queue the new element. */
+       new = json_element_new(child, NULL, OBJECT);
+       p->current = new;
+}
+
+static void
+json_attr(struct writer *w, const char *tag,
+    const char *descr, const char *value)
+{
+       struct json_writer_private *p = w->priv;
+       struct element *new = json_element_new(p->current, tag, STRING);
+       if (value && (!strcmp(value, "yes") || !strcmp(value, "on"))) {
+               new->tag = BOOL;
+               new->boolean = 1;
+       } else if (value && (!strcmp(value, "no") || !strcmp(value, "off"))) {
+               new->tag = BOOL;
+               new->boolean = 0;
+       } else {
+               new->string = strdup(value?value:"");
+       }
+}
+
+static void
+json_data(struct writer *w, const char *data)
+{
+       struct json_writer_private *p = w->priv;
+       struct element *new = json_element_new(p->current, "value", STRING);
+       new->string = strdup(data?data:"");
+}
+
+/* When an array has only one member, just remove the array. When an object has
+ * `value` as the only key, remove the object. Moreover, for an object, move the
+ * `name` key outside (inside a new object). This is a recursive function. We
+ * think the depth will be limited. Also, the provided element can be
+ * destroyed. Don't use it after this function!
+ *
+ * During the cleaning process, we will generate array of 1-size objects that
+ * could be turned into an object. We don't do that since people may rely on
+ * this format. Another problem is the format is changing depending on the
+ * number of interfaces or the number of neighbors.
+ */
+static void
+json_element_cleanup(struct element *el)
+{
+#ifndef ENABLE_JSON0
+       struct element *child, *child_next;
+
+       /* If array with one element, steal the content. Object with only one
+        * value whose key is "value", steal the content. */
+       if ((el->tag == ARRAY || el->tag == OBJECT) &&
+           (child = TAILQ_FIRST(&el->children)) &&
+           !TAILQ_NEXT(child, next) &&
+           (el->tag == ARRAY || !strcmp(child->key, "value"))) {
+               free(child->key);
+               child->key = el->key;
+               child->parent = el->parent;
+               TAILQ_INSERT_BEFORE(el, child, next);
+               TAILQ_REMOVE(&el->parent->children, el, next);
+               free(el);
+               json_element_cleanup(child);
+               return;
+       }
+
+       /* Other kind of arrays, recursively clean */
+       if (el->tag == ARRAY) {
+               for (child = TAILQ_FIRST(&el->children);
+                    child;
+                    child = child_next) {
+                       child_next = TAILQ_NEXT(child, next);
+                       json_element_cleanup(child);
+               }
+               return;
+       }
+
+       /* Other kind of objects, recursively clean, but if one key is "name",
+        * use it's value as a key for a new object stealing the existing
+        * one. */
+       if (el->tag == OBJECT) {
+               struct element *name_child = NULL;
+               for (child = TAILQ_FIRST(&el->children);
+                    child;
+                    child = child_next) {
+                       child_next = TAILQ_NEXT(child, next);
+                       json_element_cleanup(child);
+               }
+               /* Redo a check to find if we have a "name" key now */
+               for (child = TAILQ_FIRST(&el->children);
+                    child;
+                    child = child_next) {
+                       child_next = TAILQ_NEXT(child, next);
+                       if (!strcmp(child->key, "name") &&
+                           child->tag == STRING) {
+                               name_child = child;
+                       }
+               }
+               if (name_child) {
+                       struct element *new_el = json_element_new(NULL, NULL, OBJECT);
+                       /* Replace el by new_el in parent object/array */
+                       new_el->parent = el->parent;
+                       TAILQ_INSERT_BEFORE(el, new_el, next);
+                       TAILQ_REMOVE(&el->parent->children, el, next);
+                       new_el->key = el->key;
+
+                       /* new_el is parent of el */
+                       el->parent = new_el;
+                       el->key = name_child->string; /* stolen */
+                       TAILQ_INSERT_TAIL(&new_el->children, el, next);
+
+                       /* Remove "name" child */
+                       TAILQ_REMOVE(&el->children, name_child, next);
+                       free(name_child->key);
+                       free(name_child);
+               }
+               return;
+       }
+#endif
+}
+
+static void
+json_cleanup(struct json_writer_private *p)
+{
+       json_element_cleanup(p->root);
+}
+
+static void
+json_end(struct writer *w)
+{
+       struct json_writer_private *p = w->priv;
+       while ((p->current = p->current->parent) != NULL && p->current->tag != OBJECT);
+       if (p->current == NULL) {
+               fatalx("lldpctl", "unbalanced tags");
+               return;
+       }
+
+       /* Display current object if last one */
+       if (p->current == p->root) {
+               json_cleanup(p);
+               json_dump(p);
+               json_free(p);
+               fprintf(p->fh,"\n");
+               fflush(p->fh);
+               p->root = p->current = json_element_new(NULL, NULL, OBJECT);
+       }
+}
+
+static void
+json_finish(struct writer *w)
+{
+       struct json_writer_private *p = w->priv;
+       if (p->current != p->root)
+               log_warnx("lldpctl", "unbalanced tags");
+       json_free(p);
+       free(p);
+       free(w);
+}
+
+struct writer*
+json_init(FILE *fh)
+{
+       struct writer *result;
+       struct json_writer_private *priv;
+
+       priv = malloc(sizeof(*priv));
+       if (priv == NULL) fatal(NULL, NULL);
+
+       priv->fh = fh;
+       priv->root = priv->current = json_element_new(NULL, NULL, OBJECT);
+
+       result = malloc(sizeof(*result));
+       if (result == NULL) fatal(NULL, NULL);
+
+       result->priv   = priv;
+       result->start  = json_start;
+       result->attr   = json_attr;
+       result->data   = json_data;
+       result->end    = json_end;
+       result->finish = json_finish;
+
+       return result;
+}
diff --git a/src/client/jsonc_writer.c b/src/client/jsonc_writer.c
deleted file mode 100644 (file)
index 6e67aad..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-/* -*- mode: c; c-file-style: "openbsd" -*- */
-/*
- * Copyright (c) 2014 Michel Stam <michel@reverze.net>,
- *  Vincent Bernat <bernat@luffy.cx>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#if HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <json.h>
-#include <sys/queue.h>
-
-#include "writer.h"
-#include "../compat/compat.h"
-#include "../log.h"
-
-/* This list is used as a queue. The queue does not hold reference to the json_t
- * element except the first one. */
-struct json_element {
-       TAILQ_ENTRY(json_element) next;
-       json_object *el;
-};
-TAILQ_HEAD(json_element_list, json_element);
-struct json_writer_private {
-       FILE *fh;
-       struct json_element_list els;
-};
-
-static void
-jsonc_start(struct writer *w, const char *tag, const char *descr)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       struct json_element *new;
-       json_object *exist = NULL;
-
-    if (!json_object_object_get_ex(current->el, tag, &exist)) {
-               exist = json_object_new_array();
-               json_object_object_add(current->el, tag, exist);
-    }
-
-       /* Queue the new element. */
-       new = malloc(sizeof(*new));
-       if (new == NULL) fatal(NULL, NULL);
-       new->el = json_object_new_object();
-       json_object_array_add(exist, new->el);
-       TAILQ_INSERT_TAIL(&p->els, new, next);
-}
-
-static void
-jsonc_attr(struct writer *w, const char *tag,
-    const char *descr, const char *value)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       json_object *jvalue;
-       if (value && (!strcmp(value, "yes") || !strcmp(value, "on")))
-               jvalue = json_object_new_boolean(1);
-       else if (value && (!strcmp(value, "no") || !strcmp(value, "off")))
-               jvalue = json_object_new_boolean(0);
-       else
-               jvalue = json_object_new_string(value?value:"");
-       json_object_object_add(current->el, tag, jvalue);
-}
-
-static void
-jsonc_data(struct writer *w, const char *data)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       json_object_object_add(current->el, "value",
-           json_object_new_string(data?data:""));
-}
-
-/* When an array has only one member, just remove the array. When an object has
- * `value` as the only key, remove the object. Moreover, for an object, move the
- * `name` key outside (inside a new object). This is a recursive function. We
- * think the depth will be limited. */
-static json_object*
-jsonc_cleanup(json_object *el)
-{
-       if (el == NULL) return NULL;
-#ifndef ENABLE_JSON0
-       json_object *new;
-       if (json_object_get_type(el) == json_type_array) {
-               size_t len = json_object_array_length(el);
-               if (len == 1) {
-                       new = json_object_array_get_idx(el, 0);
-                       return jsonc_cleanup(new);
-               }
-               new = json_object_new_array();
-               for (size_t i = 0; i < len; i++) {
-                       json_object_array_add(new,
-                           jsonc_cleanup(json_object_array_get_idx(el, i)));
-               }
-               return new;
-       }
-       if (json_object_get_type(el) == json_type_object) {
-               if (json_object_object_length(el) == 1 &&
-                   json_object_object_get_ex(el, "value", &new)) {
-                       json_object_get(new);
-                       return new; /* This is a string or a boolean, no need to
-                                    * cleanup */
-               }
-
-               json_object *name = NULL;
-               new = json_object_new_object();
-               json_object_object_foreach(el, key, value) {
-                       value = jsonc_cleanup(value);
-                       if (strcmp(key, "name") ||
-                           json_object_get_type(value) != json_type_string) {
-                               json_object_object_add(new, key, value);
-                       } else {
-                               name = value;
-                       }
-               }
-               if (name) {
-                       /* Embed the current object into a new one with the name
-                        * as key. */
-                       json_object *replacement = json_object_new_object();
-                       json_object_object_add(replacement,
-                           json_object_get_string(name), new);
-                       json_object_put(name);
-                       return replacement;
-               }
-               return new;
-       }
-#endif
-       json_object_get(el);
-       return el;
-}
-
-static void
-jsonc_end(struct writer *w)
-{
-       struct json_writer_private *p = w->priv;
-       struct json_element *current = TAILQ_LAST(&p->els, json_element_list);
-       if (current == NULL) {
-               log_warnx("lldpctl", "unbalanced tags");
-               return;
-       }
-       TAILQ_REMOVE(&p->els, current, next);
-       free(current);
-
-       /* Display current object if last one */
-       if (TAILQ_NEXT(TAILQ_FIRST(&p->els), next) == NULL) {
-               struct json_element *root = TAILQ_FIRST(&p->els);
-               json_object *export = jsonc_cleanup(root->el);
-               int json_flags = (JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED);
-               const char *s = json_object_to_json_string_ext(export, json_flags);
-               fprintf(p->fh, "%s\n", s?s:"{}");
-               fflush(p->fh);
-               json_object_put(export);
-               json_object_put(root->el);
-               root->el = json_object_new_object();
-               if (root->el == NULL)
-                       fatalx("lldpctl", "cannot create JSON root object");
-       }
-}
-
-static void
-jsonc_finish(struct writer *w)
-{
-       struct json_writer_private *p = w->priv;
-       if (TAILQ_EMPTY(&p->els)) {
-               log_warnx("lldpctl", "nothing to output");
-       } else if (TAILQ_NEXT(TAILQ_FIRST(&p->els), next) != NULL) {
-               log_warnx("lldpctl", "unbalanced tags");
-               /* memory will leak... */
-       } else {
-               struct json_element *root = TAILQ_FIRST(&p->els);
-               json_object_put(root->el);
-               TAILQ_REMOVE(&p->els, root, next);
-               free(root);
-       }
-       free(p);
-       free(w);
-}
-
-struct writer*
-jsonc_init(FILE *fh)
-{
-       struct writer *result;
-       struct json_writer_private *priv;
-       struct json_element *root;
-
-       priv = malloc(sizeof(*priv));
-       root = malloc(sizeof(*root));
-       if (priv == NULL || root == NULL) fatal(NULL, NULL);
-
-       priv->fh = fh;
-       TAILQ_INIT(&priv->els);
-       TAILQ_INSERT_TAIL(&priv->els, root, next);
-       root->el = json_object_new_object();
-       if (root->el == NULL)
-               fatalx("lldpctl", "cannot create JSON root object");
-
-       result = malloc(sizeof(*result));
-       if (result == NULL) fatal(NULL, NULL);
-
-       result->priv   = priv;
-       result->start  = jsonc_start;
-       result->attr   = jsonc_attr;
-       result->data   = jsonc_data;
-       result->end    = jsonc_end;
-       result->finish = jsonc_finish;
-
-       return result;
-}
index 9f1380a40be22d9ad58050ef550ad6c50a94f0ab..7263483c53ce39ad36b8bc92820898a740edeec8 100644 (file)
@@ -68,12 +68,9 @@ usage()
 
        fprintf(stderr, "-d          Enable more debugging information.\n");
        fprintf(stderr, "-u socket   Specify the Unix-domain socket used for communication with lldpd(8).\n");
-       fprintf(stderr, "-f format   Choose output format (plain, keyvalue"
+       fprintf(stderr, "-f format   Choose output format (plain, keyvalue, json"
 #if defined USE_XML
            ", xml"
-#endif
-#if defined USE_JANSSON || defined USE_JSONC
-           ", json"
 #endif
            ").\n");
        if (!is_lldpctl(NULL))
@@ -275,14 +272,9 @@ cmd_exec(lldpctl_conn_t *conn, const char *fmt, int argc, const char **argv)
 
        if      (strcmp(fmt, "plain")    == 0) w = txt_init(stdout);
        else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
+       else if (strcmp(fmt, "json")     == 0) w = json_init(stdout);
 #ifdef USE_XML
        else if (strcmp(fmt, "xml")      == 0) w = xml_init(stdout);
-#endif
-#ifdef USE_JANSSON
-       else if (strcmp(fmt, "json")     == 0) w = jansson_init(stdout);
-#endif
-#ifdef USE_JSONC
-       else if (strcmp(fmt, "json")     == 0) w = jsonc_init(stdout);
 #endif
        else {
                log_warnx("lldpctl", "unknown output format \"%s\"", fmt);
diff --git a/src/client/utf8.c b/src/client/utf8.c
new file mode 100644 (file)
index 0000000..2107639
--- /dev/null
@@ -0,0 +1,99 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+  Copyright (c) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include <stddef.h>
+
+/*
+ * Validate a single UTF-8 character starting at @s.
+ * The string must be null-terminated.
+ *
+ * If it's valid, return its length (1 thru 4).
+ * If it's invalid or clipped, return 0.
+ *
+ * This function implements the syntax given in RFC3629, which is
+ * the same as that given in The Unicode Standard, Version 6.0.
+ *
+ * It has the following properties:
+ *
+ *  * All codepoints U+0000..U+10FFFF may be encoded,
+ *    except for U+D800..U+DFFF, which are reserved
+ *    for UTF-16 surrogate pair encoding.
+ *  * UTF-8 byte sequences longer than 4 bytes are not permitted,
+ *    as they exceed the range of Unicode.
+ *  * The sixty-six Unicode "non-characters" are permitted
+ *    (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF).
+ */
+size_t
+utf8_validate_cz(const char *s)
+{
+        unsigned char c = *s++;
+
+        if (c <= 0x7F) {        /* 00..7F */
+                return 1;
+        } else if (c <= 0xC1) { /* 80..C1 */
+                /* Disallow overlong 2-byte sequence. */
+                return 0;
+        } else if (c <= 0xDF) { /* C2..DF */
+                /* Make sure subsequent byte is in the range 0x80..0xBF. */
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+
+                return 2;
+        } else if (c <= 0xEF) { /* E0..EF */
+                /* Disallow overlong 3-byte sequence. */
+                if (c == 0xE0 && (unsigned char)*s < 0xA0)
+                        return 0;
+
+                /* Disallow U+D800..U+DFFF. */
+                if (c == 0xED && (unsigned char)*s > 0x9F)
+                        return 0;
+
+                /* Make sure subsequent bytes are in the range 0x80..0xBF. */
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+
+                return 3;
+        } else if (c <= 0xF4) { /* F0..F4 */
+                /* Disallow overlong 4-byte sequence. */
+                if (c == 0xF0 && (unsigned char)*s < 0x90)
+                        return 0;
+
+                /* Disallow codepoints beyond U+10FFFF. */
+                if (c == 0xF4 && (unsigned char)*s > 0x8F)
+                        return 0;
+
+                /* Make sure subsequent bytes are in the range 0x80..0xBF. */
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+                if (((unsigned char)*s++ & 0xC0) != 0x80)
+                        return 0;
+
+                return 4;
+        } else {                /* F5..FF */
+                return 0;
+        }
+}
index 1a76d94b021e5e837b213db2f253a569a8447350..be9f96a6d1ca80c601aaa98d192e790b1256e9d9 100644 (file)
@@ -37,15 +37,13 @@ struct writer {
 
 extern struct writer *txt_init(FILE *);
 extern struct writer *kv_init(FILE *);
+extern struct writer *json_init(FILE *);
 
 #ifdef USE_XML
 extern struct writer *xml_init(FILE *);
 #endif
-#ifdef USE_JANSSON
-extern struct writer *jansson_init(FILE *);
-#endif
-#ifdef USE_JSONC
-extern struct writer *jsonc_init(FILE *);
-#endif
+
+/* utf8.c */
+size_t utf8_validate_cz(const char *s);
 
 #endif /* _WRITER_H */
index 9f835b9e5cde83ddb7b6cffb1cdc9ed75a1fee88..4b88999e99e548044598fe688c1a3847dde9329d 100644 (file)
@@ -72,11 +72,11 @@ version_display(FILE *destination, const char *progname, int verbose)
 #endif
                NULL};
        const char *const output_formats[] = {
+               "TEXT",
+               "KV",
+               "JSON",
 #ifdef USE_XML
                "XML",
-#endif
-#ifdef USE_JSON
-               "JSON",
 #endif
                NULL};
 
index f6f9cf8ba16f4a7a87213a957fdd873fce33483e..3da09c6f1f39ee6f9f1335ba58061d88e7f54a9b 100755 (executable)
@@ -17,7 +17,7 @@ case "$(uname -s)" in
         sudo apt-get -qqy update
         sudo apt-get -qqy install \
             automake autoconf libtool pkg-config \
-            libsnmp-dev libxml2-dev libjansson-dev libjson-c-dev \
+            libsnmp-dev libxml2-dev \
             libevent-dev libreadline-dev libbsd-dev \
             check libc6-dbg libevent-dbg libseccomp-dev \
             libpcap-dev