1290067Sbapt/* Copyright (c) 2015, Cesanta Software
2290067Sbapt *
3290067Sbapt * Redistribution and use in source and binary forms, with or without
4290067Sbapt * modification, are permitted provided that the following conditions are met:
5290067Sbapt *       * Redistributions of source code must retain the above copyright
6290067Sbapt *         notice, this list of conditions and the following disclaimer.
7290067Sbapt *       * Redistributions in binary form must reproduce the above copyright
8290067Sbapt *         notice, this list of conditions and the following disclaimer in the
9290067Sbapt *         documentation and/or other materials provided with the distribution.
10290067Sbapt *
11290067Sbapt * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
12290067Sbapt * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13290067Sbapt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14290067Sbapt * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
15290067Sbapt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16290067Sbapt * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
17290067Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
18290067Sbapt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
19290067Sbapt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
20290067Sbapt * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21290067Sbapt */
22290067Sbapt
23290067Sbapt#include <stdio.h>
24290067Sbapt#include <getopt.h>
25290067Sbapt#include <stdlib.h>
26290067Sbapt
27290067Sbapt#include "ucl.h"
28290067Sbapt
29290067Sbaptstatic struct option opts[] = {
30290067Sbapt    {"help", no_argument, NULL, 'h'},
31290067Sbapt    {"in", required_argument, NULL, 'i' },
32290067Sbapt    {"out", required_argument, NULL, 'o' },
33290067Sbapt    {"schema", required_argument, NULL, 's'},
34290067Sbapt    {"format", required_argument, NULL, 'f'},
35290067Sbapt    {0, 0, 0, 0}
36290067Sbapt};
37290067Sbapt
38290067Sbaptvoid usage(const char *name, FILE *out) {
39290067Sbapt  fprintf(out, "Usage: %s [--help] [-i|--in file] [-o|--out file]\n", name);
40290067Sbapt  fprintf(out, "    [-s|--schema file] [-f|--format format]\n\n");
41290067Sbapt  fprintf(out, "  --help   - print this message and exit\n");
42290067Sbapt  fprintf(out, "  --in     - specify input filename "
43290067Sbapt          "(default: standard input)\n");
44290067Sbapt  fprintf(out, "  --out    - specify output filename "
45290067Sbapt          "(default: standard output)\n");
46290067Sbapt  fprintf(out, "  --schema - specify schema file for validation\n");
47290067Sbapt  fprintf(out, "  --format - output format. Options: ucl (default), "
48290067Sbapt          "json, compact_json, yaml, msgpack\n");
49290067Sbapt}
50290067Sbapt
51290067Sbaptint main(int argc, char **argv) {
52290067Sbapt  char ch;
53290067Sbapt  FILE *in = stdin, *out = stdout;
54290067Sbapt  const char *schema = NULL;
55290067Sbapt  unsigned char *buf = NULL;
56290067Sbapt  size_t size = 0, r = 0;
57290067Sbapt  struct ucl_parser *parser = NULL;
58290067Sbapt  ucl_object_t *obj = NULL;
59290067Sbapt  ucl_emitter_t emitter = UCL_EMIT_CONFIG;
60290067Sbapt
61290067Sbapt  while((ch = getopt_long(argc, argv, "hi:o:s:f:", opts, NULL)) != -1) {
62290067Sbapt    switch (ch) {
63290067Sbapt    case 'i':
64290067Sbapt      in = fopen(optarg, "r");
65290067Sbapt      if (in == NULL) {
66290067Sbapt        perror("fopen on input file");
67290067Sbapt        exit(EXIT_FAILURE);
68290067Sbapt      }
69290067Sbapt      break;
70290067Sbapt    case 'o':
71290067Sbapt      out = fopen(optarg, "w");
72290067Sbapt      if (out == NULL) {
73290067Sbapt        perror("fopen on output file");
74290067Sbapt        exit(EXIT_FAILURE);
75290067Sbapt      }
76290067Sbapt      break;
77290067Sbapt    case 's':
78290067Sbapt      schema = optarg;
79290067Sbapt      break;
80290067Sbapt    case 'f':
81290067Sbapt      if (strcmp(optarg, "ucl") == 0) {
82290067Sbapt        emitter = UCL_EMIT_CONFIG;
83290067Sbapt      } else if (strcmp(optarg, "json") == 0) {
84290067Sbapt        emitter = UCL_EMIT_JSON;
85290067Sbapt      } else if (strcmp(optarg, "yaml") == 0) {
86290067Sbapt        emitter = UCL_EMIT_YAML;
87290067Sbapt      } else if (strcmp(optarg, "compact_json") == 0) {
88290067Sbapt        emitter = UCL_EMIT_JSON_COMPACT;
89290067Sbapt      } else if (strcmp(optarg, "msgpack") == 0) {
90290067Sbapt        emitter = UCL_EMIT_MSGPACK;
91290067Sbapt      } else {
92290067Sbapt        fprintf(stderr, "Unknown output format: %s\n", optarg);
93290067Sbapt        exit(EXIT_FAILURE);
94290067Sbapt      }
95290067Sbapt      break;
96290067Sbapt    case 'h':
97290067Sbapt      usage(argv[0], stdout);
98290067Sbapt      exit(0);
99290067Sbapt    default:
100290067Sbapt      usage(argv[0], stderr);
101290067Sbapt      exit(EXIT_FAILURE);
102290067Sbapt      break;
103290067Sbapt    }
104290067Sbapt  }
105290067Sbapt
106290067Sbapt  parser = ucl_parser_new(0);
107290067Sbapt  buf = malloc(BUFSIZ);
108290067Sbapt  size = BUFSIZ;
109290067Sbapt  while(!feof(in) && !ferror(in)) {
110290067Sbapt    if (r == size) {
111290067Sbapt      buf = realloc(buf, size*2);
112290067Sbapt      size *= 2;
113290067Sbapt      if (buf == NULL) {
114290067Sbapt        perror("realloc");
115290067Sbapt        exit(EXIT_FAILURE);
116290067Sbapt      }
117290067Sbapt    }
118290067Sbapt    r += fread(buf + r, 1, size - r, in);
119290067Sbapt  }
120290067Sbapt  if (ferror(in)) {
121290067Sbapt    fprintf(stderr, "Failed to read the input file.\n");
122290067Sbapt    exit(EXIT_FAILURE);
123290067Sbapt  }
124290067Sbapt  fclose(in);
125290067Sbapt  if (!ucl_parser_add_chunk(parser, buf, r)) {
126290067Sbapt    fprintf(stderr, "Failed to parse input file: %s\n",
127290067Sbapt            ucl_parser_get_error(parser));
128290067Sbapt    exit(EXIT_FAILURE);
129290067Sbapt  }
130290067Sbapt  if ((obj = ucl_parser_get_object(parser)) == NULL) {
131290067Sbapt    fprintf(stderr, "Failed to get root object: %s\n",
132290067Sbapt            ucl_parser_get_error(parser));
133290067Sbapt    exit(EXIT_FAILURE);
134290067Sbapt  }
135290067Sbapt  if (schema != NULL) {
136290067Sbapt    struct ucl_parser *schema_parser = ucl_parser_new(0);
137290067Sbapt    ucl_object_t *schema_obj = NULL;
138290067Sbapt    struct ucl_schema_error error;
139290067Sbapt
140290067Sbapt    if (!ucl_parser_add_file(schema_parser, schema)) {
141290067Sbapt      fprintf(stderr, "Failed to parse schema file: %s\n",
142290067Sbapt              ucl_parser_get_error(schema_parser));
143290067Sbapt      exit(EXIT_FAILURE);
144290067Sbapt    }
145290067Sbapt    if ((schema_obj = ucl_parser_get_object(schema_parser)) == NULL) {
146290067Sbapt      fprintf(stderr, "Failed to get root object: %s\n",
147290067Sbapt              ucl_parser_get_error(schema_parser));
148290067Sbapt      exit(EXIT_FAILURE);
149290067Sbapt    }
150290067Sbapt    if (!ucl_object_validate(schema_obj, obj, &error)) {
151290067Sbapt      fprintf(stderr, "Validation failed: %s\n", error.msg);
152290067Sbapt      exit(EXIT_FAILURE);
153290067Sbapt    }
154290067Sbapt  }
155290067Sbapt
156290067Sbapt  if (emitter != UCL_EMIT_MSGPACK) {
157290067Sbapt    fprintf(out, "%s\n", ucl_object_emit(obj, emitter));
158290067Sbapt  }
159290067Sbapt  else {
160290067Sbapt    size_t len;
161290067Sbapt    unsigned char *res;
162290067Sbapt
163290067Sbapt    res = ucl_object_emit_len(obj, emitter, &len);
164290067Sbapt    fwrite(res, 1, len, out);
165290067Sbapt  }
166290067Sbapt
167290067Sbapt  return 0;
168290067Sbapt}
169