Introduction
Introduction Statistics Contact Development Disclaimer Help
initial repo - jfconvert - JSON Feed (subset) to sfeed or Atom converter
git clone git://git.codemadness.org/jfconvert
Log
Files
Refs
README
LICENSE
---
commit f7cde52eef12a6e77c28199a678de8665836e9e6
Author: Hiltjo Posthuma <[email protected]>
Date: Mon, 3 Apr 2023 18:20:19 +0200
initial repo
Diffstat:
A LICENSE | 15 +++++++++++++++
A Makefile | 88 +++++++++++++++++++++++++++++…
A README | 46 +++++++++++++++++++++++++++++…
A jf2atom.1 | 39 +++++++++++++++++++++++++++++…
A jf2atom.c | 267 +++++++++++++++++++++++++++++…
A json.c | 319 +++++++++++++++++++++++++++++…
A json.h | 30 ++++++++++++++++++++++++++++++
7 files changed, 804 insertions(+), 0 deletions(-)
---
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2023 Hiltjo Posthuma <[email protected]>
+
+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.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,88 @@
+.POSIX:
+
+NAME = jf2atom
+VERSION = 0.1
+
+# paths
+PREFIX = /usr/local
+MANPREFIX = ${PREFIX}/man
+DOCPREFIX = ${PREFIX}/share/doc/${NAME}
+
+RANLIB = ranlib
+
+# use system flags.
+JFA_CFLAGS = ${CFLAGS}
+JFA_LDFLAGS = ${LDFLAGS}
+JFA_CPPFLAGS = -D_DEFAULT_SOURCE
+
+# uncomment for conservative locked I/O.
+#JFA_CPPFLAGS = -D_DEFAULT_SOURCE -DGETNEXT=getchar
+
+BIN = ${NAME}
+SRC = ${BIN:=.c}
+HDR = json.h
+MAN1 = ${BIN:=.1}
+DOC = \
+ LICENSE\
+ README
+
+LIBJSON = libjson.a
+LIBJSONSRC = json.c
+LIBJSONOBJ = ${LIBJSONSRC:.c=.o}
+
+LIB = ${LIBJSON}
+
+all: ${BIN}
+
+${BIN}: ${LIB} ${@:=.o}
+
+OBJ = ${SRC:.c=.o} ${LIBJSONOBJ}
+
+${OBJ}: ${HDR}
+
+.o:
+ ${CC} ${JFA_LDFLAGS} -o $@ $< ${LIB}
+
+.c.o:
+ ${CC} ${JFA_CFLAGS} ${JFA_CPPFLAGS} -o $@ -c $<
+
+${LIBJSON}: ${LIBJSONOBJ}
+ ${AR} -rc $@ $?
+ ${RANLIB} $@
+
+dist:
+ rm -rf "${NAME}-${VERSION}"
+ mkdir -p "${NAME}-${VERSION}"
+ cp -f ${MAN1} ${DOC} ${HDR} \
+ ${SRC} ${LIBJSONSRC} Makefile "${NAME}-${VERSION}"
+ # make tarball
+ tar cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz"
+ rm -rf "${NAME}-${VERSION}"
+
+clean:
+ rm -f ${BIN} ${OBJ} ${LIB}
+
+install: all
+ # installing executable files.
+ mkdir -p "${DESTDIR}${PREFIX}/bin"
+ cp -f ${BIN} "${DESTDIR}${PREFIX}/bin"
+ for f in ${BIN}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done
+ # installing example files.
+ mkdir -p "${DESTDIR}${DOCPREFIX}"
+ cp -f ${DOC} "${DESTDIR}${DOCPREFIX}"
+ for d in ${DOC}; do chmod 644 "${DESTDIR}${DOCPREFIX}/$$d"; done
+ # installing manual pages for general commands: section 1.
+ mkdir -p "${DESTDIR}${MANPREFIX}/man1"
+ cp -f ${MAN1} "${DESTDIR}${MANPREFIX}/man1"
+ for m in ${MAN1}; do chmod 644 "${DESTDIR}${MANPREFIX}/man1/$$m"; done
+
+uninstall:
+ # removing executable files.
+ for f in ${BIN}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done
+ # removing example files.
+ for d in ${DOC}; do rm -f "${DESTDIR}${DOCPREFIX}/$$d"; done
+ -rmdir "${DESTDIR}${DOCPREFIX}"
+ # removing manual pages.
+ for m in ${MAN1}; do rm -f "${DESTDIR}${MANPREFIX}/man1/$$m"; done
+
+.PHONY: all clean dist install uninstall
diff --git a/README b/README
@@ -0,0 +1,46 @@
+jf2atom
+-------
+
+JSON Feed (subset) to Atom converter.
+
+JSON Feed specification: https://www.jsonfeed.org/version/1/
+Atom specification: https://datatracker.ietf.org/doc/html/rfc4287
+
+
+Build and install
+-----------------
+
+$ make
+# make install
+
+
+Dependencies
+------------
+
+- C compiler (C99).
+- libc
+
+
+Optional dependencies
+---------------------
+
+- POSIX make(1) (for Makefile).
+- mandoc for documentation: https://mdocml.bsd.lv/
+
+
+Examples and documentation
+--------------------------
+
+See the man page.
+
+
+License
+-------
+
+ISC, see LICENSE file.
+
+
+Author
+------
+
+Hiltjo Posthuma <[email protected]>
diff --git a/jf2atom.1 b/jf2atom.1
@@ -0,0 +1,39 @@
+.Dd April 3, 2023
+.Dt JF2ATOM 1
+.Os
+.Sh NAME
+.Nm jf2atom
+.Nd convert JSON Feed to Atom
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+reads JSON data from stdin.
+It writes an Atom feed to stdout.
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+.Bd -literal
+jf2atom < input.json
+.Ed
+.Pp
+An example to support JSON Feed in a RSS/Atom reader:
+.Bd -literal
+curl -s 'https://codemadness.org/jsonfeed_content.json' | jf2atom | sfeed | sf…
+.Ed
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr curl 1 ,
+.Xr sfeed 1
+.Sh STANDARDS
+.Rs
+.%T The Atom Syndication Format
+.%R RFC 4287
+.Re
+.Rs
+.%T JSON Feed Version 1.1
+.%U https://www.jsonfeed.org/version/1.1/
+.%D Nov, 2022
+.Re
+.Sh AUTHORS
+.An Hiltjo Posthuma Aq Mt [email protected]
diff --git a/jf2atom.c b/jf2atom.c
@@ -0,0 +1,267 @@
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __OpenBSD__
+#include <unistd.h>
+#else
+#define pledge(a,b) 0
+#endif
+
+#include "json.h"
+
+/* control-character in the ASCII range 0-127: compatible with UTF-8 */
+#define ISCNTRL(c) ((c) < ' ' || (c) == 0x7f)
+
+static int itemisopen = 0, enclosureisopen = 0;
+
+/* Escape characters below as HTML 2.0 / XML 1.0. */
+void
+xmlencode(const char *s, FILE *fp)
+{
+ for (; *s; ++s) {
+ switch (*s) {
+ case '<': fputs("&lt;", fp); break;
+ case '>': fputs("&gt;", fp); break;
+ case '\'': fputs("&#39;", fp); break;
+ case '&': fputs("&amp;", fp); break;
+ case '"': fputs("&quot;", fp); break;
+ default: putc(*s, fp);
+ }
+ }
+}
+
+void
+processnode(struct json_node *nodes, size_t depth, const char *value)
+{
+ const char *outtag, *outtype, *outhref;
+
+ /* feed / channel */
+ if (depth == 2) {
+ if (nodes[0].type == JSON_TYPE_OBJECT) {
+ if (nodes[1].type == JSON_TYPE_STRING) {
+ if (!strcasecmp(nodes[1].name, "title")) {
+ fputs("<title type=\"text\">", stdout);
+ xmlencode(value, stdout);
+ fputs("</title>\n", stdout);
+ } else if (!strcasecmp(nodes[1].name, "home_pa…
+ fputs("<link rel=\"alternate\" type=\"…
+ xmlencode(value, stdout);
+ fputs("\" />\n", stdout);
+ } else if (!strcasecmp(nodes[1].name, "descrip…
+ fputs("<subtitle>", stdout);
+ xmlencode(value, stdout);
+ fputs("</subtitle>\n", stdout);
+ }
+ }
+ }
+ }
+
+ /* item */
+ if (depth == 3) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ !strcasecmp(nodes[1].name, "items")) {
+ if (enclosureisopen) {
+ fputs(" />\n", stdout);
+ enclosureisopen = 0;
+ }
+ if (itemisopen)
+ fputs("</entry>\n", stdout);
+ fputs("<entry>\n", stdout);
+ itemisopen = 1;
+ }
+ }
+
+ /* item attributes */
+ if (depth == 4) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ !strcasecmp(nodes[1].name, "items")) {
+ outtag = NULL;
+ outtype = NULL;
+ outhref = NULL;
+
+ if (!strcasecmp(nodes[3].name, "content_html")) {
+ outtag = "content";
+ outtype = "html";
+ } else if (!strcasecmp(nodes[3].name, "content_text"))…
+ outtag = "content";
+ outtype = "text";
+ } else if (!strcasecmp(nodes[3].name, "date_published"…
+ outtag = "published";
+ } else if (!strcasecmp(nodes[3].name, "date_modified")…
+ outtag = "updated";
+ } else if (!strcasecmp(nodes[3].name, "id")) {
+ outtag = "id";
+ } else if (!strcasecmp(nodes[3].name, "summary")) {
+ outtag = "summary";
+ } else if (!strcasecmp(nodes[3].name, "title")) {
+ outtag = "title";
+ } else if (!strcasecmp(nodes[3].name, "url")) {
+ outtag = "link";
+ outhref = value;
+ value = NULL;
+ }
+
+ if (outtag) {
+ fputs("\t<", stdout);
+ fputs(outtag, stdout);
+ if (outhref) {
+ fputs(" href=\"", stdout);
+ xmlencode(outhref, stdout);
+ fputs("\"", stdout);
+ }
+ if (outtype) {
+ fputs(" type=\"", stdout);
+ xmlencode(outtype, stdout);
+ fputs("\"", stdout);
+ }
+ fputs(">", stdout);
+ if (value)
+ xmlencode(value, stdout);
+ fputs("</", stdout);
+ fputs(outtag, stdout);
+ fputs(">\n", stdout);
+ }
+ }
+ }
+
+ /* 1.0 author name */
+ if (depth == 5) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ nodes[3].type == JSON_TYPE_OBJECT &&
+ nodes[4].type == JSON_TYPE_STRING &&
+ !strcasecmp(nodes[1].name, "items") &&
+ !strcasecmp(nodes[3].name, "author") &&
+ !strcasecmp(nodes[4].name, "name")) {
+ fputs("\t<author><name>", stdout);
+ xmlencode(value, stdout);
+ fputs("</name></author>\n", stdout);
+ }
+ }
+
+ /* 1.1 author name */
+ if (depth == 6) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ nodes[3].type == JSON_TYPE_ARRAY &&
+ nodes[4].type == JSON_TYPE_OBJECT &&
+ nodes[5].type == JSON_TYPE_STRING &&
+ !strcasecmp(nodes[1].name, "items") &&
+ !strcasecmp(nodes[3].name, "authors") &&
+ !strcasecmp(nodes[5].name, "name")) {
+ fputs("\t<author><name>", stdout);
+ xmlencode(value, stdout);
+ fputs("</name></author>\n", stdout);
+ }
+ }
+
+ /* tags / categories */
+ if (depth == 5) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ nodes[3].type == JSON_TYPE_ARRAY &&
+ nodes[4].type == JSON_TYPE_STRING &&
+ !strcasecmp(nodes[1].name, "items") &&
+ !strcasecmp(nodes[3].name, "tags")) {
+ fputs("\t<category term=\"", stdout);
+ xmlencode(value, stdout);
+ fputs("\" />\n", stdout);
+ }
+ }
+
+ /* enclosure */
+ if (depth == 5) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ nodes[3].type == JSON_TYPE_ARRAY &&
+ nodes[4].type == JSON_TYPE_OBJECT &&
+ !strcasecmp(nodes[1].name, "items") &&
+ !strcasecmp(nodes[3].name, "attachments")) {
+ if (enclosureisopen)
+ fputs(" />\n", stdout);
+ fputs("\t<link rel=\"enclosure\"", stdout);
+ enclosureisopen = 1;
+ }
+ }
+
+ /* enclosure attributes */
+ if (depth == 6) {
+ if (nodes[0].type == JSON_TYPE_OBJECT &&
+ nodes[1].type == JSON_TYPE_ARRAY &&
+ nodes[2].type == JSON_TYPE_OBJECT &&
+ nodes[3].type == JSON_TYPE_ARRAY &&
+ nodes[4].type == JSON_TYPE_OBJECT &&
+ (nodes[5].type == JSON_TYPE_STRING || nodes[5].type == JSO…
+ !strcasecmp(nodes[1].name, "items") &&
+ !strcasecmp(nodes[3].name, "attachments")) {
+ if (!strcasecmp(nodes[5].name, "url")) {
+ fputs(" href=\"", stdout);
+ xmlencode(value, stdout);
+ fputs("\"", stdout);
+ } else if (!strcasecmp(nodes[5].name, "mime_type")) {
+ fputs(" type=\"", stdout);
+ xmlencode(value, stdout);
+ fputs("\"", stdout);
+ } else if (!strcasecmp(nodes[5].name, "size_in_bytes")…
+ fputs(" length=\"", stdout);
+ xmlencode(value, stdout);
+ fputs("\"", stdout);
+ }
+ }
+ }
+
+ if (ferror(stdout)) {
+ fprintf(stderr, "write error: <stdout>\n");
+ exit(2);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (pledge("stdio", NULL) == -1) {
+ fprintf(stderr, "pledge stdio: %s\n", strerror(errno));
+ return 1;
+ }
+
+ fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<feed xmlns=\"http://www.w3.org/2005/Atom\" xml:lang=\"en\">\n"…
+
+ switch (parsejson(processnode)) {
+ case JSON_ERROR_MEM:
+ fputs("error: cannot allocate enough memory\n", stderr);
+ return 2;
+ case JSON_ERROR_INVALID:
+ fputs("error: invalid JSON\n", stderr);
+ return 1;
+ }
+
+ if (enclosureisopen)
+ fputs(" />\n", stdout);
+ if (itemisopen)
+ fputs("</entry>\n", stdout);
+ fputs("</feed>\n", stdout);
+
+ if (ferror(stdin)) {
+ fprintf(stderr, "read error: <stdin>\n");
+ return 2;
+ }
+ if (fflush(stdout) || ferror(stdout)) {
+ fprintf(stderr, "write error: <stdout>\n");
+ return 2;
+ }
+
+ return 0;
+}
diff --git a/json.c b/json.c
@@ -0,0 +1,319 @@
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef GETNEXT
+#define GETNEXT getchar_unlocked
+#endif
+
+#include "json.h"
+
+#define ISDIGIT(c) (((unsigned)c) - '0' < 10)
+#define ISXDIGIT(c) ((((unsigned)c) - '0' < 10) || ((unsigned)c | 32) - 'a' < …
+
+static int
+codepointtoutf8(long r, char *s)
+{
+ if (r == 0) {
+ return 0; /* NUL byte */
+ } else if (r <= 0x7F) {
+ /* 1 byte: 0aaaaaaa */
+ s[0] = r;
+ return 1;
+ } else if (r <= 0x07FF) {
+ /* 2 bytes: 00000aaa aabbbbbb */
+ s[0] = 0xC0 | ((r & 0x0007C0) >> 6); /* 110aaaaa */
+ s[1] = 0x80 | (r & 0x00003F); /* 10bbbbbb */
+ return 2;
+ } else if (r <= 0xFFFF) {
+ /* 3 bytes: aaaabbbb bbcccccc */
+ s[0] = 0xE0 | ((r & 0x00F000) >> 12); /* 1110aaaa */
+ s[1] = 0x80 | ((r & 0x000FC0) >> 6); /* 10bbbbbb */
+ s[2] = 0x80 | (r & 0x00003F); /* 10cccccc */
+ return 3;
+ } else {
+ /* 4 bytes: 000aaabb bbbbcccc ccdddddd */
+ s[0] = 0xF0 | ((r & 0x1C0000) >> 18); /* 11110aaa */
+ s[1] = 0x80 | ((r & 0x03F000) >> 12); /* 10bbbbbb */
+ s[2] = 0x80 | ((r & 0x000FC0) >> 6); /* 10cccccc */
+ s[3] = 0x80 | (r & 0x00003F); /* 10dddddd */
+ return 4;
+ }
+}
+
+static int
+hexdigit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return 10 + (c - 'a');
+ else if (c >= 'A' && c <= 'F')
+ return 10 + (c - 'A');
+ return 0;
+}
+
+static int
+capacity(char **value, size_t *sz, size_t cur, size_t inc)
+{
+ size_t need, newsiz;
+ char *newp;
+
+ /* check for addition overflow */
+ if (cur > SIZE_MAX - inc) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ need = cur + inc;
+
+ if (need > *sz) {
+ if (need > SIZE_MAX / 2) {
+ newsiz = SIZE_MAX;
+ } else {
+ for (newsiz = *sz < 64 ? 64 : *sz; newsiz <= need; new…
+ ;
+ }
+ if (!(newp = realloc(*value, newsiz)))
+ return -1; /* up to caller to free *value */
+ *value = newp;
+ *sz = newsiz;
+ }
+ return 0;
+}
+
+#define EXPECT_VALUE "{[\"-0123456789tfn"
+#define EXPECT_STRING "\""
+#define EXPECT_END "}],"
+#define EXPECT_OBJECT_STRING EXPECT_STRING "}"
+#define EXPECT_OBJECT_KEY ":"
+#define EXPECT_ARRAY_VALUE EXPECT_VALUE "]"
+
+#define JSON_INVALID() do { ret = JSON_ERROR_INVALID; goto end; } while …
+
+int
+parsejson(void (*cb)(struct json_node *, size_t, const char *))
+{
+ struct json_node nodes[JSON_MAX_NODE_DEPTH] = { { 0 } };
+ size_t depth = 0, p = 0, len, sz = 0;
+ long cp, hi, lo;
+ char pri[128], *str = NULL;
+ int c, i, escape, iskey = 0, ret = JSON_ERROR_MEM;
+ const char *expect = EXPECT_VALUE;
+
+ if (capacity(&(nodes[0].name), &(nodes[0].namesiz), 0, 1) == -1)
+ goto end;
+ nodes[0].name[0] = '\0';
+
+ while (1) {
+ c = GETNEXT();
+handlechr:
+ if (c == EOF)
+ break;
+
+ /* skip JSON white-space, (NOTE: no \v, \f, \b etc) */
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ continue;
+
+ if (!c || !strchr(expect, c))
+ JSON_INVALID();
+
+ switch (c) {
+ case ':':
+ iskey = 0;
+ expect = EXPECT_VALUE;
+ break;
+ case '"':
+ nodes[depth].type = JSON_TYPE_STRING;
+ escape = 0;
+ len = 0;
+ while (1) {
+ c = GETNEXT();
+chr:
+ /* EOF or control char: 0x7f is not defined as…
+ if (c < 0x20)
+ JSON_INVALID();
+
+ if (escape) {
+escchr:
+ escape = 0;
+ switch (c) {
+ case '"': /* FALLTHROUGH */
+ case '\\':
+ case '/': break;
+ case 'b': c = '\b'; break;
+ case 'f': c = '\f'; break;
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 't': c = '\t'; break;
+ case 'u': /* hex hex hex hex */
+ if (capacity(&str, &sz, len, 4…
+ goto end;
+ for (i = 12, cp = 0; i >= 0; i…
+ if ((c = GETNEXT()) ==…
+ JSON_INVALID()…
+ cp |= (hexdigit(c) << …
+ }
+ /* RFC8259 - 7. Strings - surr…
+ * 0xd800 - 0xdbff - high surr…
+ if (cp >= 0xd800 && cp <= 0xdb…
+ if ((c = GETNEXT()) !=…
+ len += codepoi…
+ goto chr;
+ }
+ if ((c = GETNEXT()) !=…
+ len += codepoi…
+ goto escchr;
+ }
+ for (hi = cp, i = 12, …
+ if ((c = GETNE…
+ JSON_I…
+ lo |= (hexdigi…
+ }
+ /* 0xdc00 - 0xdfff - l…
+ if (lo >= 0xdc00 && lo…
+ cp = (hi << 10…
+ } else {
+ /* handle grac…
+ len += codepoi…
+ if (capacity(&…
+ goto e…
+ len += codepoi…
+ continue;
+ }
+ }
+ len += codepointtoutf8(cp, &st…
+ continue;
+ default:
+ JSON_INVALID(); /* invalid esc…
+ }
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = c;
+ } else if (c == '\\') {
+ escape = 1;
+ } else if (c == '"') {
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = '\0';
+
+ if (iskey) {
+ /* copy string as key, includi…
+ if (capacity(&(nodes[depth].na…
+ goto end;
+ memcpy(nodes[depth].name, str,…
+ } else {
+ cb(nodes, depth + 1, str);
+ }
+ break;
+ } else {
+ if (capacity(&str, &sz, len, 1) == -1)
+ goto end;
+ str[len++] = c;
+ }
+ }
+ if (iskey)
+ expect = EXPECT_OBJECT_KEY;
+ else
+ expect = EXPECT_END;
+ break;
+ case '[':
+ case '{':
+ if (depth + 1 >= JSON_MAX_NODE_DEPTH)
+ JSON_INVALID(); /* too deep */
+
+ nodes[depth].index = 0;
+ if (c == '[') {
+ nodes[depth].type = JSON_TYPE_ARRAY;
+ expect = EXPECT_ARRAY_VALUE;
+ } else if (c == '{') {
+ iskey = 1;
+ nodes[depth].type = JSON_TYPE_OBJECT;
+ expect = EXPECT_OBJECT_STRING;
+ }
+
+ cb(nodes, depth + 1, "");
+
+ depth++;
+ nodes[depth].index = 0;
+ if (capacity(&(nodes[depth].name), &(nodes[depth].name…
+ goto end;
+ nodes[depth].name[0] = '\0';
+ break;
+ case ']':
+ case '}':
+ if (!depth ||
+ (c == ']' && nodes[depth - 1].type != JSON_TYPE_ARR…
+ (c == '}' && nodes[depth - 1].type != JSON_TYPE_OBJ…
+ JSON_INVALID(); /* unbalanced nodes */
+
+ depth--;
+ nodes[depth].index++;
+ expect = EXPECT_END;
+ break;
+ case ',':
+ if (!depth)
+ JSON_INVALID(); /* unbalanced nodes */
+
+ nodes[depth - 1].index++;
+ if (nodes[depth - 1].type == JSON_TYPE_OBJECT) {
+ iskey = 1;
+ expect = EXPECT_STRING;
+ } else {
+ expect = EXPECT_VALUE;
+ }
+ break;
+ case 't': /* true */
+ if (GETNEXT() != 'r' || GETNEXT() != 'u' || GETNEXT() …
+ JSON_INVALID();
+ nodes[depth].type = JSON_TYPE_BOOL;
+ cb(nodes, depth + 1, "true");
+ expect = EXPECT_END;
+ break;
+ case 'f': /* false */
+ if (GETNEXT() != 'a' || GETNEXT() != 'l' || GETNEXT() …
+ GETNEXT() != 'e')
+ JSON_INVALID();
+ nodes[depth].type = JSON_TYPE_BOOL;
+ cb(nodes, depth + 1, "false");
+ expect = EXPECT_END;
+ break;
+ case 'n': /* null */
+ if (GETNEXT() != 'u' || GETNEXT() != 'l' || GETNEXT() …
+ JSON_INVALID();
+ nodes[depth].type = JSON_TYPE_NULL;
+ cb(nodes, depth + 1, "null");
+ expect = EXPECT_END;
+ break;
+ default: /* number */
+ nodes[depth].type = JSON_TYPE_NUMBER;
+ p = 0;
+ pri[p++] = c;
+ expect = EXPECT_END;
+ while (1) {
+ c = GETNEXT();
+ if (c == EOF ||
+ (!ISDIGIT(c) && c != 'e' && c != 'E' &&
+ c != '+' && c != '-' && c != '.') ||
+ p + 1 >= sizeof(pri)) {
+ pri[p] = '\0';
+ cb(nodes, depth + 1, pri);
+ goto handlechr; /* do not read next ch…
+ } else {
+ pri[p++] = c;
+ }
+ }
+ }
+ }
+ if (depth)
+ JSON_INVALID(); /* unbalanced nodes */
+
+ ret = 0; /* success */
+end:
+ for (depth = 0; depth < sizeof(nodes) / sizeof(nodes[0]); depth++)
+ free(nodes[depth].name);
+ free(str);
+
+ return ret;
+}
diff --git a/json.h b/json.h
@@ -0,0 +1,30 @@
+#ifndef _JSON_H_
+#define _JSON_H_
+
+#include <stddef.h>
+
+enum JSONType {
+ JSON_TYPE_ARRAY = 'a',
+ JSON_TYPE_OBJECT = 'o',
+ JSON_TYPE_STRING = 's',
+ JSON_TYPE_BOOL = 'b',
+ JSON_TYPE_NULL = '?',
+ JSON_TYPE_NUMBER = 'n'
+};
+
+enum JSONError {
+ JSON_ERROR_MEM = -2,
+ JSON_ERROR_INVALID = -1
+};
+
+#define JSON_MAX_NODE_DEPTH 64
+
+struct json_node {
+ enum JSONType type;
+ char *name;
+ size_t namesiz;
+ size_t index; /* count/index for array or object type */
+};
+
+int parsejson(void (*cb)(struct json_node *, size_t, const char *));
+#endif
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.