main: implement array mode
authorJo-Philipp Wich <jo@mein.io>
Sun, 4 Feb 2018 14:37:15 +0000 (15:37 +0100)
committerJo-Philipp Wich <jo@mein.io>
Sun, 4 Feb 2018 14:37:15 +0000 (15:37 +0100)
Implement a new option "-a" which turns on array mode, meaning that
the program will attempt to parse multiple consecutive JSON objects
from the source data and merge them into a JSON array.

This is mainly useful for operating on JSON log files with one object
per line.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
main.c

diff --git a/main.c b/main.c
index 85a53f4af40e4d02429ee2fdfda597780b7e1f96..5041d10de8343915ca85136c569c0d66c34ddae2 100644 (file)
--- a/main.c
+++ b/main.c
@@ -43,9 +43,10 @@ print_usage(char *app)
 {
        printf(
        "== Usage ==\n\n"
-       "  # %s [-i <file> | -s \"json...\"] {-t <pattern> | -e <pattern>}\n"
+       "  # %s [-a] [-i <file> | -s \"json...\"] {-t <pattern> | -e <pattern>}\n"
        "  -q           Quiet, no errors are printed\n"
        "  -h, --help   Print this help\n"
+       "  -a           Implicitely treat input as array, useful for JSON logs\n"
        "  -i path      Specify a JSON file to parse\n"
        "  -s \"json\"  Specify a JSON string to parse\n"
        "  -l limit     Specify max number of results to show\n"
@@ -75,30 +76,80 @@ print_usage(char *app)
 }
 
 static struct json_object *
-parse_json(FILE *fd, const char *source, const char **error)
+parse_json_chunk(struct json_tokener *tok, struct json_object *array,
+                 const char *buf, size_t len, enum json_tokener_error *err)
 {
-       int len;
-       char buf[256];
        struct json_object *obj = NULL;
+
+       while (len)
+       {
+               obj = json_tokener_parse_ex(tok, buf, len);
+               *err = json_tokener_get_error(tok);
+
+               if (*err == json_tokener_success)
+               {
+                       if (array)
+                       {
+                               json_object_array_add(array, obj);
+                       }
+                       else
+                       {
+                               break;
+                       }
+               }
+               else if (*err != json_tokener_continue)
+               {
+                       break;
+               }
+
+               buf += tok->char_offset;
+               len -= tok->char_offset;
+       }
+
+       return obj;
+}
+
+static struct json_object *
+parse_json(FILE *fd, const char *source, const char **error, bool array_mode)
+{
+       size_t len;
+       char buf[256];
+       struct json_object *obj = NULL, *array = NULL;
        struct json_tokener *tok = json_tokener_new();
        enum json_tokener_error err = json_tokener_continue;
 
        if (!tok)
+       {
+               *error = "Out of memory";
                return NULL;
+       }
+
+       if (array_mode)
+       {
+               array = json_object_new_array();
+
+               if (!array)
+               {
+                       json_tokener_free(tok);
+                       *error = "Out of memory";
+                       return NULL;
+               }
+       }
 
        if (source)
        {
-               obj = json_tokener_parse_ex(tok, source, strlen(source));
-               err = json_tokener_get_error(tok);
+               obj = parse_json_chunk(tok, array, source, strlen(source), &err);
        }
        else
        {
                while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
                {
-                       obj = json_tokener_parse_ex(tok, buf, len);
-                       err = json_tokener_get_error(tok);
+                       obj = parse_json_chunk(tok, array, buf, len, &err);
 
-                       if (!err || err != json_tokener_continue)
+                       if (err == json_tokener_success && !array)
+                               break;
+
+                       if (err != json_tokener_continue)
                                break;
                }
        }
@@ -114,7 +165,7 @@ parse_json(FILE *fd, const char *source, const char **error)
                return NULL;
        }
 
-       return obj;
+       return array ? array : obj;
 }
 
 static void
@@ -414,6 +465,7 @@ out:
 
 int main(int argc, char **argv)
 {
+       bool array_mode = false;
        int opt, rv = 0, limit = 0x7FFFFFFF;
        FILE *input = stdin;
        struct json_object *jsobj = NULL;
@@ -425,10 +477,14 @@ int main(int argc, char **argv)
                goto out;
        }
 
-       while ((opt = getopt(argc, argv, "hi:s:e:t:F:l:q")) != -1)
+       while ((opt = getopt(argc, argv, "ahi:s:e:t:F:l:q")) != -1)
        {
                switch (opt)
                {
+               case 'a':
+                       array_mode = true;
+                       break;
+
                case 'h':
                        print_usage(argv[0]);
                        goto out;
@@ -464,7 +520,7 @@ int main(int argc, char **argv)
                case 'e':
                        if (!jsobj)
                        {
-                               jsobj = parse_json(input, source, &jserr);
+                               jsobj = parse_json(input, source, &jserr, array_mode);
 
                                if (!jsobj)
                                {