|
@@ -0,0 +1,695 @@
|
|
|
|
+/* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
|
|
|
|
+/*
|
|
|
|
+ * Copyright 2017 Red Hat, Inc.
|
|
|
|
+ *
|
|
|
|
+ * Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
+ * you may not use this file except in compliance with the License.
|
|
|
|
+ * You may obtain a copy of the License at
|
|
|
|
+ *
|
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
+ *
|
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
|
+ * limitations under the License.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include "jose.h"
|
|
|
|
+#include <stdbool.h>
|
|
|
|
+#include <stdint.h>
|
|
|
|
+#include <string.h>
|
|
|
|
+#include <unistd.h>
|
|
|
|
+#include <getopt.h>
|
|
|
|
+#include <ctype.h>
|
|
|
|
+
|
|
|
|
+#define SUMMARY "Converts JSON between serialization formats"
|
|
|
|
+#define JAIN json_array_insert_new
|
|
|
|
+
|
|
|
|
+#ifdef __MINGW32__
|
|
|
|
+#define sscanf __mingw_sscanf
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static const char *prefix = "jose fmt [OPTIONS]\n\n" SUMMARY;
|
|
|
|
+
|
|
|
|
+typedef struct {
|
|
|
|
+ json_t *args;
|
|
|
|
+} jcmd_opt_t;
|
|
|
|
+
|
|
|
|
+static size_t
|
|
|
|
+convert_int(const json_t *arr, const char *arg)
|
|
|
|
+{
|
|
|
|
+ ssize_t indx = 0;
|
|
|
|
+
|
|
|
|
+ if (sscanf(arg, "%zd", &indx) != 1)
|
|
|
|
+ return SIZE_MAX;
|
|
|
|
+
|
|
|
|
+ if (indx < 0)
|
|
|
|
+ indx += json_array_size(arr);
|
|
|
|
+
|
|
|
|
+ if (indx < 0)
|
|
|
|
+ return SIZE_MAX;
|
|
|
|
+
|
|
|
|
+ return indx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+jcmd_opt_cleanup(jcmd_opt_t *opt)
|
|
|
|
+{
|
|
|
|
+ json_decref(opt->args);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_output(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const int wflags = JSON_ENCODE_ANY | JSON_COMPACT | JSON_SORT_KEYS;
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+ FILE *file = NULL;
|
|
|
|
+ bool ret = false;
|
|
|
|
+
|
|
|
|
+ if (strcmp(s, "-") == 0)
|
|
|
|
+ file = stdout;
|
|
|
|
+ else
|
|
|
|
+ file = fopen(s, "w");
|
|
|
|
+ if (!file)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (json_dumpf(cur, file, wflags) < 0)
|
|
|
|
+ goto egress;
|
|
|
|
+
|
|
|
|
+ if (isatty(fileno(file)) && fwrite("\n", 1, 1, file) != 1)
|
|
|
|
+ goto egress;
|
|
|
|
+
|
|
|
|
+ ret = true;
|
|
|
|
+
|
|
|
|
+egress:
|
|
|
|
+ if (strcmp(s, "-") != 0)
|
|
|
|
+ fclose(file);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_foreach(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const int wflags = JSON_ENCODE_ANY | JSON_COMPACT | JSON_SORT_KEYS;
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+ FILE *file = NULL;
|
|
|
|
+ bool ret = false;
|
|
|
|
+
|
|
|
|
+ if (!json_is_array(cur) && !json_is_object(cur))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (strcmp(s, "-") == 0)
|
|
|
|
+ file = stdout;
|
|
|
|
+ else
|
|
|
|
+ file = fopen(s, "w");
|
|
|
|
+ if (!file)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (json_is_array(cur)) {
|
|
|
|
+ json_t *v = NULL;
|
|
|
|
+ size_t i = 0;
|
|
|
|
+
|
|
|
|
+ json_array_foreach(cur, i, v) {
|
|
|
|
+ if (json_dumpf(v, file, wflags) < 0 ||
|
|
|
|
+ fprintf(file, "\n") < 0)
|
|
|
|
+ goto egress;
|
|
|
|
+ }
|
|
|
|
+ } else if (json_is_object(cur)) {
|
|
|
|
+ const char *k = NULL;
|
|
|
|
+ json_t *v = NULL;
|
|
|
|
+
|
|
|
|
+ json_object_foreach(cur, k, v) {
|
|
|
|
+ if (fprintf(file, "%s=", k) < 0 ||
|
|
|
|
+ json_dumpf(v, file, wflags) < 0 ||
|
|
|
|
+ fprintf(file, "\n") < 0)
|
|
|
|
+ goto egress;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = true;
|
|
|
|
+
|
|
|
|
+egress:
|
|
|
|
+ if (strcmp(s, "-") != 0)
|
|
|
|
+ fclose(file);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_unquote(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+ FILE *file = NULL;
|
|
|
|
+ bool ret = false;
|
|
|
|
+
|
|
|
|
+ if (!json_is_string(cur))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (strcmp(s, "-") == 0)
|
|
|
|
+ return fprintf(stdout, "%s\n", json_string_value(cur)) >= 0;
|
|
|
|
+
|
|
|
|
+ file = fopen(s, "w");
|
|
|
|
+ if (!file)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ ret = fprintf(file, "%s\n", json_string_value(cur)) >= 0;
|
|
|
|
+ fclose(file);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_move(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ json_int_t i = json_integer_value(arg);
|
|
|
|
+
|
|
|
|
+ if (json_array_insert(stk, i + 1, cur) < 0)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (json_array_remove(stk, 0) < 0)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_trunc(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ size_t i = json_integer_value(arg);
|
|
|
|
+ size_t s;
|
|
|
|
+
|
|
|
|
+ for (s = json_array_size(cur); s > i; s--) {
|
|
|
|
+ if (json_array_remove(cur, s - 1) < 0)
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_insert(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ size_t i = json_integer_value(arg);
|
|
|
|
+ return json_array_insert(lst, i, cur) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_append(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ if (json_is_array(lst))
|
|
|
|
+ return json_array_append(lst, cur) >= 0;
|
|
|
|
+
|
|
|
|
+ if (json_is_object(lst))
|
|
|
|
+ return json_object_update_missing(lst, cur) >= 0;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_extend(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ if (json_is_array(lst))
|
|
|
|
+ return json_array_extend(lst, cur) >= 0;
|
|
|
|
+
|
|
|
|
+ if (json_is_object(lst))
|
|
|
|
+ return json_object_update(lst, cur) >= 0;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_delete(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+
|
|
|
|
+ if (json_is_array(cur)) {
|
|
|
|
+ size_t indx;
|
|
|
|
+
|
|
|
|
+ indx = convert_int(cur, s);
|
|
|
|
+ if (indx == SIZE_MAX)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return json_array_remove(cur, indx) >= 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (json_is_object(cur))
|
|
|
|
+ return json_object_del(cur, s) >= 0;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_length(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ size_t count = 0;
|
|
|
|
+
|
|
|
|
+ if (json_is_array(cur))
|
|
|
|
+ count = json_array_size(cur);
|
|
|
|
+ else if (json_is_object(cur))
|
|
|
|
+ count = json_object_size(cur);
|
|
|
|
+ else if (json_is_string(cur))
|
|
|
|
+ count = json_string_length(cur);
|
|
|
|
+ else
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return json_array_insert_new(stk, 0, json_integer(count)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_empty(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ if (json_is_array(cur))
|
|
|
|
+ return json_array_clear(cur) >= 0;
|
|
|
|
+
|
|
|
|
+ if (json_is_object(cur))
|
|
|
|
+ return json_object_clear(cur) >= 0;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_get(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+ json_t *v = NULL;
|
|
|
|
+
|
|
|
|
+ if (json_is_array(cur)) {
|
|
|
|
+ size_t indx;
|
|
|
|
+
|
|
|
|
+ indx = convert_int(cur, s);
|
|
|
|
+ if (indx == SIZE_MAX)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ v = json_array_get(cur, indx);
|
|
|
|
+ } else if (json_is_object(cur)) {
|
|
|
|
+ v = json_object_get(cur, s);
|
|
|
|
+ } else {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return json_array_insert(stk, 0, v) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+cmd_set(const json_t *arg, json_t *stk, json_t *cur, json_t *lst)
|
|
|
|
+{
|
|
|
|
+ const char *s = json_string_value(arg);
|
|
|
|
+
|
|
|
|
+ if (json_is_array(lst)) {
|
|
|
|
+ size_t indx;
|
|
|
|
+
|
|
|
|
+ indx = convert_int(lst, s);
|
|
|
|
+ if (indx == SIZE_MAX)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ return json_array_set(lst, indx, cur) >= 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (json_is_object(lst))
|
|
|
|
+ return json_object_set(lst, s, cur) >= 0;
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_not[] = {
|
|
|
|
+ { .doc = "Invert the following assertion" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_object[] = {
|
|
|
|
+ { .doc = "Assert TOP to be an object" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_array[] = {
|
|
|
|
+ { .doc = "Assert TOP to be an array" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_string[] = {
|
|
|
|
+ { .doc = "Assert TOP to be a string" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_int[] = {
|
|
|
|
+ { .doc = "Assert TOP to be an integer" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_real[] = {
|
|
|
|
+ { .doc = "Assert TOP to be a real" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_number[] = {
|
|
|
|
+ { .doc = "Assert TOP to be a number" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_true[] = {
|
|
|
|
+ { .doc = "Assert TOP to be true" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_false[] = {
|
|
|
|
+ { .doc = "Assert TOP to be false" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_bool[] = {
|
|
|
|
+ { .doc = "Assert TOP to be a boolean" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_null[] = {
|
|
|
|
+ { .doc = "Assert TOP to be null" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_equal[] = {
|
|
|
|
+ { .doc = "Assert TOP to be equal to PREV" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_json[] = {
|
|
|
|
+ { .arg = "JSON", .doc = "Parse JSON constant, push onto TOP" },
|
|
|
|
+ { .arg = "FILE", .doc = "Read from FILE, push onto TOP" },
|
|
|
|
+ { .arg = "-", .doc = "Read from STDIN, push onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_quote[] = {
|
|
|
|
+ { .arg = "STR", .doc = "Convert STR to a string, push onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_output[] = {
|
|
|
|
+ { .arg = "FILE", .doc = "Write TOP to FILE" },
|
|
|
|
+ { .arg = "-", .doc = "Write TOP to STDOUT" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_foreach[] = {
|
|
|
|
+ { .arg = "FILE", .doc = "Write TOP (obj./arr.) to FILE, one line/item" },
|
|
|
|
+ { .arg = "-", .doc = "Write TOP (obj./arr.) to STDOUT, one line/item" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_unquote[] = {
|
|
|
|
+ { .arg = "FILE", .doc = "Write TOP (str.) to FILE without quotes" },
|
|
|
|
+ { .arg = "-", .doc = "Write TOP (str.) to STDOUT without quotes" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_copy[] = {
|
|
|
|
+ { .doc = "Deep copy TOP, push onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_query[] = {
|
|
|
|
+ { .doc = "Query the stack by deep copying and pushing onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_move[] = {
|
|
|
|
+ { .arg = "#", .doc = "Move TOP back # places on the stack" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_unwind[] = {
|
|
|
|
+ { .doc = "Discard TOP from the stack" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_trunc[] = {
|
|
|
|
+ { .arg = "#", .doc = "Shrink TOP (arr.) to length #" },
|
|
|
|
+ { .arg = "-#", .doc = "Discard last # items from TOP (arr.)" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_insert[] = {
|
|
|
|
+ { .arg = "#", .doc = "Insert TOP into PREV (arr.) at #" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_append[] = {
|
|
|
|
+ { .doc = "Append TOP to the end of PREV (arr.)" },
|
|
|
|
+ { .doc = "Set missing values from TOP (obj.) into PREV (obj.)" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_extend[] = {
|
|
|
|
+ { .doc = "Append items from TOP to the end of PREV (arr.)" },
|
|
|
|
+ { .doc = "Set all values from TOP (obj.) into PREV (obj.)" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_delete[] = {
|
|
|
|
+ { .arg = "NAME", .doc = "Delete NAME from TOP (obj.)" },
|
|
|
|
+ { .arg = "#", .doc = "Delete # from TOP (arr.)" },
|
|
|
|
+ { .arg = "-#", .doc = "Delete # from the end of TOP (arr.)" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_length[] = {
|
|
|
|
+ { .doc = "Push length of TOP (arr./str./obj.) to TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_empty[] = {
|
|
|
|
+ { .doc = "Erase all items from TOP (arr./obj.)" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_get[] = {
|
|
|
|
+ { .arg = "NAME", .doc = "Get item with NAME from TOP (obj.), push to TOP" },
|
|
|
|
+ { .arg = "#", .doc = "Get # item from TOP (arr.), push to TOP" },
|
|
|
|
+ { .arg = "-#", .doc = "Get # item from the end of TOP (arr.), push to TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_set[] = {
|
|
|
|
+ { .arg = "NAME", .doc = "Sets TOP into PREV (obj.) with NAME" },
|
|
|
|
+ { .arg = "#", .doc = "Sets TOP into PREV (obj.) at #" },
|
|
|
|
+ { .arg = "-#", .doc = "Sets TOP into PREV (obj.) at # from the end" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_b64l[] = {
|
|
|
|
+ { .doc = "URL-safe Base64 decode TOP (str.), push onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const jcmd_doc_t doc_b64d[] = {
|
|
|
|
+ { .doc = "URL-safe Base64 encode TOP, push onto TOP" },
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+opt_set_null(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
|
|
|
|
+{
|
|
|
|
+ json_t **x = vopt;
|
|
|
|
+ if (!*x) *x = json_array();
|
|
|
|
+ return json_array_append_new(*x, json_pack("[i,n]", cfg->opt.val)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+opt_set_str(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
|
|
|
|
+{
|
|
|
|
+ json_t **x = vopt;
|
|
|
|
+ if (!*x) *x = json_array();
|
|
|
|
+ return json_array_append_new(*x, json_pack("[i,s]", cfg->opt.val, arg)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+opt_set_int(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
|
|
|
|
+{
|
|
|
|
+ json_t **x = vopt;
|
|
|
|
+ json_int_t j = 0;
|
|
|
|
+ int i = 0;
|
|
|
|
+
|
|
|
|
+ if (sscanf(arg, "%d", &i) != 1)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ j = i;
|
|
|
|
+ if (!*x) *x = json_array();
|
|
|
|
+ return json_array_append_new(*x, json_pack("[i,I]", cfg->opt.val, j)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+opt_set_uint(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
|
|
|
|
+{
|
|
|
|
+ unsigned int i = 0;
|
|
|
|
+ json_t **x = vopt;
|
|
|
|
+ json_int_t j = 0;
|
|
|
|
+
|
|
|
|
+ if (sscanf(arg, "%u", &i) != 1)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ j = i;
|
|
|
|
+ if (!*x) *x = json_array();
|
|
|
|
+ return json_array_append_new(*x, json_pack("[i,I]", cfg->opt.val, j)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool
|
|
|
|
+opt_set_json(const jcmd_cfg_t *cfg, void *vopt, const char *arg)
|
|
|
|
+{
|
|
|
|
+ json_auto_t *j = NULL;
|
|
|
|
+ json_t **x = vopt;
|
|
|
|
+
|
|
|
|
+ if (!jcmd_opt_set_json(cfg, &j, arg))
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ if (!*x) *x = json_array();
|
|
|
|
+ return json_array_append_new(*x, json_pack("[i,O]", cfg->opt.val, j)) >= 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const jcmd_cfg_t cfgs[] = {
|
|
|
|
+ { .opt = { "not", no_argument, .val = 'X' }, .doc = doc_not,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "object", no_argument, .val = 'O' }, .doc = doc_object,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "array", no_argument, .val = 'A' }, .doc = doc_array,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "string", no_argument, .val = 'S' }, .doc = doc_string,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "integer", no_argument, .val = 'I' }, .doc = doc_int,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "real", no_argument, .val = 'R' }, .doc = doc_real,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "number", no_argument, .val = 'N' }, .doc = doc_number,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "true", no_argument, .val = 'T' }, .doc = doc_true,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "false", no_argument, .val = 'F' }, .doc = doc_false,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "boolean", no_argument, .val = 'B' }, .doc = doc_bool,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "null", no_argument, .val = '0' }, .doc = doc_null,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "equal", no_argument, .val = 'E' }, .doc = doc_equal,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ { .opt = { "query", no_argument, .val = 'Q' }, .doc = doc_query,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "move", required_argument, .val = 'M' }, .doc = doc_move,
|
|
|
|
+ .set = opt_set_uint, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "unwind", no_argument, .val = 'U' }, .doc = doc_unwind,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ { .opt = { "json", required_argument, .val = 'j' }, .doc = doc_json,
|
|
|
|
+ .set = opt_set_json, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "copy", no_argument, .val = 'c' }, .doc = doc_copy,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "quote", required_argument, .val = 'q' }, .doc = doc_quote,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "output", required_argument, .val = 'o' }, .doc = doc_output,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "foreach", required_argument, .val = 'f' }, .doc = doc_foreach,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "unquote", required_argument, .val = 'u' }, .doc = doc_unquote,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ { .opt = { "truncate", required_argument, .val = 't' }, .doc = doc_trunc,
|
|
|
|
+ .set = opt_set_int, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "insert", required_argument, .val = 'i' }, .doc = doc_insert,
|
|
|
|
+ .set = opt_set_uint, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "append", no_argument, .val = 'a' }, .doc = doc_append,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "extend", no_argument, .val = 'x' }, .doc = doc_extend,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ { .opt = { "delete", required_argument, .val = 'd' }, .doc = doc_delete,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "length", no_argument, .val = 'l' }, .doc = doc_length,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "empty", no_argument, .val = 'e' }, .doc = doc_empty,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "get", required_argument, .val = 'g' }, .doc = doc_get,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "set", required_argument, .val = 's' }, .doc = doc_set,
|
|
|
|
+ .set = opt_set_str, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ { .opt = { "b64load", no_argument, .val = 'y' }, .doc = doc_b64l,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+ { .opt = { "b64dump", no_argument, .val = 'Y' }, .doc = doc_b64d,
|
|
|
|
+ .set = opt_set_null, .off = offsetof(jcmd_opt_t, args) },
|
|
|
|
+
|
|
|
|
+ {}
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int
|
|
|
|
+jcmd_fmt(int argc, char *argv[])
|
|
|
|
+{
|
|
|
|
+ json_auto_t *stk = json_array();
|
|
|
|
+ jcmd_opt_auto_t opt = {};
|
|
|
|
+ unsigned char ret = 0;
|
|
|
|
+ bool not = false;
|
|
|
|
+
|
|
|
|
+ if (!jcmd_opt_parse(argc, argv, cfgs, &opt, prefix))
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < json_array_size(opt.args); i++) {
|
|
|
|
+ json_t *lst = NULL;
|
|
|
|
+ json_t *cur = NULL;
|
|
|
|
+ json_t *p = NULL;
|
|
|
|
+ bool ok = false;
|
|
|
|
+ int o = 0;
|
|
|
|
+
|
|
|
|
+ if (json_unpack(json_array_get(opt.args, i), "[i,o!]", &o, &p) < 0)
|
|
|
|
+ return ++ret;
|
|
|
|
+
|
|
|
|
+ if (not && !strchr("OASIRNTFB0E", o))
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ cur = json_array_get(stk, 0);
|
|
|
|
+ lst = json_array_get(stk, 1);
|
|
|
|
+ ret++;
|
|
|
|
+
|
|
|
|
+ switch (o) {
|
|
|
|
+ case 'X': ok = not = true; break;
|
|
|
|
+ case 'O': ok = not ^ json_is_object(cur); not = false; break;
|
|
|
|
+ case 'A': ok = not ^ json_is_array(cur); not = false; break;
|
|
|
|
+ case 'S': ok = not ^ json_is_string(cur); not = false; break;
|
|
|
|
+ case 'I': ok = not ^ json_is_integer(cur); not = false; break;
|
|
|
|
+ case 'R': ok = not ^ json_is_real(cur); not = false; break;
|
|
|
|
+ case 'N': ok = not ^ json_is_number(cur); not = false; break;
|
|
|
|
+ case 'T': ok = not ^ json_is_true(cur); not = false; break;
|
|
|
|
+ case 'F': ok = not ^ json_is_false(cur); not = false; break;
|
|
|
|
+ case 'B': ok = not ^ json_is_boolean(cur); not = false; break;
|
|
|
|
+ case '0': ok = not ^ json_is_null(cur); not = false; break;
|
|
|
|
+ case 'E': ok = not ^ json_equal(cur, lst); not = false; break;
|
|
|
|
+ case 'Q': ok = JAIN(stk, 0, json_deep_copy(stk)) >= 0; break;
|
|
|
|
+ case 'M': ok = cmd_move(p, stk, cur, lst); break;
|
|
|
|
+ case 'U': ok = json_array_remove(stk, 0) >= 0; break;
|
|
|
|
+ case 'j': ok = json_array_insert(stk, 0, p) >= 0; break;
|
|
|
|
+ case 'c': ok = JAIN(stk, 0, json_deep_copy(cur)) >= 0; break;
|
|
|
|
+ case 'q': ok = json_array_insert(stk, 0, p) >= 0; break;
|
|
|
|
+ case 'o': ok = cmd_output(p, stk, cur, lst); break;
|
|
|
|
+ case 'f': ok = cmd_foreach(p, stk, cur, lst); break;
|
|
|
|
+ case 'u': ok = cmd_unquote(p, stk, cur, lst); break;
|
|
|
|
+ case 't': ok = cmd_trunc(p, stk, cur, lst); break;
|
|
|
|
+ case 'i': ok = cmd_insert(p, stk, cur, lst); break;
|
|
|
|
+ case 'a': ok = cmd_append(p, stk, cur, lst); break;
|
|
|
|
+ case 'x': ok = cmd_extend(p, stk, cur, lst); break;
|
|
|
|
+ case 'd': ok = cmd_delete(p, stk, cur, lst); break;
|
|
|
|
+ case 'l': ok = cmd_length(p, stk, cur, lst); break;
|
|
|
|
+ case 'e': ok = cmd_empty(p, stk, cur, lst); break;
|
|
|
|
+ case 'g': ok = cmd_get(p, stk, cur, lst); break;
|
|
|
|
+ case 's': ok = cmd_set(p, stk, cur, lst); break;
|
|
|
|
+ case 'Y': ok = JAIN(stk, 0, jose_b64_enc_dump(cur)) >= 0; break;
|
|
|
|
+ case 'y': ok = JAIN(stk, 0, jose_b64_dec_load(cur)) >= 0; break;
|
|
|
|
+ default: ok = false; break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!ok)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (not)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return EXIT_SUCCESS;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+JCMD_REGISTER(SUMMARY, jcmd_fmt, "fmt")
|