1/* Copyright (c) 2013, Vsevolod Stakhov
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *       * Redistributions of source code must retain the above copyright
7 *         notice, this list of conditions and the following disclaimer.
8 *       * Redistributions in binary form must reproduce the above copyright
9 *         notice, this list of conditions and the following disclaimer in the
10 *         documentation and/or other materials provided with the distribution.
11 *
12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 */
23
24#include "ucl.h"
25#include "ucl_internal.h"
26#include <sys/types.h>
27#include <fcntl.h>
28#include <unistd.h>
29
30
31int
32main (int argc, char **argv)
33{
34	char *inbuf = NULL;
35	struct ucl_parser *parser = NULL, *parser2 = NULL;
36	ucl_object_t *obj, *comments = NULL;
37	ssize_t bufsize, r;
38	FILE *in, *out;
39	unsigned char *emitted = NULL;
40	const char *fname_in = NULL, *fname_out = NULL;
41	int ret = 0, opt, json = 0, compact = 0, yaml = 0,
42			save_comments = 0, skip_macro = 0,
43			flags, fd_out, fd_in, use_fd = 0;
44	struct ucl_emitter_functions *func;
45
46	while ((opt = getopt(argc, argv, "fjcyCM")) != -1) {
47		switch (opt) {
48		case 'j':
49			json = 1;
50			break;
51		case 'c':
52			compact = 1;
53			break;
54		case 'C':
55			save_comments = 1;
56			break;
57		case 'y':
58			yaml = 1;
59			break;
60		case 'M':
61			skip_macro = true;
62			break;
63		case 'f':
64			use_fd = true;
65			break;
66		default: /* '?' */
67			fprintf (stderr, "Usage: %s [-jcy] [-CM] [-f] [in] [out]\n",
68					argv[0]);
69			exit (EXIT_FAILURE);
70		}
71	}
72
73	argc -= optind;
74	argv += optind;
75
76	switch (argc) {
77	case 1:
78		fname_in = argv[0];
79		break;
80	case 2:
81		fname_in = argv[0];
82		fname_out = argv[1];
83		break;
84	}
85
86	if (!use_fd) {
87		if (fname_in != NULL) {
88			in = fopen (fname_in, "r");
89			if (in == NULL) {
90				exit (-errno);
91			}
92		}
93		else {
94			in = stdin;
95		}
96	}
97	else {
98		if (fname_in != NULL) {
99			fd_in = open (fname_in, O_RDONLY);
100			if (fd_in == -1) {
101				exit (-errno);
102			}
103		}
104		else {
105			fd_in = STDIN_FILENO;
106		}
107	}
108
109	flags = UCL_PARSER_KEY_LOWERCASE;
110
111	if (save_comments) {
112		flags |= UCL_PARSER_SAVE_COMMENTS;
113	}
114
115	if (skip_macro) {
116		flags |= UCL_PARSER_DISABLE_MACRO;
117	}
118
119	parser = ucl_parser_new (flags);
120	ucl_parser_register_variable (parser, "ABI", "unknown");
121
122	if (fname_in != NULL) {
123		ucl_parser_set_filevars (parser, fname_in, true);
124	}
125
126	if (!use_fd) {
127		inbuf = malloc (BUFSIZ);
128		bufsize = BUFSIZ;
129		r = 0;
130
131		while (!feof (in) && !ferror (in)) {
132			if (r == bufsize) {
133				inbuf = realloc (inbuf, bufsize * 2);
134				bufsize *= 2;
135				if (inbuf == NULL) {
136					perror ("realloc");
137					exit (EXIT_FAILURE);
138				}
139			}
140			r += fread (inbuf + r, 1, bufsize - r, in);
141		}
142
143		if (ferror (in)) {
144			fprintf (stderr, "Failed to read the input file.\n");
145			exit (EXIT_FAILURE);
146		}
147
148		ucl_parser_add_chunk (parser, (const unsigned char *)inbuf, r);
149		fclose (in);
150	}
151	else {
152		ucl_parser_add_fd (parser, fd_in);
153		close (fd_in);
154	}
155
156	if (!use_fd) {
157		if (fname_out != NULL) {
158			out = fopen (fname_out, "w");
159			if (out == NULL) {
160				exit (-errno);
161			}
162		}
163		else {
164			out = stdout;
165		}
166	}
167	else {
168		if (fname_out != NULL) {
169			fd_out = open (fname_out, O_WRONLY | O_CREAT, 00644);
170			if (fd_out == -1) {
171				exit (-errno);
172			}
173		}
174		else {
175			fd_out = STDOUT_FILENO;
176		}
177	}
178
179
180	if (ucl_parser_get_error (parser) != NULL) {
181		fprintf (out, "Error occurred (phase 1): %s\n",
182						ucl_parser_get_error(parser));
183		ret = 1;
184		goto end;
185	}
186
187	obj = ucl_parser_get_object (parser);
188
189	if (save_comments) {
190		comments = ucl_object_ref (ucl_parser_get_comments (parser));
191	}
192
193	if (json) {
194		if (compact) {
195			emitted = ucl_object_emit (obj, UCL_EMIT_JSON_COMPACT);
196		}
197		else {
198			emitted = ucl_object_emit (obj, UCL_EMIT_JSON);
199		}
200	}
201	else if (yaml) {
202		emitted = ucl_object_emit (obj, UCL_EMIT_YAML);
203	}
204	else {
205		emitted = NULL;
206		func = ucl_object_emit_memory_funcs ((void **)&emitted);
207
208		if (func != NULL) {
209			ucl_object_emit_full (obj, UCL_EMIT_CONFIG, func, comments);
210			ucl_object_emit_funcs_free (func);
211		}
212	}
213
214#if 0
215	fprintf (out, "%s\n****\n", emitted);
216#endif
217
218	ucl_parser_free (parser);
219	ucl_object_unref (obj);
220	parser2 = ucl_parser_new (flags);
221	ucl_parser_add_string (parser2, (const char *)emitted, 0);
222
223	if (ucl_parser_get_error(parser2) != NULL) {
224		fprintf (out, "Error occurred (phase 2): %s\n",
225				ucl_parser_get_error(parser2));
226		fprintf (out, "%s\n", emitted);
227		ret = 1;
228		goto end;
229	}
230
231	if (emitted != NULL) {
232		free (emitted);
233	}
234	if (comments) {
235		ucl_object_unref (comments);
236		comments = NULL;
237	}
238
239	if (save_comments) {
240		comments = ucl_object_ref (ucl_parser_get_comments (parser2));
241	}
242
243	obj = ucl_parser_get_object (parser2);
244
245	if (!use_fd) {
246		func = ucl_object_emit_file_funcs (out);
247	}
248	else {
249		func = ucl_object_emit_fd_funcs (fd_out);
250	}
251
252	if (func != NULL) {
253		if (json) {
254			if (compact) {
255				ucl_object_emit_full (obj, UCL_EMIT_JSON_COMPACT,
256						func, comments);
257			}
258			else {
259				ucl_object_emit_full (obj, UCL_EMIT_JSON,
260						func, comments);
261			}
262		}
263		else if (yaml) {
264			ucl_object_emit_full (obj, UCL_EMIT_YAML,
265					func, comments);
266		}
267		else {
268			ucl_object_emit_full (obj, UCL_EMIT_CONFIG,
269					func, comments);
270		}
271
272		ucl_object_emit_funcs_free (func);
273	}
274
275	if (!use_fd) {
276		fprintf (out, "\n");
277		fclose (out);
278	}
279	else {
280		write (fd_out, "\n", 1);
281		close (fd_out);
282	}
283
284	ucl_object_unref (obj);
285
286end:
287	if (parser2 != NULL) {
288		ucl_parser_free (parser2);
289	}
290	if (comments) {
291		ucl_object_unref (comments);
292	}
293	if (inbuf != NULL) {
294		free (inbuf);
295	}
296
297	return ret;
298}
299