|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
-
- #include "apps_config.h"
-
- /* XXX for a regular program, these should be <json-c/foo.h>
- * but that's inconvenient when building in the json-c source tree.
- */
- #include "json_object.h"
- #include "json_tokener.h"
- #include "json_util.h"
-
- #ifdef HAVE_SYS_RESOURCE_H
- #include <sys/resource.h>
- #include <sys/time.h>
- #endif
-
- #ifndef JSON_NORETURN
- #if defined(_MSC_VER)
- #define JSON_NORETURN __declspec(noreturn)
- #elif defined(__OS400__)
- #define JSON_NORETURN
- #else
- /* 'cold' attribute is for optimization, telling the computer this code
- * path is unlikely.
- */
- #define JSON_NORETURN __attribute__((noreturn, cold))
- #endif
- #endif
-
- static int formatted_output = JSON_C_TO_STRING_SPACED;
- static int show_output = 1;
- static int strict_mode = 0;
- static int validate_utf8 = 0;
- static int tokener_flags = 0;
- static int color = 0;
- static const char *fname = NULL;
-
- #ifndef HAVE_JSON_TOKENER_GET_PARSE_END
- #define json_tokener_get_parse_end(tok) ((tok)->char_offset)
- #endif
-
- JSON_NORETURN static void usage(const char *argv0, int exitval, const char *errmsg);
- static void showmem(void);
- static int parseit(int fd, int (*callback)(struct json_object *));
- static int showobj(struct json_object *new_obj);
-
- static void showmem(void)
- {
- #ifdef HAVE_GETRUSAGE
- struct rusage rusage;
- memset(&rusage, 0, sizeof(rusage));
- getrusage(RUSAGE_SELF, &rusage);
- fprintf(stderr, "maxrss: %ld KB\n", rusage.ru_maxrss);
- #endif
- }
-
- static int parseit(int fd, int (*callback)(struct json_object *))
- {
- struct json_object *obj;
- char buf[32768];
- ssize_t ret;
- int depth = JSON_TOKENER_DEFAULT_DEPTH;
- json_tokener *tok;
-
- tok = json_tokener_new_ex(depth);
- if (!tok)
- {
- fprintf(stderr, "unable to allocate json_tokener: %s\n", strerror(errno));
- return 1;
- }
- if (strict_mode)
- {
- tokener_flags |= JSON_TOKENER_STRICT;
- #ifdef JSON_TOKENER_ALLOW_TRAILING_CHARS
- tokener_flags |= JSON_TOKENER_ALLOW_TRAILING_CHARS;
- #endif
- }
- if (validate_utf8)
- {
- tokener_flags |= JSON_TOKENER_VALIDATE_UTF8;
- }
- if (tokener_flags)
- {
- json_tokener_set_flags(tok, tokener_flags);
- }
-
- // XXX push this into some kind of json_tokener_parse_fd API?
- // json_object_from_fd isn't flexible enough, and mirroring
- // everything you can do with a tokener into json_util.c seems
- // like the wrong approach.
- size_t total_read = 0;
- while ((ret = read(fd, buf, sizeof(buf))) > 0)
- {
- size_t retu = (size_t)ret; // We know it's positive
- total_read += retu;
- size_t start_pos = 0;
- while (start_pos != retu)
- {
- obj = json_tokener_parse_ex(tok, &buf[start_pos], retu - start_pos);
- enum json_tokener_error jerr = json_tokener_get_error(tok);
- size_t parse_end = json_tokener_get_parse_end(tok);
- if (obj == NULL && jerr != json_tokener_continue)
- {
- const char *aterr = (start_pos + parse_end < (int)sizeof(buf)) ?
- &buf[start_pos + parse_end] : "";
- fflush(stdout);
- size_t fail_offset = total_read - retu + start_pos + parse_end;
- fprintf(stderr, "Failed at offset %lu: %s %c\n", (unsigned long)fail_offset,
- json_tokener_error_desc(jerr), aterr[0]);
- json_tokener_free(tok);
- return 1;
- }
- if (obj != NULL)
- {
- int cb_ret = callback(obj);
- json_object_put(obj);
- if (cb_ret != 0)
- {
- json_tokener_free(tok);
- return 1;
- }
- }
- start_pos += json_tokener_get_parse_end(tok);
- assert(start_pos <= retu);
- }
- }
- if (ret < 0)
- {
- fprintf(stderr, "error reading fd %d: %s\n", fd, strerror(errno));
- }
-
- json_tokener_free(tok);
- return 0;
- }
-
- static int showobj(struct json_object *new_obj)
- {
- if (new_obj == NULL)
- {
- fprintf(stderr, "%s: Failed to parse\n", fname);
- return 1;
- }
-
- fprintf(stderr, "Successfully parsed object from %s\n", fname);
-
- if (show_output)
- {
- const char *output;
- output = json_object_to_json_string_ext(new_obj, formatted_output | color);
- printf("%s\n", output);
- }
-
- showmem();
- return 0;
- }
-
- static void usage(const char *argv0, int exitval, const char *errmsg)
- {
- FILE *fp = stdout;
- if (exitval != 0)
- fp = stderr;
- if (errmsg != NULL)
- fprintf(fp, "ERROR: %s\n\n", errmsg);
- fprintf(fp, "Usage: %s [-f|-F <arg>] [-n] [-s] [-u] [filename]\n", argv0);
- fprintf(fp, " -f - Format the output to stdout with JSON_C_TO_STRING_PRETTY (default is JSON_C_TO_STRING_SPACED)\n");
- fprintf(fp, " -F - Format the output to stdout with <arg>, e.g. 0 for JSON_C_TO_STRING_PLAIN\n");
- fprintf(fp, " -n - No output\n");
- fprintf(fp, " -c - Set JSON_C_TO_STRING_COLOR to colorize the output\n");
- fprintf(fp, " -P - Initialize tokener flags to the given value\n");
- fprintf(fp, " -s - Parse in strict mode, add flags:\n");
- fprintf(fp, " JSON_TOKENER_STRICT|JSON_TOKENER_ALLOW_TRAILING_CHARS\n");
- fprintf(fp, " -u - Add the JSON_TOKENER_VALIDATE_UTF8 flag when parsing\n");
- fprintf(fp, " Diagnostic information will be emitted to stderr\n");
-
- fprintf(fp, "\nWARNING WARNING WARNING\n");
- fprintf(fp, "This is a prototype, it may change or be removed at any time!\n");
- exit(exitval);
- }
-
- int main(int argc, char **argv)
- {
- int opt;
-
- while ((opt = getopt(argc, argv, "cfF:hnP:su")) != -1)
- {
- switch (opt)
- {
- case 'c': color = JSON_C_TO_STRING_COLOR; break;
- case 'f': formatted_output = JSON_C_TO_STRING_PRETTY; break;
- case 'F': formatted_output = atoi(optarg); break;
- case 'n': show_output = 0; break;
- case 'P': tokener_flags = atoi(optarg); break;
- case 's': strict_mode = 1; break;
- case 'u': validate_utf8 = 1; break;
- case 'h': usage(argv[0], 0, NULL);
- default: /* '?' */ usage(argv[0], EXIT_FAILURE, "Unknown arguments");
- }
- }
- int fd = STDIN_FILENO;
- fname = "stdin";
- if (argc > optind && strcmp(argv[optind], "-") != 0)
- {
- fname = argv[optind];
- fd = open(fname, O_RDONLY, 0);
- }
- showmem();
- if (parseit(fd, showobj) != 0)
- exit(EXIT_FAILURE);
- showmem();
-
- exit(EXIT_SUCCESS);
- }
|