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