1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to you under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  * https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14  * implied.  See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 
18 #include <ctype.h>
19 #include <errno.h>
20 #include <getopt.h>
21 #include <avro/platform.h>
22 #include <avro/platform.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "avro.h"
28 #include "avro_private.h"
MoveTracksCommand(Composition * composition,TrackId srcTrack,TrackId destTrack)29 
30 
31 /* The path separator to use in the JSON output. */
32 
33 static const char  *separator = "/";
34 
35 
36 /*-- PROCESSING A FILE --*/
37 
~MoveTracksCommand()38 /**
39  * Fills in a raw string with the path to an element of an array.
40  */
41 
42 static void
43 create_array_prefix(avro_raw_string_t *dest, const char *prefix, size_t index)
44 {
45 	static char  buf[100];
46 	snprintf(buf, sizeof(buf), "%" PRIsz, index);
47 	avro_raw_string_set(dest, prefix);
48 	avro_raw_string_append(dest, separator);
49 	avro_raw_string_append(dest, buf);
50 }
51 
52 static void
53 create_object_prefix(avro_raw_string_t *dest, const char *prefix, const char *key)
54 {
55 	/*
56 	 * Make sure that the key doesn't contain the separator
57 	 * character.
unexecute()58 	 */
59 
60 	if (strstr(key, separator) != NULL) {
61 		fprintf(stderr,
62 			"Error: Element \"%s\" in object %s "
63 			"contains the separator character.\n"
64 			"Please use the --separator option to choose another.\n",
65 			key, prefix);
66 		exit(1);
67 	}
68 
69 	avro_raw_string_set(dest, prefix);
70 	avro_raw_string_append(dest, separator);
71 	avro_raw_string_append(dest, key);
72 }
73 
74 static void
75 print_bytes_value(const char *buf, size_t size)
76 {
77 	size_t  i;
78 	printf("\"");
79 	for (i = 0; i < size; i++)
80 	{
81 		if (buf[i] == '"') {
82 			printf("\\\"");
83 		} else if (buf[i] == '\\') {
84 			printf("\\\\");
85 		} else if (buf[i] == '\b') {
86 			printf("\\b");
87 		} else if (buf[i] == '\f') {
88 			printf("\\f");
89 		} else if (buf[i] == '\n') {
90 			printf("\\n");
91 		} else if (buf[i] == '\r') {
92 			printf("\\r");
93 		} else if (buf[i] == '\t') {
94 			printf("\\t");
95 		} else if (isprint(buf[i])) {
96 			printf("%c", (int) buf[i]);
97 		} else {
98 			printf("\\u00%02x", (unsigned int) (unsigned char) buf[i]);
99 		}
100 	}
101 	printf("\"");
102 }
103 
104 static void
105 process_value(const char *prefix, avro_value_t *value);
106 
107 static void
108 process_array(const char *prefix, avro_value_t *value)
109 {
110 	printf("%s\t[]\n", prefix);
111 	size_t  element_count;
112 	avro_value_get_size(value, &element_count);
113 
114 	avro_raw_string_t  element_prefix;
115 	avro_raw_string_init(&element_prefix);
116 
117 	size_t  i;
118 	for (i = 0; i < element_count; i++) {
119 		avro_value_t  element_value;
120 		avro_value_get_by_index(value, i, &element_value, NULL);
121 
122 		create_array_prefix(&element_prefix, prefix, i);
123 		process_value((const char *) avro_raw_string_get(&element_prefix), &element_value);
124 	}
125 
126 	avro_raw_string_done(&element_prefix);
127 }
128 
129 static void
130 process_enum(const char *prefix, avro_value_t *value)
131 {
132 	int  val;
133 	const char  *symbol_name;
134 
135 	avro_schema_t  schema = avro_value_get_schema(value);
136 	avro_value_get_enum(value, &val);
137 	symbol_name = avro_schema_enum_get(schema, val);
138 	printf("%s\t", prefix);
139 	print_bytes_value(symbol_name, strlen(symbol_name));
140 	printf("\n");
141 }
142 
143 static void
144 process_map(const char *prefix, avro_value_t *value)
145 {
146 	printf("%s\t{}\n", prefix);
147 	size_t  element_count;
148 	avro_value_get_size(value, &element_count);
149 
150 	avro_raw_string_t  element_prefix;
151 	avro_raw_string_init(&element_prefix);
152 
153 	size_t  i;
154 	for (i = 0; i < element_count; i++) {
155 		const char  *key;
156 		avro_value_t  element_value;
157 		avro_value_get_by_index(value, i, &element_value, &key);
158 
159 		create_object_prefix(&element_prefix, prefix, key);
160 		process_value((const char *) avro_raw_string_get(&element_prefix), &element_value);
161 	}
162 
163 	avro_raw_string_done(&element_prefix);
164 }
165 
166 static void
167 process_record(const char *prefix, avro_value_t *value)
168 {
169 	printf("%s\t{}\n", prefix);
170 	size_t  field_count;
171 	avro_value_get_size(value, &field_count);
172 
173 	avro_raw_string_t  field_prefix;
174 	avro_raw_string_init(&field_prefix);
175 
176 	size_t  i;
177 	for (i = 0; i < field_count; i++) {
178 		avro_value_t  field_value;
179 		const char  *field_name;
180 		avro_value_get_by_index(value, i, &field_value, &field_name);
181 
182 		create_object_prefix(&field_prefix, prefix, field_name);
183 		process_value((const char *) avro_raw_string_get(&field_prefix), &field_value);
184 	}
185 
186 	avro_raw_string_done(&field_prefix);
187 }
188 
189 static void
190 process_union(const char *prefix, avro_value_t *value)
191 {
192 	avro_value_t  branch_value;
193 	avro_value_get_current_branch(value, &branch_value);
194 
195 	/* nulls in a union aren't wrapped in a JSON object */
196 	if (avro_value_get_type(&branch_value) == AVRO_NULL) {
197 		printf("%s\tnull\n", prefix);
198 		return;
199 	}
200 
201 	int  discriminant;
202 	avro_value_get_discriminant(value, &discriminant);
203 
204 	avro_schema_t  schema = avro_value_get_schema(value);
205 	avro_schema_t  branch_schema = avro_schema_union_branch(schema, discriminant);
206 	const char  *branch_name = avro_schema_type_name(branch_schema);
207 
208 	avro_raw_string_t  branch_prefix;
209 	avro_raw_string_init(&branch_prefix);
210 	create_object_prefix(&branch_prefix, prefix, branch_name);
211 
212 	printf("%s\t{}\n", prefix);
213 	process_value((const char *) avro_raw_string_get(&branch_prefix), &branch_value);
214 
215 	avro_raw_string_done(&branch_prefix);
216 }
217 
218 static void
219 process_value(const char *prefix, avro_value_t *value)
220 {
221 	avro_type_t  type = avro_value_get_type(value);
222 	switch (type) {
223 		case AVRO_BOOLEAN:
224 		{
225 			int  val;
226 			avro_value_get_boolean(value, &val);
227 			printf("%s\t%s\n", prefix, val? "true": "false");
228 			return;
229 		}
230 
231 		case AVRO_BYTES:
232 		{
233 			const void  *buf;
234 			size_t  size;
235 			avro_value_get_bytes(value, &buf, &size);
236 			printf("%s\t", prefix);
237 			print_bytes_value((const char *) buf, size);
238 			printf("\n");
239 			return;
240 		}
241 
242 		case AVRO_DOUBLE:
243 		{
244 			double  val;
245 			avro_value_get_double(value, &val);
246 			printf("%s\t%lf\n", prefix, val);
247 			return;
248 		}
249 
250 		case AVRO_FLOAT:
251 		{
252 			float  val;
253 			avro_value_get_float(value, &val);
254 			printf("%s\t%f\n", prefix, val);
255 			return;
256 		}
257 
258 		case AVRO_INT32:
259 		{
260 			int32_t  val;
261 			avro_value_get_int(value, &val);
262 			printf("%s\t%" PRId32 "\n", prefix, val);
263 			return;
264 		}
265 
266 		case AVRO_INT64:
267 		{
268 			int64_t  val;
269 			avro_value_get_long(value, &val);
270 			printf("%s\t%" PRId64 "\n", prefix, val);
271 			return;
272 		}
273 
274 		case AVRO_NULL:
275 		{
276 			avro_value_get_null(value);
277 			printf("%s\tnull\n", prefix);
278 			return;
279 		}
280 
281 		case AVRO_STRING:
282 		{
283 			/* TODO: Convert the UTF-8 to the current
284 			 * locale's character set */
285 			const char  *buf;
286 			size_t  size;
287 			avro_value_get_string(value, &buf, &size);
288 			printf("%s\t", prefix);
289                         /* For strings, size includes the NUL terminator. */
290 			print_bytes_value(buf, size-1);
291 			printf("\n");
292 			return;
293 		}
294 
295 		case AVRO_ARRAY:
296 			process_array(prefix, value);
297 			return;
298 
299 		case AVRO_ENUM:
300 			process_enum(prefix, value);
301 			return;
302 
303 		case AVRO_FIXED:
304 		{
305 			const void  *buf;
306 			size_t  size;
307 			avro_value_get_fixed(value, &buf, &size);
308 			printf("%s\t", prefix);
309 			print_bytes_value((const char *) buf, size);
310 			printf("\n");
311 			return;
312 		}
313 
314 		case AVRO_MAP:
315 			process_map(prefix, value);
316 			return;
317 
318 		case AVRO_RECORD:
319 			process_record(prefix, value);
320 			return;
321 
322 		case AVRO_UNION:
323 			process_union(prefix, value);
324 			return;
325 
326 		default:
327 		{
328 			fprintf(stderr, "Unknown schema type\n");
329 			exit(1);
330 		}
331 	}
332 }
333 
334 static void
335 process_file(const char *filename)
336 {
337 	avro_file_reader_t  reader;
338 
339 	if (filename == NULL) {
340 		if (avro_file_reader_fp(stdin, "<stdin>", 0, &reader)) {
341 			fprintf(stderr, "Error opening <stdin>:\n  %s\n",
342 				avro_strerror());
343 			exit(1);
344 		}
345 	} else {
346 		if (avro_file_reader(filename, &reader)) {
347 			fprintf(stderr, "Error opening %s:\n  %s\n",
348 				filename, avro_strerror());
349 			exit(1);
350 		}
351 	}
352 
353 	/* The JSON root is an array */
354 	printf("%s\t[]\n", separator);
355 
356 	avro_raw_string_t  prefix;
357 	avro_raw_string_init(&prefix);
358 
359 	avro_schema_t  wschema = avro_file_reader_get_writer_schema(reader);
360 	avro_value_iface_t  *iface = avro_generic_class_from_schema(wschema);
361 	avro_value_t  value;
362 	avro_generic_value_new(iface, &value);
363 
364 	size_t  record_number = 0;
365 	int rval;
366 
367 	for (; (rval = avro_file_reader_read_value(reader, &value)) == 0; record_number++) {
368 		create_array_prefix(&prefix, "", record_number);
369 		process_value((const char *) avro_raw_string_get(&prefix), &value);
370 		avro_value_reset(&value);
371 	}
372 
373 	if (rval != EOF) {
374 		fprintf(stderr, "Error reading value: %s", avro_strerror());
375 	}
376 
377 	avro_raw_string_done(&prefix);
378 	avro_value_decref(&value);
379 	avro_value_iface_decref(iface);
380 	avro_file_reader_close(reader);
381 	avro_schema_decref(wschema);
382 }
383 
384 
385 /*-- MAIN PROGRAM --*/
386 static struct option longopts[] = {
387 	{ "separator", required_argument, NULL, 's' },
388 	{ NULL, 0, NULL, 0 }
389 };
390 
391 static void usage(void)
392 {
393 	fprintf(stderr,
394 		"Usage: avropipe [--separator=<separator>]\n"
395 		"                <avro data file>\n");
396 }
397 
398 
399 int main(int argc, char **argv)
400 {
401 	char  *data_filename;
402 
403 	int  ch;
404 	while ((ch = getopt_long(argc, argv, "s:", longopts, NULL) ) != -1) {
405 		switch (ch) {
406 			case 's':
407 				separator = optarg;
408 				break;
409 
410 			default:
411 				usage();
412 				exit(1);
413 		}
414 	}
415 
416 	argc -= optind;
417 	argv += optind;
418 
419 	if (argc == 1) {
420 		data_filename = argv[0];
421 	} else if (argc == 0) {
422 		data_filename = NULL;
423 	} else {
424 		fprintf(stderr, "Can't read from multiple input files.\n");
425 		usage();
426 		exit(1);
427 	}
428 
429 	/* Process the data file */
430 	process_file(data_filename);
431 	return 0;
432 }
433