|
@@ -0,0 +1,258 @@
|
|
|
|
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
+ *
|
|
|
|
+ * Copyright 2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+/* Enable POSIX features */
|
|
|
|
+#if !defined(_XOPEN_SOURCE)
|
|
|
|
+# define _XOPEN_SOURCE 600
|
|
|
|
+#endif
|
|
|
|
+#ifndef _DEFAULT_SOURCE
|
|
|
|
+# define _DEFAULT_SOURCE
|
|
|
|
+#endif
|
|
|
|
+/* On older systems we need to define _BSD_SOURCE.
|
|
|
|
+ * _DEFAULT_SOURCE is an alias for that. */
|
|
|
|
+#ifndef _BSD_SOURCE
|
|
|
|
+# define _BSD_SOURCE
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <ua_types.h>
|
|
|
|
+
|
|
|
|
+/* Internal headers */
|
|
|
|
+#include "ua_types_generated.h"
|
|
|
|
+#include "ua_types_generated_handling.h"
|
|
|
|
+#include "ua_types_encoding_binary.h"
|
|
|
|
+#include "ua_types_encoding_json.h"
|
|
|
|
+
|
|
|
|
+static UA_StatusCode
|
|
|
|
+encode(const UA_ByteString *buf, UA_ByteString *out,
|
|
|
|
+ const UA_DataType *type) {
|
|
|
|
+ void *data = malloc(type->memSize);
|
|
|
|
+ if(!data)
|
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
+
|
|
|
|
+ size_t offset = 0;
|
|
|
|
+ UA_StatusCode retval = UA_decodeBinary(buf, &offset, data, type, NULL);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ free(data);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+ if(offset != buf->length) {
|
|
|
|
+ UA_delete(data, type);
|
|
|
|
+ fprintf(stderr, "Input buffer not completely read\n");
|
|
|
|
+ return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ size_t jsonLength = UA_calcSizeJson(data, type, NULL, 0, NULL, 0, true);
|
|
|
|
+ retval = UA_ByteString_allocBuffer(out, jsonLength);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ UA_delete(data, type);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ uint8_t *bufPos = &out->data[0];
|
|
|
|
+ const uint8_t *bufEnd = &out->data[out->length];
|
|
|
|
+ retval = UA_encodeJson(data, type, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
|
|
|
|
+ UA_delete(data, type);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ UA_ByteString_deleteMembers(out);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ out->length = (size_t)((uintptr_t)bufPos - (uintptr_t)out->data);
|
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static UA_StatusCode
|
|
|
|
+decode(const UA_ByteString *buf, UA_ByteString *out,
|
|
|
|
+ const UA_DataType *type) {
|
|
|
|
+ void *data = malloc(type->memSize);
|
|
|
|
+ if(!data)
|
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
|
+
|
|
|
|
+ UA_StatusCode retval = UA_decodeJson(buf, data, type);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ free(data);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ size_t binLength = UA_calcSizeBinary(data, type);
|
|
|
|
+ retval = UA_ByteString_allocBuffer(out, binLength);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ UA_delete(data, type);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ uint8_t *bufPos = &out->data[0];
|
|
|
|
+ const uint8_t *bufEnd = &out->data[out->length];
|
|
|
|
+ retval = UA_encodeBinary(data, type, &bufPos, &bufEnd, NULL, NULL);
|
|
|
|
+ UA_delete(data, type);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
|
+ UA_ByteString_deleteMembers(out);
|
|
|
|
+ return retval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ out->length = (size_t)((uintptr_t)bufPos - (uintptr_t)out->data);
|
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+usage(void) {
|
|
|
|
+ printf("Usage: ua2json [encode|decode] [-t dataType] [-o outputFile] [inputFile]\n"
|
|
|
|
+ "- encode/decode: Translate UA binary input to UA JSON / "
|
|
|
|
+ "Translate UA JSON input to UA binary (required)\n"
|
|
|
|
+ "- dataType: UA DataType of the input (default: Variant)\n"
|
|
|
|
+ "- outputFile: Output target (default: write to stdout)\n"
|
|
|
|
+ "- inputFile: Input source (default: read from stdin)\n");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int main(int argc, char **argv) {
|
|
|
|
+ UA_Boolean encode_option = true;
|
|
|
|
+ const char *datatype_option = "Variant";
|
|
|
|
+ const char *input_option = NULL;
|
|
|
|
+ const char *output_option = NULL;
|
|
|
|
+ UA_ByteString outbuf = UA_BYTESTRING_NULL;
|
|
|
|
+ UA_ByteString buf = UA_BYTESTRING_NULL;
|
|
|
|
+ FILE *in = stdin;
|
|
|
|
+ FILE *out = stdout;
|
|
|
|
+ int retcode = -1;
|
|
|
|
+
|
|
|
|
+ /* Read the command line options */
|
|
|
|
+ if(argc < 2) {
|
|
|
|
+ usage();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(strcmp(argv[1], "encode") == 0) {
|
|
|
|
+ encode_option = true;
|
|
|
|
+ } else if(strcmp(argv[1], "decode") == 0) {
|
|
|
|
+ encode_option = false;
|
|
|
|
+ } else {
|
|
|
|
+ fprintf(stderr, "Error: The first argument must be \"encode\" or \"decode\"\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for(int argpos = 2; argpos < argc; argpos++) {
|
|
|
|
+ if(strcmp(argv[argpos], "--help") == 0) {
|
|
|
|
+ usage();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(strcmp(argv[argpos], "-t") == 0) {
|
|
|
|
+ if(argpos + 1 == argc) {
|
|
|
|
+ usage();
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ argpos++;
|
|
|
|
+ datatype_option = argv[argpos];
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(strcmp(argv[argpos], "-o") == 0) {
|
|
|
|
+ if(argpos + 1 == argc) {
|
|
|
|
+ usage();
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ argpos++;
|
|
|
|
+ output_option = argv[argpos];
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(argpos + 1 == argc) {
|
|
|
|
+ input_option = argv[argpos];
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ usage();
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Find the data type */
|
|
|
|
+ const UA_DataType *type = NULL;
|
|
|
|
+ for(size_t i = 0; i < UA_TYPES_COUNT; ++i) {
|
|
|
|
+ if(strcmp(datatype_option, UA_TYPES[i].typeName) == 0) {
|
|
|
|
+ type = &UA_TYPES[i];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if(!type) {
|
|
|
|
+ fprintf(stderr, "Error: Datatype not found\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Open files */
|
|
|
|
+ if(input_option) {
|
|
|
|
+ in = fopen(input_option, "rb");
|
|
|
|
+ if(!in) {
|
|
|
|
+ fprintf(stderr, "Could not open input file %s\n", input_option);
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if(output_option) {
|
|
|
|
+ out = fopen(output_option, "wb");
|
|
|
|
+ if(!out) {
|
|
|
|
+ fprintf(stderr, "Could not open output file %s\n", output_option);
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Read input until EOF */
|
|
|
|
+ size_t pos = 0;
|
|
|
|
+ size_t length = 128;
|
|
|
|
+ while(true) {
|
|
|
|
+ if(pos >= buf.length) {
|
|
|
|
+ length = length * 8;
|
|
|
|
+ UA_Byte *r = (UA_Byte*)realloc(buf.data, length);
|
|
|
|
+ if(!r) {
|
|
|
|
+ fprintf(stderr, "Out of memory\n");
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+ buf.length = length;
|
|
|
|
+ buf.data = r;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ssize_t c = read(fileno(in), &buf.data[pos], length - pos);
|
|
|
|
+ if(c == 0)
|
|
|
|
+ break;
|
|
|
|
+ if(c < 0) {
|
|
|
|
+ fprintf(stderr, "Reading from input failed\n");
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pos += (size_t)c;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(pos == 0) {
|
|
|
|
+ fprintf(stderr, "No input\n");
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+ buf.length = pos;
|
|
|
|
+
|
|
|
|
+ /* Convert */
|
|
|
|
+ UA_StatusCode result;
|
|
|
|
+ if(encode_option)
|
|
|
|
+ result = encode(&buf, &outbuf, type);
|
|
|
|
+ else
|
|
|
|
+ result = decode(&buf, &outbuf, type);
|
|
|
|
+ if(result != UA_STATUSCODE_GOOD) {
|
|
|
|
+ fprintf(stderr, "Error: Parsing failed with code %s\n",
|
|
|
|
+ UA_StatusCode_name(result));
|
|
|
|
+ goto cleanup;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Print the output and quit */
|
|
|
|
+ fwrite(outbuf.data, 1, outbuf.length, out);
|
|
|
|
+ retcode = 0;
|
|
|
|
+
|
|
|
|
+ cleanup:
|
|
|
|
+ UA_ByteString_deleteMembers(&buf);
|
|
|
|
+ UA_ByteString_deleteMembers(&outbuf);
|
|
|
|
+ if(in != stdin)
|
|
|
|
+ fclose(in);
|
|
|
|
+ if(out != stdout)
|
|
|
|
+ fclose(out);
|
|
|
|
+ return retcode;
|
|
|
|
+}
|