Fix: params arg parsing: apply to current component params
[babeltrace.git] / converter / babeltrace-cfg.c
CommitLineData
c42c79ea
PP
1/*
2 * Babeltrace trace converter - parameter parsing
3 *
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#include <errno.h>
26#include <stdlib.h>
27#include <string.h>
28#include <assert.h>
29#include <stdio.h>
30#include <stdbool.h>
31#include <inttypes.h>
32#include <babeltrace/babeltrace.h>
33#include <babeltrace/values.h>
34#include <popt.h>
35#include <glib.h>
36#include "babeltrace-cfg.h"
37
65582340
MD
38#define DEFAULT_SOURCE_COMPONENT_NAME "ctf.fs"
39
c42c79ea
PP
40/*
41 * Error printf() macro which prepends "Error: " the first time it's
42 * called. This gives a nicer feel than having a bunch of error prefixes
43 * (since the following lines usually describe the error and possible
44 * solutions), or the error prefix just at the end.
45 */
46#define printf_err(fmt, args...) \
47 do { \
48 if (is_first_error) { \
49 fprintf(stderr, "Error: "); \
50 is_first_error = false; \
51 } \
52 fprintf(stderr, fmt, ##args); \
53 } while (0)
54
55static bool is_first_error = true;
56
57/* INI-style parsing FSM states */
58enum ini_parsing_fsm_state {
59 /* Expect a map key (identifier) */
60 INI_EXPECT_MAP_KEY,
61
62 /* Expect an equal character ('=') */
63 INI_EXPECT_EQUAL,
64
65 /* Expect a value */
66 INI_EXPECT_VALUE,
67
68 /* Expect a negative number value */
69 INI_EXPECT_VALUE_NUMBER_NEG,
70
71 /* Expect a comma character (',') */
72 INI_EXPECT_COMMA,
73};
74
75/* INI-style parsing state variables */
76struct ini_parsing_state {
77 /* Lexical scanner (owned by this) */
78 GScanner *scanner;
79
80 /* Output map value object being filled (owned by this) */
81 struct bt_value *params;
82
83 /* Next expected FSM state */
84 enum ini_parsing_fsm_state expecting;
85
86 /* Last decoded map key (owned by this) */
87 char *last_map_key;
88
89 /* Complete INI-style string to parse (not owned by this) */
90 const char *arg;
91
92 /* Error buffer (not owned by this) */
93 GString *ini_error;
94};
95
96/* Offset option with "is set" boolean */
97struct offset_opt {
98 int64_t value;
99 bool is_set;
100};
101
102/* Legacy "ctf"/"lttng-live" format options */
103struct ctf_legacy_opts {
104 struct offset_opt offset_s;
105 struct offset_opt offset_ns;
106 bool stream_intersection;
107};
108
109/* Legacy "text" format options */
110struct text_legacy_opts {
111 /*
112 * output, dbg_info_dir, dbg_info_target_prefix, names,
113 * and fields are owned by this.
114 */
115 GString *output;
116 GString *dbg_info_dir;
117 GString *dbg_info_target_prefix;
118 struct bt_value *names;
119 struct bt_value *fields;
120
121 /* Flags */
122 bool no_delta;
123 bool clock_cycles;
124 bool clock_seconds;
125 bool clock_date;
126 bool clock_gmt;
127 bool dbg_info_full_path;
128};
129
130/* Legacy input format format */
131enum legacy_input_format {
132 LEGACY_INPUT_FORMAT_NONE = 0,
133 LEGACY_INPUT_FORMAT_CTF,
134 LEGACY_INPUT_FORMAT_LTTNG_LIVE,
135};
136
137/* Legacy output format format */
138enum legacy_output_format {
139 LEGACY_OUTPUT_FORMAT_NONE = 0,
140 LEGACY_OUTPUT_FORMAT_TEXT,
141 LEGACY_OUTPUT_FORMAT_CTF_METADATA,
142 LEGACY_OUTPUT_FORMAT_DUMMY,
143};
144
145/*
146 * Prints the "out of memory" error.
147 */
148static
149void print_err_oom(void)
150{
151 printf_err("Out of memory\n");
152}
153
154/*
155 * Prints duplicate legacy output format error.
156 */
157static
158void print_err_dup_legacy_output(void)
159{
160 printf_err("More than one legacy output format specified\n");
161}
162
163/*
164 * Prints duplicate legacy input format error.
165 */
166static
167void print_err_dup_legacy_input(void)
168{
169 printf_err("More than one legacy input format specified\n");
170}
171
172/*
173 * Checks if any of the "text" legacy options is set.
174 */
175static
176bool text_legacy_opts_is_any_set(struct text_legacy_opts *opts)
177{
178 return (opts->output && opts->output->len > 0) ||
179 (opts->dbg_info_dir && opts->dbg_info_dir->len > 0) ||
180 (opts->dbg_info_target_prefix &&
181 opts->dbg_info_target_prefix->len > 0) ||
182 bt_value_array_size(opts->names) > 0 ||
183 bt_value_array_size(opts->fields) > 0 ||
184 opts->no_delta || opts->clock_cycles || opts->clock_seconds ||
185 opts->clock_date || opts->clock_gmt ||
186 opts->dbg_info_full_path;
187}
188
189/*
190 * Checks if any of the "ctf" legacy options is set.
191 */
192static
193bool ctf_legacy_opts_is_any_set(struct ctf_legacy_opts *opts)
194{
195 return opts->offset_s.is_set || opts->offset_ns.is_set ||
196 opts->stream_intersection;
197}
198
199/*
200 * Appends an "expecting token" error to the INI-style parsing state's
201 * error buffer.
202 */
203static
204void ini_append_error_expecting(struct ini_parsing_state *state,
205 GScanner *scanner, const char *expecting)
206{
207 size_t i;
208 size_t pos;
209
210 g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting);
211
212 /* Only print error if there's one line */
213 if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) {
214 return;
215 }
216
217 g_string_append_printf(state->ini_error, "\n %s\n", state->arg);
218 pos = g_scanner_cur_position(scanner) + 4;
219
220 if (!g_scanner_eof(scanner)) {
221 pos--;
222 }
223
224 for (i = 0; i < pos; ++i) {
225 g_string_append_printf(state->ini_error, " ");
226 }
227
228 g_string_append_printf(state->ini_error, "^\n\n");
229}
230
231static
232int ini_handle_state(struct ini_parsing_state *state)
233{
234 int ret = 0;
235 GTokenType token_type;
236 struct bt_value *value = NULL;
237
238 token_type = g_scanner_get_next_token(state->scanner);
239 if (token_type == G_TOKEN_EOF) {
240 if (state->expecting != INI_EXPECT_COMMA) {
241 switch (state->expecting) {
242 case INI_EXPECT_EQUAL:
243 ini_append_error_expecting(state,
244 state->scanner, "'='");
245 break;
246 case INI_EXPECT_VALUE:
247 case INI_EXPECT_VALUE_NUMBER_NEG:
248 ini_append_error_expecting(state,
249 state->scanner, "value");
250 break;
251 case INI_EXPECT_MAP_KEY:
252 ini_append_error_expecting(state,
253 state->scanner, "unquoted map key");
254 break;
255 default:
256 break;
257 }
258 goto error;
259 }
260
261 /* We're done! */
262 ret = 1;
263 goto success;
264 }
265
266 switch (state->expecting) {
267 case INI_EXPECT_MAP_KEY:
268 if (token_type != G_TOKEN_IDENTIFIER) {
269 ini_append_error_expecting(state, state->scanner,
270 "unquoted map key");
271 goto error;
272 }
273
274 free(state->last_map_key);
275 state->last_map_key =
276 strdup(state->scanner->value.v_identifier);
277 if (!state->last_map_key) {
278 g_string_append(state->ini_error,
279 "Out of memory\n");
280 goto error;
281 }
282
283 if (bt_value_map_has_key(state->params, state->last_map_key)) {
284 g_string_append_printf(state->ini_error,
6d1d5711 285 "Duplicate parameter key: `%s`\n",
c42c79ea
PP
286 state->last_map_key);
287 goto error;
288 }
289
290 state->expecting = INI_EXPECT_EQUAL;
291 goto success;
292 case INI_EXPECT_EQUAL:
293 if (token_type != G_TOKEN_CHAR) {
294 ini_append_error_expecting(state,
295 state->scanner, "'='");
296 goto error;
297 }
298
299 if (state->scanner->value.v_char != '=') {
300 ini_append_error_expecting(state,
301 state->scanner, "'='");
302 goto error;
303 }
304
305 state->expecting = INI_EXPECT_VALUE;
306 goto success;
307 case INI_EXPECT_VALUE:
308 {
309 switch (token_type) {
310 case G_TOKEN_CHAR:
311 if (state->scanner->value.v_char == '-') {
312 /* Negative number */
313 state->expecting =
314 INI_EXPECT_VALUE_NUMBER_NEG;
315 goto success;
316 } else {
317 ini_append_error_expecting(state,
318 state->scanner, "value");
319 goto error;
320 }
321 break;
322 case G_TOKEN_INT:
323 {
324 /* Positive integer */
325 uint64_t int_val = state->scanner->value.v_int64;
326
327 if (int_val > (1ULL << 63) - 1) {
328 g_string_append_printf(state->ini_error,
329 "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n",
330 int_val);
331 goto error;
332 }
333
334 value = bt_value_integer_create_init(
335 (int64_t) int_val);
336 break;
337 }
338 case G_TOKEN_FLOAT:
339 /* Positive floating point number */
340 value = bt_value_float_create_init(
341 state->scanner->value.v_float);
342 break;
343 case G_TOKEN_STRING:
344 /* Quoted string */
345 value = bt_value_string_create_init(
346 state->scanner->value.v_string);
347 break;
348 case G_TOKEN_IDENTIFIER:
349 {
350 /*
351 * Using symbols would be appropriate here,
352 * but said symbols are allowed as map key,
353 * so it's easier to consider everything an
354 * identifier.
355 *
356 * If one of the known symbols is not
357 * recognized here, then fall back to creating
358 * a string value.
359 */
360 const char *id = state->scanner->value.v_identifier;
361
362 if (!strcmp(id, "null") || !strcmp(id, "NULL") ||
363 !strcmp(id, "nul")) {
364 value = bt_value_null;
365 } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") ||
366 !strcmp(id, "yes") ||
367 !strcmp(id, "YES")) {
368 value = bt_value_bool_create_init(true);
369 } else if (!strcmp(id, "false") ||
370 !strcmp(id, "FALSE") ||
371 !strcmp(id, "no") ||
372 !strcmp(id, "NO")) {
373 value = bt_value_bool_create_init(false);
374 } else {
375 value = bt_value_string_create_init(id);
376 }
377 break;
378 }
379 default:
380 /* Unset value variable will trigger the error */
381 break;
382 }
383
384 if (!value) {
385 ini_append_error_expecting(state,
386 state->scanner, "value");
387 goto error;
388 }
389
390 state->expecting = INI_EXPECT_COMMA;
391 goto success;
392 }
393 case INI_EXPECT_VALUE_NUMBER_NEG:
394 {
395 switch (token_type) {
396 case G_TOKEN_INT:
397 {
398 /* Negative integer */
399 uint64_t int_val = state->scanner->value.v_int64;
400
401 if (int_val > (1ULL << 63) - 1) {
402 g_string_append_printf(state->ini_error,
403 "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n",
404 int_val);
405 goto error;
406 }
407
408 value = bt_value_integer_create_init(
409 -((int64_t) int_val));
410 break;
411 }
412 case G_TOKEN_FLOAT:
413 /* Negative floating point number */
414 value = bt_value_float_create_init(
415 -state->scanner->value.v_float);
416 break;
417 default:
418 /* Unset value variable will trigger the error */
419 break;
420 }
421
422 if (!value) {
423 ini_append_error_expecting(state,
424 state->scanner, "value");
425 goto error;
426 }
427
428 state->expecting = INI_EXPECT_COMMA;
429 goto success;
430 }
431 case INI_EXPECT_COMMA:
432 if (token_type != G_TOKEN_CHAR) {
433 ini_append_error_expecting(state,
434 state->scanner, "','");
435 goto error;
436 }
437
438 if (state->scanner->value.v_char != ',') {
439 ini_append_error_expecting(state,
440 state->scanner, "','");
441 goto error;
442 }
443
444 state->expecting = INI_EXPECT_MAP_KEY;
445 goto success;
446 default:
447 assert(false);
448 }
449
450error:
451 ret = -1;
452 goto end;
453
454success:
455 if (value) {
456 if (bt_value_map_insert(state->params,
457 state->last_map_key, value)) {
458 /* Only override return value on error */
459 ret = -1;
460 }
461 }
462
463end:
464 BT_PUT(value);
465 return ret;
466}
467
468/*
469 * Converts an INI-style argument to an equivalent map value object.
470 *
471 * Return value is owned by the caller.
472 */
473static
474struct bt_value *bt_value_from_ini(const char *arg, GString *ini_error)
475{
476 /* Lexical scanner configuration */
477 GScannerConfig scanner_config = {
478 /* Skip whitespaces */
479 .cset_skip_characters = " \t\n",
480
481 /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */
482 .cset_identifier_first =
483 G_CSET_a_2_z
484 "_"
485 G_CSET_A_2_Z,
486 .cset_identifier_nth =
487 G_CSET_a_2_z
488 "_0123456789-.:"
489 G_CSET_A_2_Z,
490
491 /* "hello" and "Hello" two different keys */
492 .case_sensitive = TRUE,
493
494 /* No comments */
495 .cpair_comment_single = NULL,
496 .skip_comment_multi = TRUE,
497 .skip_comment_single = TRUE,
498 .scan_comment_multi = FALSE,
499
500 /*
501 * Do scan identifiers, including 1-char identifiers,
502 * but NULL is a normal identifier.
503 */
504 .scan_identifier = TRUE,
505 .scan_identifier_1char = TRUE,
506 .scan_identifier_NULL = FALSE,
507
508 /*
509 * No specific symbols: null and boolean "symbols" are
510 * scanned as plain identifiers.
511 */
512 .scan_symbols = FALSE,
513 .symbol_2_token = FALSE,
514 .scope_0_fallback = FALSE,
515
516 /*
517 * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not
518 * integers prefixed with "$".
519 */
520 .scan_binary = TRUE,
521 .scan_octal = TRUE,
522 .scan_float = TRUE,
523 .scan_hex = TRUE,
524 .scan_hex_dollar = FALSE,
525
526 /* Convert scanned numbers to integer tokens */
527 .numbers_2_int = TRUE,
528
529 /* Support both integers and floating-point numbers */
530 .int_2_float = FALSE,
531
532 /* Scan integers as 64-bit signed integers */
533 .store_int64 = TRUE,
534
535 /* Only scan double-quoted strings */
536 .scan_string_sq = FALSE,
537 .scan_string_dq = TRUE,
538
539 /* Do not converter identifiers to string tokens */
540 .identifier_2_string = FALSE,
541
542 /* Scan characters as G_TOKEN_CHAR token */
543 .char_2_token = FALSE,
544 };
545 struct ini_parsing_state state = {
546 .scanner = NULL,
547 .params = NULL,
548 .expecting = INI_EXPECT_MAP_KEY,
549 .arg = arg,
550 .ini_error = ini_error,
551 };
552
553 state.params = bt_value_map_create();
554 if (!state.params) {
555 goto error;
556 }
557
558 state.scanner = g_scanner_new(&scanner_config);
559 if (!state.scanner) {
560 goto error;
561 }
562
563 /* Let the scan begin */
564 g_scanner_input_text(state.scanner, arg, strlen(arg));
565
566 while (true) {
567 int ret = ini_handle_state(&state);
568
569 if (ret < 0) {
570 /* Error */
571 goto error;
572 } else if (ret > 0) {
573 /* Done */
574 break;
575 }
576 }
577
578 goto end;
579
580error:
581 BT_PUT(state.params);
582
583end:
584 if (state.scanner) {
585 g_scanner_destroy(state.scanner);
586 }
587
588 free(state.last_map_key);
589 return state.params;
590}
591
592/*
593 * Returns the parameters map value object from a command-line
bdc61c70 594 * parameter option's argument.
c42c79ea
PP
595 *
596 * Return value is owned by the caller.
597 */
598static
599struct bt_value *bt_value_from_arg(const char *arg)
600{
601 struct bt_value *params = NULL;
c42c79ea
PP
602 GString *ini_error = NULL;
603
c42c79ea
PP
604 ini_error = g_string_new(NULL);
605 if (!ini_error) {
606 print_err_oom();
607 goto end;
608 }
609
610 /* Try INI-style parsing */
bdc61c70 611 params = bt_value_from_ini(arg, ini_error);
c42c79ea
PP
612 if (!params) {
613 printf_err("%s", ini_error->str);
614 goto end;
615 }
616
617end:
618 if (ini_error) {
619 g_string_free(ini_error, TRUE);
620 }
621 return params;
622}
623
624/*
625 * Returns the plugin and component names from a command-line
bdc61c70
PP
626 * source/sink option's argument. arg must have the following format:
627 *
628 * PLUGIN.COMPONENT
629 *
630 * where PLUGIN is the plugin name, and COMPONENT is the component
631 * name.
c42c79ea
PP
632 *
633 * On success, both *plugin and *component are not NULL. *plugin
634 * and *component are owned by the caller.
635 */
636static
637void plugin_component_names_from_arg(const char *arg, char **plugin,
638 char **component)
639{
640 const char *dot;
bdc61c70 641 const char *end;
c42c79ea
PP
642 size_t plugin_len;
643 size_t component_len;
644
bdc61c70 645 /* Initialize both return values to NULL: not found */
c42c79ea
PP
646 *plugin = NULL;
647 *component = NULL;
648
649 dot = strchr(arg, '.');
bdc61c70
PP
650 if (!dot) {
651 /* No dot */
c42c79ea
PP
652 goto end;
653 }
654
bdc61c70 655 end = arg + strlen(arg);
c42c79ea 656 plugin_len = dot - arg;
bdc61c70 657 component_len = end - dot - 1;
c42c79ea
PP
658 if (plugin_len == 0 || component_len == 0) {
659 goto end;
660 }
661
bdc61c70 662 *plugin = g_malloc0(plugin_len + 1);
c42c79ea
PP
663 if (!*plugin) {
664 print_err_oom();
665 goto end;
666 }
667
bdc61c70
PP
668 g_strlcpy(*plugin, arg, plugin_len + 1);
669 *component = g_malloc0(component_len + 1);
c42c79ea
PP
670 if (!*component) {
671 print_err_oom();
672 goto end;
673 }
674
bdc61c70 675 g_strlcpy(*component, dot + 1, component_len + 1);
c42c79ea
PP
676
677end:
678 return;
679}
680
681/*
682 * Prints the Babeltrace version.
683 */
684static
685void print_version(void)
686{
687 puts("Babeltrace " VERSION);
688}
689
690/*
691 * Prints the legacy, Babeltrace 1.x command usage. Those options are
692 * still compatible in Babeltrace 2.x, but it is recommended to use
693 * the more generic plugin/component parameters instead of those
694 * hard-coded option names.
695 */
696static
697void print_legacy_usage(FILE *fp)
698{
699 fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n");
700 fprintf(fp, "\n");
701 fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n");
702 fprintf(fp, "\n");
703 fprintf(fp, " --help-legacy Show this help\n");
704 fprintf(fp, " -V, --version Show version\n");
705 fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n");
706 fprintf(fp, " across traces\n");
707 fprintf(fp, " -d, --debug Enable debug mode\n");
708 fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n");
709 fprintf(fp, " -l, --list List available formats\n");
710 fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n");
711 fprintf(fp, " -v, --verbose Enable verbose output\n");
712 fprintf(fp, "\n");
713 fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n");
714 fprintf(fp, " Available output formats: text, dummy\n");
715 fprintf(fp, "\n");
716 fprintf(fp, "Input formats specific options:\n");
717 fprintf(fp, "\n");
718 fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n");
719 fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n");
720 fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n");
721 fprintf(fp, " --stream-intersection Only process events when all streams are active\n");
722 fprintf(fp, "\n");
723 fprintf(fp, "text output format specific options:\n");
724 fprintf(fp, " \n");
725 fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n");
726 fprintf(fp, " --clock-date Print timestamp dates\n");
528debdf 727 fprintf(fp, " --clock-gmt Print and parse timestamps in GMT time zone\n");
c42c79ea
PP
728 fprintf(fp, " (default: local time zone)\n");
729 fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n");
730 fprintf(fp, " (default format: [HH:MM:SS.NS])\n");
731 fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n");
6d1d5711 732 fprintf(fp, " (default: `/usr/lib/debug`)\n");
c42c79ea
PP
733 fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n");
734 fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n");
735 fprintf(fp, " up executables during debug info analysis\n");
6d1d5711 736 fprintf(fp, " (default: `/usr/lib/debug`)\n");
c42c79ea
PP
737 fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n");
738 fprintf(fp, " all, trace, trace:hostname, trace:domain,\n");
d9b99e4e 739 fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n");
c42c79ea
PP
740 fprintf(fp, " (default: trace:hostname, trace:procname,\n");
741 fprintf(fp, " trace:vpid)\n");
742 fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n");
743 fprintf(fp, " payload (or arg or args)\n");
744 fprintf(fp, " none, all, scope, header, context (or ctx)\n");
745 fprintf(fp, " (default: payload, context)\n");
746 fprintf(fp, " --no-delta Do not print time delta between consecutive\n");
747 fprintf(fp, " events\n");
748 fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n");
749}
750
751/*
752 * Prints the Babeltrace 2.x usage.
753 */
754static
755void print_usage(FILE *fp)
756{
757 fprintf(fp, "Usage: babeltrace [OPTIONS]\n");
758 fprintf(fp, "\n");
b07ffa28
PP
759 fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n");
760 fprintf(fp, " of the following source and sink component\n");
761 fprintf(fp, " instances (see the exact format of PARAMS\n");
762 fprintf(fp, " below)\n");
c42c79ea
PP
763 fprintf(fp, " -d, --debug Enable debug mode\n");
764 fprintf(fp, " -l, --list List available plugins and their components\n");
ad6a19bd
PP
765 fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest source\n");
766 fprintf(fp, " or sink component to PATH\n");
bdc61c70 767 fprintf(fp, " -p, --params=PARAMS Set the parameters of the latest source or\n");
b07ffa28
PP
768 fprintf(fp, " sink component instance (in command-line \n");
769 fprintf(fp, " order) to PARAMS (see the exact format of\n");
770 fprintf(fp, " PARAMS below)\n");
ad6a19bd 771 fprintf(fp, " --plugin-path=PATH[:PATH]... Set paths from which dynamic plugins can be\n");
015cee23 772 fprintf(fp, " loaded to PATH\n");
b07ffa28
PP
773 fprintf(fp, " -r, --reset-base-params Reset the current base parameters of the\n");
774 fprintf(fp, " following source and sink component\n");
775 fprintf(fp, " instances to an empty map\n");
015cee23
PP
776 fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n");
777 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
778 fprintf(fp, " repeated)\n");
779 fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n");
780 fprintf(fp, " PLUGIN and component class COMPCLS (may be\n");
781 fprintf(fp, " repeated)\n");
0f9915c6
MD
782 fprintf(fp, " --begin Start time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
783 fprintf(fp, " --end End time: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
784 fprintf(fp, " --timerange Time range: begin,end or [begin,end] (where [] are actual brackets)\n");
bdc61c70
PP
785 fprintf(fp, " -h --help Show this help\n");
786 fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy options\n");
c42c79ea
PP
787 fprintf(fp, " -v, --verbose Enable verbose output\n");
788 fprintf(fp, " -V, --version Show version\n");
bdc61c70 789 fprintf(fp, "\n\n");
b07ffa28
PP
790 fprintf(fp, "Format of PARAMS\n");
791 fprintf(fp, "----------------\n");
c42c79ea 792 fprintf(fp, "\n");
bdc61c70 793 fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n");
c42c79ea 794 fprintf(fp, "\n");
bdc61c70
PP
795 fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n");
796 fprintf(fp, "where PARAM is the parameter name (C identifier plus [:.-] characters), and\n");
797 fprintf(fp, "VALUE can be one of:\n");
c42c79ea 798 fprintf(fp, "\n");
bdc61c70
PP
799 fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n");
800 fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n");
801 fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n");
802 fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n");
803 fprintf(fp, " (`0x` prefix) signed 64-bit integer.\n");
804 fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n");
805 fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n");
806 fprintf(fp, " the null and boolean value symbols above.\n");
807 fprintf(fp, "* Double-quoted string (accepts escape characters).\n");
c42c79ea 808 fprintf(fp, "\n");
bdc61c70 809 fprintf(fp, "Whitespaces are allowed around individual `=` and `,` tokens.\n");
c42c79ea 810 fprintf(fp, "\n");
bdc61c70 811 fprintf(fp, "Example:\n");
c42c79ea 812 fprintf(fp, "\n");
bdc61c70
PP
813 fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n");
814 fprintf(fp, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n");
815 fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n");
c42c79ea 816 fprintf(fp, "\n");
bdc61c70
PP
817 fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n");
818 fprintf(fp, "from a shell.\n");
c42c79ea
PP
819}
820
821/*
822 * Destroys a component configuration.
823 */
824static
825void bt_config_component_destroy(struct bt_object *obj)
826{
827 struct bt_config_component *bt_config_component =
828 container_of(obj, struct bt_config_component, base);
829
830 if (!obj) {
831 goto end;
832 }
833
834 if (bt_config_component->plugin_name) {
835 g_string_free(bt_config_component->plugin_name, TRUE);
836 }
837
838 if (bt_config_component->component_name) {
839 g_string_free(bt_config_component->component_name, TRUE);
840 }
841
842 BT_PUT(bt_config_component->params);
843 g_free(bt_config_component);
844
845end:
846 return;
847}
848
849/*
bdc61c70
PP
850 * Creates a component configuration using the given plugin name and
851 * component name. plugin_name and component_name are copied (belong to
852 * the return value).
c42c79ea
PP
853 *
854 * Return value is owned by the caller.
855 */
856static
857struct bt_config_component *bt_config_component_create(const char *plugin_name,
bdc61c70 858 const char *component_name)
c42c79ea
PP
859{
860 struct bt_config_component *cfg_component = NULL;
861
862 cfg_component = g_new0(struct bt_config_component, 1);
863 if (!cfg_component) {
864 print_err_oom();
865 goto error;
866 }
867
868 bt_object_init(cfg_component, bt_config_component_destroy);
869 cfg_component->plugin_name = g_string_new(plugin_name);
870 if (!cfg_component->plugin_name) {
871 print_err_oom();
872 goto error;
873 }
874
875 cfg_component->component_name = g_string_new(component_name);
876 if (!cfg_component->component_name) {
877 print_err_oom();
878 goto error;
879 }
880
bdc61c70
PP
881 /* Start with empty parameters */
882 cfg_component->params = bt_value_map_create();
883 if (!cfg_component->params) {
884 print_err_oom();
885 goto error;
886 }
887
c42c79ea
PP
888 goto end;
889
890error:
891 BT_PUT(cfg_component);
892
893end:
894 return cfg_component;
895}
896
897/*
898 * Creates a component configuration from a command-line source/sink
bdc61c70 899 * option's argument.
c42c79ea
PP
900 */
901static
902struct bt_config_component *bt_config_component_from_arg(const char *arg)
903{
904 struct bt_config_component *bt_config_component = NULL;
905 char *plugin_name;
906 char *component_name;
c42c79ea
PP
907
908 plugin_component_names_from_arg(arg, &plugin_name, &component_name);
909 if (!plugin_name || !component_name) {
49849a47 910 printf_err("Cannot get plugin or component class name\n");
c42c79ea
PP
911 goto error;
912 }
913
c42c79ea 914 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 915 component_name);
c42c79ea
PP
916 if (!bt_config_component) {
917 goto error;
918 }
919
920 goto end;
921
922error:
923 BT_PUT(bt_config_component);
924
925end:
bdc61c70
PP
926 g_free(plugin_name);
927 g_free(component_name);
c42c79ea
PP
928 return bt_config_component;
929}
930
931/*
932 * Destroys a configuration.
933 */
934static
935void bt_config_destroy(struct bt_object *obj)
936{
937 struct bt_config *bt_config =
938 container_of(obj, struct bt_config, base);
939
940 if (!obj) {
941 goto end;
942 }
943
944 if (bt_config->sources) {
945 g_ptr_array_free(bt_config->sources, TRUE);
946 }
947
948 if (bt_config->sinks) {
949 g_ptr_array_free(bt_config->sinks, TRUE);
950 }
951
952 BT_PUT(bt_config->plugin_paths);
953 g_free(bt_config);
954
955end:
956 return;
957}
958
959/*
960 * Extracts the various paths from the string arg, delimited by ':',
961 * and converts them to an array value object.
962 *
963 * Returned array value object is empty if arg is empty.
964 *
965 * Return value is owned by the caller.
966 */
967static
968struct bt_value *plugin_paths_from_arg(const char *arg)
969{
970 struct bt_value *plugin_paths;
971 const char *at = arg;
972 const char *end = arg + strlen(arg);
973
974 plugin_paths = bt_value_array_create();
975 if (!plugin_paths) {
976 print_err_oom();
977 goto error;
978 }
979
980 while (at < end) {
981 int ret;
982 GString *path;
983 const char *next_colon;
984
985 next_colon = strchr(at, ':');
986 if (next_colon == at) {
987 /*
988 * Empty path: try next character (supported
989 * to conform to the typical parsing of $PATH).
990 */
991 at++;
992 continue;
993 } else if (!next_colon) {
994 /* No more colon: use the remaining */
995 next_colon = arg + strlen(arg);
996 }
997
998 path = g_string_new(NULL);
999 if (!path) {
1000 print_err_oom();
1001 goto error;
1002 }
1003
1004 g_string_append_len(path, at, next_colon - at);
1005 at = next_colon + 1;
1006 ret = bt_value_array_append_string(plugin_paths, path->str);
1007 g_string_free(path, TRUE);
1008 if (ret) {
1009 print_err_oom();
1010 goto error;
1011 }
1012 }
1013
1014 goto end;
1015
1016error:
1017 BT_PUT(plugin_paths);
1018
1019end:
1020 return plugin_paths;
1021}
1022
1023/*
1024 * Creates a simple lexical scanner for parsing comma-delimited names
1025 * and fields.
1026 *
1027 * Return value is owned by the caller.
1028 */
1029static
1030GScanner *create_csv_identifiers_scanner(void)
1031{
1032 GScannerConfig scanner_config = {
1033 .cset_skip_characters = " \t\n",
1034 .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_",
1035 .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-",
1036 .case_sensitive = TRUE,
1037 .cpair_comment_single = NULL,
1038 .skip_comment_multi = TRUE,
1039 .skip_comment_single = TRUE,
1040 .scan_comment_multi = FALSE,
1041 .scan_identifier = TRUE,
1042 .scan_identifier_1char = TRUE,
1043 .scan_identifier_NULL = FALSE,
1044 .scan_symbols = FALSE,
1045 .symbol_2_token = FALSE,
1046 .scope_0_fallback = FALSE,
1047 .scan_binary = FALSE,
1048 .scan_octal = FALSE,
1049 .scan_float = FALSE,
1050 .scan_hex = FALSE,
1051 .scan_hex_dollar = FALSE,
1052 .numbers_2_int = FALSE,
1053 .int_2_float = FALSE,
1054 .store_int64 = FALSE,
1055 .scan_string_sq = FALSE,
1056 .scan_string_dq = FALSE,
1057 .identifier_2_string = FALSE,
1058 .char_2_token = TRUE,
1059 };
1060
1061 return g_scanner_new(&scanner_config);
1062}
1063
6e1bc0df
MD
1064/*
1065 * Inserts a string (if exists and not empty) or null to a map value
1066 * object.
1067 */
1068static
1069enum bt_value_status map_insert_string_or_null(struct bt_value *map,
1070 const char *key, GString *string)
1071{
1072 enum bt_value_status ret;
1073
1074 if (string && string->len > 0) {
1075 ret = bt_value_map_insert_string(map, key, string->str);
1076 } else {
1077 ret = bt_value_map_insert(map, key, bt_value_null);
1078 }
1079 return ret;
1080}
1081
c42c79ea
PP
1082/*
1083 * Converts a comma-delimited list of known names (--names option) to
1084 * an array value object containing those names as string value objects.
1085 *
1086 * Return value is owned by the caller.
1087 */
1088static
1089struct bt_value *names_from_arg(const char *arg)
1090{
1091 GScanner *scanner = NULL;
1092 struct bt_value *names = NULL;
6e1bc0df 1093 bool found_all = false, found_none = false, found_item = false;
c42c79ea
PP
1094
1095 names = bt_value_array_create();
1096 if (!names) {
1097 print_err_oom();
1098 goto error;
1099 }
1100
1101 scanner = create_csv_identifiers_scanner();
1102 if (!scanner) {
1103 print_err_oom();
1104 goto error;
1105 }
1106
1107 g_scanner_input_text(scanner, arg, strlen(arg));
1108
1109 while (true) {
1110 GTokenType token_type = g_scanner_get_next_token(scanner);
1111
1112 switch (token_type) {
1113 case G_TOKEN_IDENTIFIER:
1114 {
1115 const char *identifier = scanner->value.v_identifier;
1116
1117 if (!strcmp(identifier, "payload") ||
1118 !strcmp(identifier, "args") ||
1119 !strcmp(identifier, "arg")) {
6e1bc0df 1120 found_item = true;
c42c79ea
PP
1121 if (bt_value_array_append_string(names,
1122 "payload")) {
1123 goto error;
1124 }
1125 } else if (!strcmp(identifier, "context") ||
1126 !strcmp(identifier, "ctx")) {
6e1bc0df 1127 found_item = true;
c42c79ea
PP
1128 if (bt_value_array_append_string(names,
1129 "context")) {
1130 goto error;
1131 }
1132 } else if (!strcmp(identifier, "scope") ||
1133 !strcmp(identifier, "header")) {
6e1bc0df 1134 found_item = true;
c42c79ea
PP
1135 if (bt_value_array_append_string(names,
1136 identifier)) {
1137 goto error;
1138 }
6e1bc0df
MD
1139 } else if (!strcmp(identifier, "all")) {
1140 found_all = true;
1141 if (bt_value_array_append_string(names,
1142 identifier)) {
c42c79ea
PP
1143 goto error;
1144 }
6e1bc0df
MD
1145 } else if (!strcmp(identifier, "none")) {
1146 found_none = true;
c42c79ea
PP
1147 if (bt_value_array_append_string(names,
1148 identifier)) {
1149 goto error;
1150 }
c42c79ea 1151 } else {
6d1d5711 1152 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1153 identifier);
1154 goto error;
1155 }
1156 break;
1157 }
1158 case G_TOKEN_COMMA:
1159 continue;
1160 case G_TOKEN_EOF:
1161 goto end;
1162 default:
1163 goto error;
1164 }
1165 }
1166
6e1bc0df
MD
1167end:
1168 if (found_none && found_all) {
6d1d5711 1169 printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n");
6e1bc0df
MD
1170 goto error;
1171 }
1172 /*
1173 * Legacy behavior is to clear the defaults (show none) when at
1174 * least one item is specified.
1175 */
1176 if (found_item && !found_none && !found_all) {
1177 if (bt_value_array_append_string(names, "none")) {
1178 goto error;
1179 }
1180 }
1181 if (scanner) {
1182 g_scanner_destroy(scanner);
1183 }
1184 return names;
c42c79ea
PP
1185
1186error:
1187 BT_PUT(names);
c42c79ea
PP
1188 if (scanner) {
1189 g_scanner_destroy(scanner);
1190 }
1191 return names;
1192}
1193
1194
1195/*
1196 * Converts a comma-delimited list of known fields (--fields option) to
1197 * an array value object containing those fields as string
1198 * value objects.
1199 *
1200 * Return value is owned by the caller.
1201 */
1202static
1203struct bt_value *fields_from_arg(const char *arg)
1204{
1205 GScanner *scanner = NULL;
1206 struct bt_value *fields;
1207
1208 fields = bt_value_array_create();
1209 if (!fields) {
1210 print_err_oom();
1211 goto error;
1212 }
1213
1214 scanner = create_csv_identifiers_scanner();
1215 if (!scanner) {
1216 print_err_oom();
1217 goto error;
1218 }
1219
1220 g_scanner_input_text(scanner, arg, strlen(arg));
1221
1222 while (true) {
1223 GTokenType token_type = g_scanner_get_next_token(scanner);
1224
1225 switch (token_type) {
1226 case G_TOKEN_IDENTIFIER:
1227 {
1228 const char *identifier = scanner->value.v_identifier;
1229
1230 if (!strcmp(identifier, "trace") ||
1231 !strcmp(identifier, "trace:hostname") ||
1232 !strcmp(identifier, "trace:domain") ||
1233 !strcmp(identifier, "trace:procname") ||
1234 !strcmp(identifier, "trace:vpid") ||
1235 !strcmp(identifier, "loglevel") ||
1236 !strcmp(identifier, "emf") ||
6e1bc0df
MD
1237 !strcmp(identifier, "callsite") ||
1238 !strcmp(identifier, "all")) {
c42c79ea
PP
1239 if (bt_value_array_append_string(fields,
1240 identifier)) {
1241 goto error;
1242 }
c42c79ea 1243 } else {
6d1d5711 1244 printf_err("Unknown field name: `%s`\n",
c42c79ea
PP
1245 identifier);
1246 goto error;
1247 }
1248 break;
1249 }
1250 case G_TOKEN_COMMA:
1251 continue;
1252 case G_TOKEN_EOF:
1253 goto end;
1254 default:
1255 goto error;
1256 }
1257 }
1258
1259 goto end;
1260
1261error:
1262 BT_PUT(fields);
1263
1264end:
1265 if (scanner) {
1266 g_scanner_destroy(scanner);
1267 }
1268 return fields;
1269}
1270
1271/*
1272 * Inserts the equivalent "prefix-name" true boolean value objects into
1273 * map_obj where the names are in array_obj.
1274 */
1275static
1276int insert_flat_names_fields_from_array(struct bt_value *map_obj,
1277 struct bt_value *array_obj, const char *prefix)
1278{
1279 int ret = 0;
1280 int i;
6e1bc0df 1281 GString *tmpstr = NULL, *default_value = NULL;
c42c79ea
PP
1282
1283 /*
1284 * array_obj may be NULL if no CLI options were specified to
1285 * trigger its creation.
1286 */
1287 if (!array_obj) {
1288 goto end;
1289 }
1290
1291 tmpstr = g_string_new(NULL);
1292 if (!tmpstr) {
1293 print_err_oom();
1294 ret = -1;
1295 goto end;
1296 }
1297
6e1bc0df
MD
1298 default_value = g_string_new(NULL);
1299 if (!default_value) {
1300 print_err_oom();
1301 ret = -1;
1302 goto end;
1303 }
1304
c42c79ea
PP
1305 for (i = 0; i < bt_value_array_size(array_obj); i++) {
1306 struct bt_value *str_obj = bt_value_array_get(array_obj, i);
1307 const char *suffix;
6e1bc0df 1308 bool is_default = false;
c42c79ea
PP
1309
1310 if (!str_obj) {
1311 printf_err("Unexpected error\n");
1312 ret = -1;
1313 goto end;
1314 }
1315
1316 ret = bt_value_string_get(str_obj, &suffix);
1317 BT_PUT(str_obj);
1318 if (ret) {
1319 printf_err("Unexpected error\n");
1320 goto end;
1321 }
1322
1323 g_string_assign(tmpstr, prefix);
1324 g_string_append(tmpstr, "-");
6e1bc0df
MD
1325
1326 /* Special-case for "all" and "none". */
1327 if (!strcmp(suffix, "all")) {
1328 is_default = true;
1329 g_string_assign(default_value, "show");
1330 } else if (!strcmp(suffix, "none")) {
1331 is_default = true;
1332 g_string_assign(default_value, "hide");
1333 }
1334 if (is_default) {
1335 g_string_append(tmpstr, "default");
1336 ret = map_insert_string_or_null(map_obj,
1337 tmpstr->str,
1338 default_value);
1339 if (ret) {
1340 print_err_oom();
1341 goto end;
1342 }
1343 } else {
1344 g_string_append(tmpstr, suffix);
1345 ret = bt_value_map_insert_bool(map_obj, tmpstr->str,
1346 true);
1347 if (ret) {
1348 print_err_oom();
1349 goto end;
1350 }
c42c79ea
PP
1351 }
1352 }
1353
1354end:
6e1bc0df
MD
1355 if (default_value) {
1356 g_string_free(default_value, TRUE);
1357 }
c42c79ea
PP
1358 if (tmpstr) {
1359 g_string_free(tmpstr, TRUE);
1360 }
1361
1362 return ret;
1363}
1364
c42c79ea
PP
1365/*
1366 * Returns the parameters (map value object) corresponding to the
1367 * legacy text format options.
1368 *
1369 * Return value is owned by the caller.
1370 */
1371static
1372struct bt_value *params_from_text_legacy_opts(
1373 struct text_legacy_opts *text_legacy_opts)
1374{
1375 struct bt_value *params;
1376
1377 params = bt_value_map_create();
1378 if (!params) {
1379 print_err_oom();
1380 goto error;
1381 }
1382
1383 if (map_insert_string_or_null(params, "output-path",
1384 text_legacy_opts->output)) {
1385 print_err_oom();
1386 goto error;
1387 }
1388
1389 if (map_insert_string_or_null(params, "debug-info-dir",
1390 text_legacy_opts->dbg_info_dir)) {
1391 print_err_oom();
1392 goto error;
1393 }
1394
1395 if (map_insert_string_or_null(params, "debug-info-target-prefix",
1396 text_legacy_opts->dbg_info_target_prefix)) {
1397 print_err_oom();
1398 goto error;
1399 }
1400
1401 if (bt_value_map_insert_bool(params, "debug-info-full-path",
1402 text_legacy_opts->dbg_info_full_path)) {
1403 print_err_oom();
1404 goto error;
1405 }
1406
1407 if (bt_value_map_insert_bool(params, "no-delta",
1408 text_legacy_opts->no_delta)) {
1409 print_err_oom();
1410 goto error;
1411 }
1412
1413 if (bt_value_map_insert_bool(params, "clock-cycles",
1414 text_legacy_opts->clock_cycles)) {
1415 print_err_oom();
1416 goto error;
1417 }
1418
1419 if (bt_value_map_insert_bool(params, "clock-seconds",
1420 text_legacy_opts->clock_seconds)) {
1421 print_err_oom();
1422 goto error;
1423 }
1424
1425 if (bt_value_map_insert_bool(params, "clock-date",
1426 text_legacy_opts->clock_date)) {
1427 print_err_oom();
1428 goto error;
1429 }
1430
1431 if (bt_value_map_insert_bool(params, "clock-gmt",
1432 text_legacy_opts->clock_gmt)) {
1433 print_err_oom();
1434 goto error;
1435 }
1436
1437 if (insert_flat_names_fields_from_array(params,
1438 text_legacy_opts->names, "name")) {
1439 goto error;
1440 }
1441
1442 if (insert_flat_names_fields_from_array(params,
1443 text_legacy_opts->fields, "field")) {
1444 goto error;
1445 }
1446
1447 goto end;
1448
1449error:
1450 BT_PUT(params);
1451
1452end:
1453 return params;
1454}
1455
1456static
1457int append_sinks_from_legacy_opts(GPtrArray *sinks,
1458 enum legacy_output_format legacy_output_format,
1459 struct text_legacy_opts *text_legacy_opts)
1460{
1461 int ret = 0;
1462 struct bt_value *params = NULL;
1463 const char *plugin_name;
1464 const char *component_name;
1465 struct bt_config_component *bt_config_component = NULL;
1466
1467 switch (legacy_output_format) {
1468 case LEGACY_OUTPUT_FORMAT_TEXT:
1469 plugin_name = "text";
1470 component_name = "text";
1471 break;
1472 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1473 plugin_name = "ctf";
1474 component_name = "metadata-text";
1475 break;
1476 case LEGACY_OUTPUT_FORMAT_DUMMY:
1477 plugin_name = "dummy";
1478 component_name = "dummy";
1479 break;
1480 default:
1481 assert(false);
1482 break;
1483 }
1484
1485 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT) {
1486 /* Legacy "text" output format has parameters */
1487 params = params_from_text_legacy_opts(text_legacy_opts);
1488 if (!params) {
1489 goto error;
1490 }
1491 } else {
1492 /*
1493 * Legacy "dummy" and "ctf-metadata" output formats do
1494 * not have parameters.
1495 */
1496 params = bt_value_map_create();
1497 if (!params) {
1498 print_err_oom();
1499 goto error;
1500 }
1501 }
1502
1503 /* Create a component configuration */
1504 bt_config_component = bt_config_component_create(plugin_name,
bdc61c70 1505 component_name);
c42c79ea
PP
1506 if (!bt_config_component) {
1507 goto error;
1508 }
1509
bdc61c70
PP
1510 BT_MOVE(bt_config_component->params, params);
1511
c42c79ea
PP
1512 /* Move created component configuration to the array */
1513 g_ptr_array_add(sinks, bt_config_component);
1514
1515 goto end;
1516
1517error:
1518 ret = -1;
1519
1520end:
1521 BT_PUT(params);
1522
1523 return ret;
1524}
1525
1526/*
1527 * Returns the parameters (map value object) corresponding to the
1528 * given legacy CTF format options.
1529 *
1530 * Return value is owned by the caller.
1531 */
1532static
1533struct bt_value *params_from_ctf_legacy_opts(
1534 struct ctf_legacy_opts *ctf_legacy_opts)
1535{
1536 struct bt_value *params;
1537
1538 params = bt_value_map_create();
1539 if (!params) {
1540 print_err_oom();
1541 goto error;
1542 }
1543
1544 if (bt_value_map_insert_integer(params, "offset-s",
1545 ctf_legacy_opts->offset_s.value)) {
1546 print_err_oom();
1547 goto error;
1548 }
1549
1550 if (bt_value_map_insert_integer(params, "offset-ns",
1551 ctf_legacy_opts->offset_ns.value)) {
1552 print_err_oom();
1553 goto error;
1554 }
1555
1556 if (bt_value_map_insert_bool(params, "stream-intersection",
1557 ctf_legacy_opts->stream_intersection)) {
1558 print_err_oom();
1559 goto error;
1560 }
1561
1562 goto end;
1563
1564error:
1565 BT_PUT(params);
1566
1567end:
1568 return params;
1569}
1570
1571static
1572int append_sources_from_legacy_opts(GPtrArray *sources,
1573 enum legacy_input_format legacy_input_format,
1574 struct ctf_legacy_opts *ctf_legacy_opts,
528debdf 1575 struct bt_value *legacy_input_paths)
c42c79ea
PP
1576{
1577 int ret = 0;
1578 int i;
1579 struct bt_value *base_params;
1580 struct bt_value *params = NULL;
1581 struct bt_value *input_path = NULL;
1582 struct bt_value *input_path_copy = NULL;
1583 const char *input_key;
1584 const char *component_name;
1585
1586 switch (legacy_input_format) {
1587 case LEGACY_INPUT_FORMAT_CTF:
1588 input_key = "path";
1589 component_name = "fs";
1590 break;
1591 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1592 input_key = "url";
1593 component_name = "lttng-live";
1594 break;
1595 default:
1596 assert(false);
1597 break;
1598 }
1599
1600 base_params = params_from_ctf_legacy_opts(ctf_legacy_opts);
1601 if (!base_params) {
1602 goto error;
1603 }
1604
1605 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1606 struct bt_config_component *bt_config_component = NULL;
1607
1608 /* Copy base parameters as current parameters */
1609 params = bt_value_copy(base_params);
1610 if (!params) {
1611 goto error;
1612 }
1613
1614 /* Get current input path string value object */
1615 input_path = bt_value_array_get(legacy_input_paths, i);
1616 if (!input_path) {
1617 goto error;
1618 }
1619
1620 /* Copy current input path value object */
1621 input_path_copy = bt_value_copy(input_path);
1622 if (!input_path_copy) {
1623 goto error;
1624 }
1625
1626 /* Insert input path value object into current parameters */
1627 ret = bt_value_map_insert(params, input_key, input_path_copy);
1628 if (ret) {
1629 goto error;
1630 }
1631
1632 /* Create a component configuration */
1633 bt_config_component = bt_config_component_create("ctf",
bdc61c70 1634 component_name);
c42c79ea
PP
1635 if (!bt_config_component) {
1636 goto error;
1637 }
1638
bdc61c70
PP
1639 BT_MOVE(bt_config_component->params, params);
1640
c42c79ea
PP
1641 /* Move created component configuration to the array */
1642 g_ptr_array_add(sources, bt_config_component);
1643
1644 /* Put current stuff */
1645 BT_PUT(input_path);
1646 BT_PUT(input_path_copy);
c42c79ea
PP
1647 }
1648
1649 goto end;
1650
1651error:
1652 ret = -1;
1653
1654end:
1655 BT_PUT(base_params);
1656 BT_PUT(params);
1657 BT_PUT(input_path);
1658 BT_PUT(input_path_copy);
1659 return ret;
1660}
1661
1662/*
1663 * Escapes a string for the shell. The string is escaped knowing that
1664 * it's a parameter string value (double-quoted), and that it will be
1665 * entered between single quotes in the shell.
1666 *
1667 * Return value is owned by the caller.
1668 */
1669static
1670char *str_shell_escape(const char *input)
1671{
1672 char *ret = NULL;
1673 const char *at = input;
1674 GString *str = g_string_new(NULL);
1675
1676 if (!str) {
1677 goto end;
1678 }
1679
1680 while (*at != '\0') {
1681 switch (*at) {
1682 case '\\':
1683 g_string_append(str, "\\\\");
1684 break;
1685 case '"':
1686 g_string_append(str, "\\\"");
1687 break;
1688 case '\'':
1689 g_string_append(str, "'\"'\"'");
1690 break;
1691 case '\n':
1692 g_string_append(str, "\\n");
1693 break;
1694 case '\t':
1695 g_string_append(str, "\\t");
1696 break;
1697 default:
1698 g_string_append_c(str, *at);
1699 break;
1700 }
1701
1702 at++;
1703 }
1704
1705end:
1706 if (str) {
1707 ret = str->str;
1708 g_string_free(str, FALSE);
1709 }
1710
1711 return ret;
1712}
1713
1714static
1715int append_prefixed_flag_params(GString *str, struct bt_value *flags,
1716 const char *prefix)
1717{
1718 int ret = 0;
1719 int i;
1720
1721 if (!flags) {
1722 goto end;
1723 }
1724
1725 for (i = 0; i < bt_value_array_size(flags); i++) {
1726 struct bt_value *value = bt_value_array_get(flags, i);
1727 const char *flag;
1728
1729 if (!value) {
1730 ret = -1;
1731 goto end;
1732 }
1733
1734 if (bt_value_string_get(value, &flag)) {
1735 BT_PUT(value);
1736 ret = -1;
1737 goto end;
1738 }
1739
1740 g_string_append_printf(str, ",%s-%s=true", prefix, flag);
1741 BT_PUT(value);
1742 }
1743
1744end:
1745 return ret;
1746}
1747
1748/*
1749 * Appends a boolean parameter string.
1750 */
1751static
1752void g_string_append_bool_param(GString *str, const char *name, bool value)
1753{
1754 g_string_append_printf(str, ",%s=%s", name, value ? "true" : "false");
1755}
1756
1757/*
1758 * Appends a path parameter string, or null if it's empty.
1759 */
1760static
1761int g_string_append_string_path_param(GString *str, const char *name,
1762 GString *path)
1763{
1764 int ret = 0;
1765
1766 if (path->len > 0) {
1767 char *escaped_path = str_shell_escape(path->str);
1768
1769 if (!escaped_path) {
1770 print_err_oom();
1771 goto error;
1772 }
1773
1774 g_string_append_printf(str, "%s=\"%s\"", name, escaped_path);
1775 free(escaped_path);
1776 } else {
1777 g_string_append_printf(str, "%s=null", name);
1778 }
1779
1780 goto end;
1781
1782error:
1783 ret = -1;
1784
1785end:
1786 return ret;
1787}
1788
1789/*
1790 * Prints the non-legacy sink options equivalent to the specified
1791 * legacy output format options.
1792 */
1793static
1794void print_output_legacy_to_sinks(
1795 enum legacy_output_format legacy_output_format,
1796 struct text_legacy_opts *text_legacy_opts)
1797{
1798 const char *input_format;
1799 GString *str = NULL;
1800
1801 str = g_string_new(" ");
1802 if (!str) {
1803 print_err_oom();
1804 goto end;
1805 }
1806
1807 switch (legacy_output_format) {
1808 case LEGACY_OUTPUT_FORMAT_TEXT:
1809 input_format = "text";
1810 break;
1811 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1812 input_format = "ctf-metadata";
1813 break;
1814 case LEGACY_OUTPUT_FORMAT_DUMMY:
1815 input_format = "dummy";
1816 break;
1817 default:
1818 assert(false);
1819 }
1820
49849a47 1821 printf_err("Both `%s` legacy output format and non-legacy sink component\ninstances(s) specified.\n\n",
c42c79ea 1822 input_format);
49849a47 1823 printf_err("Specify the following non-legacy sink component instance instead of the\nlegacy `%s` output format options:\n\n",
c42c79ea
PP
1824 input_format);
1825 g_string_append(str, "-o ");
1826
1827 switch (legacy_output_format) {
1828 case LEGACY_OUTPUT_FORMAT_TEXT:
1829 g_string_append(str, "text.text");
1830 break;
1831 case LEGACY_OUTPUT_FORMAT_CTF_METADATA:
1832 g_string_append(str, "ctf.metadata-text");
1833 break;
1834 case LEGACY_OUTPUT_FORMAT_DUMMY:
1835 g_string_append(str, "dummy.dummy");
1836 break;
1837 default:
1838 assert(false);
1839 }
1840
1841 if (legacy_output_format == LEGACY_OUTPUT_FORMAT_TEXT &&
1842 text_legacy_opts_is_any_set(text_legacy_opts)) {
1843 int ret;
1844
bdc61c70 1845 g_string_append(str, " -p '");
c42c79ea
PP
1846
1847 if (g_string_append_string_path_param(str, "output-path",
1848 text_legacy_opts->output)) {
1849 goto end;
1850 }
1851
1852 g_string_append(str, ",");
1853
1854 if (g_string_append_string_path_param(str, "debug-info-dir",
1855 text_legacy_opts->dbg_info_dir)) {
1856 goto end;
1857 }
1858
1859 g_string_append(str, ",");
1860
1861 if (g_string_append_string_path_param(str,
1862 "debug-info-target-prefix",
1863 text_legacy_opts->dbg_info_target_prefix)) {
1864 goto end;
1865 }
1866
1867 g_string_append_bool_param(str, "no-delta",
1868 text_legacy_opts->no_delta);
1869 g_string_append_bool_param(str, "clock-cycles",
1870 text_legacy_opts->clock_cycles);
1871 g_string_append_bool_param(str, "clock-seconds",
1872 text_legacy_opts->clock_seconds);
1873 g_string_append_bool_param(str, "clock-date",
1874 text_legacy_opts->clock_date);
1875 g_string_append_bool_param(str, "clock-gmt",
1876 text_legacy_opts->clock_gmt);
1877 ret = append_prefixed_flag_params(str, text_legacy_opts->names,
1878 "name");
1879 if (ret) {
1880 goto end;
1881 }
1882
1883 ret = append_prefixed_flag_params(str, text_legacy_opts->fields,
1884 "field");
1885 if (ret) {
1886 goto end;
1887 }
1888
1889 /* Remove last comma and close single quote */
1890 g_string_append(str, "'");
1891 }
1892
1893 printf_err("%s\n\n", str->str);
1894
1895end:
1896 if (str) {
1897 g_string_free(str, TRUE);
1898 }
1899 return;
1900}
1901
1902/*
1903 * Prints the non-legacy source options equivalent to the specified
1904 * legacy input format options.
1905 */
1906static
1907void print_input_legacy_to_sources(enum legacy_input_format legacy_input_format,
1908 struct bt_value *legacy_input_paths,
1909 struct ctf_legacy_opts *ctf_legacy_opts)
1910{
1911 const char *input_format;
1912 GString *str = NULL;
1913 int i;
1914
1915 str = g_string_new(" ");
1916 if (!str) {
1917 print_err_oom();
1918 goto end;
1919 }
1920
1921 switch (legacy_input_format) {
1922 case LEGACY_INPUT_FORMAT_CTF:
1923 input_format = "ctf";
1924 break;
1925 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
1926 input_format = "lttng-live";
1927 break;
1928 default:
1929 assert(false);
1930 }
1931
49849a47 1932 printf_err("Both `%s` legacy input format and non-legacy source component\ninstance(s) specified.\n\n",
c42c79ea 1933 input_format);
49849a47 1934 printf_err("Specify the following non-legacy source component instance(s) instead of the\nlegacy `%s` input format options and positional arguments:\n\n",
c42c79ea
PP
1935 input_format);
1936
1937 for (i = 0; i < bt_value_array_size(legacy_input_paths); i++) {
1938 struct bt_value *input_value =
1939 bt_value_array_get(legacy_input_paths, i);
1940 const char *input = NULL;
1941 char *escaped_input;
1942 int ret;
1943
1944 assert(input_value);
1945 ret = bt_value_string_get(input_value, &input);
1946 BT_PUT(input_value);
1947 assert(!ret && input);
1948 escaped_input = str_shell_escape(input);
1949 if (!escaped_input) {
1950 print_err_oom();
1951 goto end;
1952 }
1953
1954 g_string_append(str, "-i ctf.");
1955
1956 switch (legacy_input_format) {
1957 case LEGACY_INPUT_FORMAT_CTF:
bdc61c70 1958 g_string_append(str, "fs -p 'path=\"");
c42c79ea
PP
1959 break;
1960 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
bdc61c70 1961 g_string_append(str, "lttng-live -p 'url=\"");
c42c79ea
PP
1962 break;
1963 default:
1964 assert(false);
1965 }
1966
1967 g_string_append(str, escaped_input);
1968 g_string_append(str, "\"");
1969 g_string_append_printf(str, ",offset-s=%" PRId64,
1970 ctf_legacy_opts->offset_s.value);
1971 g_string_append_printf(str, ",offset-ns=%" PRId64,
1972 ctf_legacy_opts->offset_ns.value);
1973 g_string_append_bool_param(str, "stream-intersection",
1974 ctf_legacy_opts->stream_intersection);
1975 g_string_append(str, "' ");
1976 g_free(escaped_input);
1977 }
1978
1979 printf_err("%s\n\n", str->str);
1980
1981end:
1982 if (str) {
1983 g_string_free(str, TRUE);
1984 }
1985 return;
1986}
1987
1988/*
1989 * Validates a given configuration, with optional legacy input and
1990 * output formats options. Prints useful error messages if anything
1991 * is wrong.
1992 *
1993 * Returns true when the configuration is valid.
1994 */
1995static
1996bool validate_cfg(struct bt_config *cfg,
1997 enum legacy_input_format *legacy_input_format,
1998 enum legacy_output_format *legacy_output_format,
1999 struct bt_value *legacy_input_paths,
2000 struct ctf_legacy_opts *ctf_legacy_opts,
2001 struct text_legacy_opts *text_legacy_opts)
2002{
2003 bool legacy_input = false;
2004 bool legacy_output = false;
2005
2006 /* Determine if the input and output should be legacy-style */
2007 if (*legacy_input_format != LEGACY_INPUT_FORMAT_NONE ||
2008 cfg->sources->len == 0 ||
2009 !bt_value_array_is_empty(legacy_input_paths) ||
2010 ctf_legacy_opts_is_any_set(ctf_legacy_opts)) {
2011 legacy_input = true;
2012 }
2013
2014 if (*legacy_output_format != LEGACY_OUTPUT_FORMAT_NONE ||
2015 cfg->sinks->len == 0 ||
2016 text_legacy_opts_is_any_set(text_legacy_opts)) {
2017 legacy_output = true;
2018 }
2019
2020 if (legacy_input) {
2021 /* If no legacy input format was specified, default to CTF */
2022 if (*legacy_input_format == LEGACY_INPUT_FORMAT_NONE) {
2023 *legacy_input_format = LEGACY_INPUT_FORMAT_CTF;
2024 }
2025
2026 /* Make sure at least one input path exists */
2027 if (bt_value_array_is_empty(legacy_input_paths)) {
2028 switch (*legacy_input_format) {
2029 case LEGACY_INPUT_FORMAT_CTF:
6d1d5711 2030 printf_err("No input path specified for legacy `ctf` input format\n");
c42c79ea
PP
2031 break;
2032 case LEGACY_INPUT_FORMAT_LTTNG_LIVE:
6d1d5711 2033 printf_err("No URL specified for legacy `lttng-live` input format\n");
c42c79ea
PP
2034 break;
2035 default:
2036 assert(false);
2037 }
2038 goto error;
2039 }
2040
2041 /* Make sure no non-legacy sources are specified */
2042 if (cfg->sources->len != 0) {
2043 print_input_legacy_to_sources(*legacy_input_format,
2044 legacy_input_paths, ctf_legacy_opts);
2045 goto error;
2046 }
2047 }
2048
2049 if (legacy_output) {
2050 /*
2051 * If no legacy output format was specified, default to
2052 * "text".
2053 */
2054 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_NONE) {
2055 *legacy_output_format = LEGACY_OUTPUT_FORMAT_TEXT;
2056 }
2057
2058 /*
2059 * If any "text" option was specified, the output must be
2060 * legacy "text".
2061 */
2062 if (text_legacy_opts_is_any_set(text_legacy_opts) &&
2063 *legacy_output_format !=
2064 LEGACY_OUTPUT_FORMAT_TEXT) {
6d1d5711 2065 printf_err("Options for legacy `text` output format specified with a different legacy output format\n");
c42c79ea
PP
2066 goto error;
2067 }
2068
2069 /* Make sure no non-legacy sinks are specified */
2070 if (cfg->sinks->len != 0) {
2071 print_output_legacy_to_sinks(*legacy_output_format,
2072 text_legacy_opts);
2073 goto error;
2074 }
2075 }
2076
2077 /*
2078 * If the output is the legacy "ctf-metadata" format, then the
2079 * input should be the legacy "ctf" input format.
2080 */
2081 if (*legacy_output_format == LEGACY_OUTPUT_FORMAT_CTF_METADATA &&
2082 *legacy_input_format != LEGACY_INPUT_FORMAT_CTF) {
6d1d5711 2083 printf_err("Legacy `ctf-metadata` output format requires using legacy `ctf` input format\n");
c42c79ea
PP
2084 goto error;
2085 }
2086
2087 return true;
2088
2089error:
2090 return false;
2091}
2092
2093/*
2094 * Parses a 64-bit signed integer.
2095 *
2096 * Returns a negative value if anything goes wrong.
2097 */
2098static
2099int parse_int64(const char *arg, int64_t *val)
2100{
2101 char *endptr;
2102
2103 errno = 0;
2104 *val = strtoll(arg, &endptr, 0);
2105 if (*endptr != '\0' || arg == endptr || errno != 0) {
2106 return -1;
2107 }
2108
2109 return 0;
2110}
2111
2112/* popt options */
2113enum {
2114 OPT_NONE = 0,
b07ffa28 2115 OPT_BASE_PARAMS,
0f9915c6 2116 OPT_BEGIN,
c42c79ea
PP
2117 OPT_CLOCK_CYCLES,
2118 OPT_CLOCK_DATE,
2119 OPT_CLOCK_FORCE_CORRELATE,
2120 OPT_CLOCK_GMT,
2121 OPT_CLOCK_OFFSET,
2122 OPT_CLOCK_OFFSET_NS,
2123 OPT_CLOCK_SECONDS,
2124 OPT_DEBUG,
2125 OPT_DEBUG_INFO_DIR,
2126 OPT_DEBUG_INFO_FULL_PATH,
2127 OPT_DEBUG_INFO_TARGET_PREFIX,
0f9915c6 2128 OPT_END,
c42c79ea
PP
2129 OPT_FIELDS,
2130 OPT_HELP,
2131 OPT_HELP_LEGACY,
2132 OPT_INPUT_FORMAT,
2133 OPT_LIST,
2134 OPT_NAMES,
2135 OPT_NO_DELTA,
2136 OPT_OUTPUT_FORMAT,
2137 OPT_OUTPUT_PATH,
ad6a19bd 2138 OPT_PATH,
bdc61c70 2139 OPT_PARAMS,
c42c79ea 2140 OPT_PLUGIN_PATH,
b07ffa28 2141 OPT_RESET_BASE_PARAMS,
c42c79ea
PP
2142 OPT_SINK,
2143 OPT_SOURCE,
2144 OPT_STREAM_INTERSECTION,
0f9915c6 2145 OPT_TIMERANGE,
c42c79ea
PP
2146 OPT_VERBOSE,
2147 OPT_VERSION,
2148};
2149
2150/* popt long option descriptions */
2151static struct poptOption long_options[] = {
2152 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
b07ffa28 2153 { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL },
0f9915c6 2154 { "begin", '\0', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL },
c42c79ea
PP
2155 { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL },
2156 { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL },
2157 { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL },
2158 { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL },
2159 { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
2160 { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
2161 { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
2162 { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
2163 { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
2164 { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
2165 { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL },
0f9915c6 2166 { "end", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL },
c42c79ea
PP
2167 { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL },
2168 { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL },
2169 { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL },
2170 { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL },
2171 { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL },
2172 { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL },
2173 { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL },
2174 { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL },
2175 { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL },
ad6a19bd 2176 { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL },
bdc61c70 2177 { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL },
ad6a19bd 2178 { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL },
b07ffa28 2179 { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL },
c42c79ea
PP
2180 { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL },
2181 { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL },
2182 { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL },
0f9915c6 2183 { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL },
c42c79ea
PP
2184 { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL },
2185 { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL },
2186 { NULL, 0, 0, NULL, 0, NULL, NULL },
2187};
2188
2189/*
2190 * Sets the value of a given legacy offset option and marks it as set.
2191 */
2192static void set_offset_value(struct offset_opt *offset_opt, int64_t value)
2193{
2194 offset_opt->value = value;
2195 offset_opt->is_set = true;
2196}
2197
bdc61c70
PP
2198enum bt_config_component_dest {
2199 BT_CONFIG_COMPONENT_DEST_SOURCE,
2200 BT_CONFIG_COMPONENT_DEST_SINK,
2201};
2202
2203/*
2204 * Adds a configuration component to the appropriate configuration
2205 * array depending on the destination.
2206 */
2207static void add_cfg_comp(struct bt_config *cfg,
2208 struct bt_config_component *cfg_comp,
2209 enum bt_config_component_dest dest)
2210{
2211 if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) {
2212 g_ptr_array_add(cfg->sources, cfg_comp);
2213 } else {
2214 g_ptr_array_add(cfg->sinks, cfg_comp);
2215 }
2216}
2217
0f9915c6
MD
2218static int split_timerange(const char *arg, const char **begin, const char **end)
2219{
2220 const char *c;
2221
2222 /* Try to match [begin,end] */
2223 c = strchr(arg, '[');
2224 if (!c)
2225 goto skip;
2226 *begin = ++c;
2227 c = strchr(c, ',');
2228 if (!c)
2229 goto skip;
2230 *end = ++c;
2231 c = strchr(c, ']');
2232 if (!c)
2233 goto skip;
2234 goto found;
2235
2236skip:
2237 /* Try to match begin,end */
2238 c = arg;
2239 *begin = c;
2240 c = strchr(c, ',');
2241 if (!c)
2242 goto not_found;
2243 *end = ++c;
2244 /* fall-through */
2245found:
2246 return 0;
2247not_found:
2248 return -1;
2249}
2250
c42c79ea
PP
2251/*
2252 * Returns a Babeltrace configuration, out of command-line arguments,
2253 * containing everything that is needed to instanciate specific
2254 * components with given parameters.
2255 *
2256 * *exit_code is set to the appropriate exit code to use as far as this
2257 * function goes.
2258 *
2259 * Return value is NULL on error, otherwise it's owned by the caller.
2260 */
528debdf 2261struct bt_config *bt_config_from_args(int argc, const char *argv[], int *exit_code)
c42c79ea
PP
2262{
2263 struct bt_config *cfg = NULL;
2264 poptContext pc = NULL;
2265 char *arg = NULL;
b7726e32
MD
2266 struct ctf_legacy_opts ctf_legacy_opts;
2267 struct text_legacy_opts text_legacy_opts;
c42c79ea
PP
2268 enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE;
2269 enum legacy_output_format legacy_output_format =
2270 LEGACY_OUTPUT_FORMAT_NONE;
2271 struct bt_value *legacy_input_paths = NULL;
65582340 2272 struct bt_config_component *implicit_source_comp = NULL;
bdc61c70 2273 struct bt_config_component *cur_cfg_comp = NULL;
65582340
MD
2274 bool cur_is_implicit_source = false;
2275 bool use_implicit_source = false;
bdc61c70
PP
2276 enum bt_config_component_dest cur_cfg_comp_dest =
2277 BT_CONFIG_COMPONENT_DEST_SOURCE;
b07ffa28 2278 struct bt_value *cur_base_params = NULL;
c42c79ea
PP
2279 int opt;
2280
b7726e32
MD
2281 memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts));
2282 memset(&text_legacy_opts, 0, sizeof(text_legacy_opts));
c42c79ea
PP
2283 *exit_code = 0;
2284
2285 if (argc <= 1) {
2286 print_usage(stdout);
2287 goto end;
2288 }
2289
2290 text_legacy_opts.output = g_string_new(NULL);
2291 if (!text_legacy_opts.output) {
2292 print_err_oom();
2293 goto error;
2294 }
2295
2296 text_legacy_opts.dbg_info_dir = g_string_new(NULL);
2297 if (!text_legacy_opts.dbg_info_dir) {
2298 print_err_oom();
2299 goto error;
2300 }
2301
2302 text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL);
2303 if (!text_legacy_opts.dbg_info_target_prefix) {
2304 print_err_oom();
2305 goto error;
2306 }
2307
b07ffa28
PP
2308 cur_base_params = bt_value_map_create();
2309 if (!cur_base_params) {
2310 print_err_oom();
2311 goto error;
2312 }
2313
c42c79ea
PP
2314 /* Create config */
2315 cfg = g_new0(struct bt_config, 1);
2316 if (!cfg) {
2317 print_err_oom();
2318 goto error;
2319 }
2320
2321 bt_object_init(cfg, bt_config_destroy);
2322 cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2323 if (!cfg->sources) {
2324 print_err_oom();
2325 goto error;
2326 }
2327
2328 cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put);
2329 if (!cfg->sinks) {
2330 print_err_oom();
2331 goto error;
2332 }
2333
2334 legacy_input_paths = bt_value_array_create();
2335 if (!legacy_input_paths) {
2336 print_err_oom();
2337 goto error;
2338 }
2339
65582340
MD
2340 /* Note: implicit source never gets positional base params. */
2341 implicit_source_comp = bt_config_component_from_arg(DEFAULT_SOURCE_COMPONENT_NAME);
2342 if (implicit_source_comp) {
2343 cur_cfg_comp = implicit_source_comp;
2344 cur_is_implicit_source = true;
2345 use_implicit_source = true;
2346 } else {
2347 printf_debug("Cannot find implicit source plugin \"%s\"",
2348 DEFAULT_SOURCE_COMPONENT_NAME);
2349 }
2350
c42c79ea
PP
2351 /* Parse options */
2352 pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0);
2353 if (!pc) {
2354 printf_err("Cannot get popt context\n");
2355 goto error;
2356 }
2357
2358 poptReadDefaultConfig(pc, 0);
2359
2360 while ((opt = poptGetNextOpt(pc)) > 0) {
2361 arg = poptGetOptArg(pc);
2362
2363 switch (opt) {
2364 case OPT_PLUGIN_PATH:
2365 if (cfg->plugin_paths) {
2366 printf_err("Duplicate --plugin-path option\n");
2367 goto error;
2368 }
2369
2370 cfg->plugin_paths = plugin_paths_from_arg(arg);
2371 if (!cfg->plugin_paths) {
2372 printf_err("Invalid --plugin-path option's argument\n");
2373 goto error;
2374 }
2375 break;
2376 case OPT_OUTPUT_PATH:
2377 if (text_legacy_opts.output->len > 0) {
2378 printf_err("Duplicate --output option\n");
2379 goto error;
2380 }
2381
2382 g_string_assign(text_legacy_opts.output, arg);
2383 break;
2384 case OPT_DEBUG_INFO_DIR:
2385 if (text_legacy_opts.dbg_info_dir->len > 0) {
2386 printf_err("Duplicate --debug-info-dir option\n");
2387 goto error;
2388 }
2389
2390 g_string_assign(text_legacy_opts.dbg_info_dir, arg);
2391 break;
2392 case OPT_DEBUG_INFO_TARGET_PREFIX:
2393 if (text_legacy_opts.dbg_info_target_prefix->len > 0) {
2394 printf_err("Duplicate --debug-info-target-prefix option\n");
2395 goto error;
2396 }
2397
2398 g_string_assign(text_legacy_opts.dbg_info_target_prefix, arg);
2399 break;
2400 case OPT_INPUT_FORMAT:
2401 case OPT_SOURCE:
2402 {
c42c79ea
PP
2403 if (opt == OPT_INPUT_FORMAT) {
2404 if (!strcmp(arg, "ctf")) {
2405 /* Legacy CTF input format */
2406 if (legacy_input_format) {
2407 print_err_dup_legacy_input();
2408 goto error;
2409 }
2410
2411 legacy_input_format =
2412 LEGACY_INPUT_FORMAT_CTF;
2413 break;
2414 } else if (!strcmp(arg, "lttng-live")) {
2415 /* Legacy LTTng-live input format */
2416 if (legacy_input_format) {
2417 print_err_dup_legacy_input();
2418 goto error;
2419 }
2420
2421 legacy_input_format =
2422 LEGACY_INPUT_FORMAT_LTTNG_LIVE;
2423 break;
2424 }
2425 }
2426
65582340
MD
2427 use_implicit_source = false;
2428
c42c79ea 2429 /* Non-legacy: try to create a component config */
65582340 2430 if (cur_cfg_comp && !cur_is_implicit_source) {
bdc61c70
PP
2431 add_cfg_comp(cfg, cur_cfg_comp,
2432 cur_cfg_comp_dest);
2433 }
2434
2435 cur_cfg_comp = bt_config_component_from_arg(arg);
2436 if (!cur_cfg_comp) {
49849a47 2437 printf_err("Invalid format for --source option's argument:\n %s\n",
c42c79ea
PP
2438 arg);
2439 goto error;
2440 }
65582340 2441 cur_is_implicit_source = false;
c42c79ea 2442
b07ffa28
PP
2443 assert(cur_base_params);
2444 bt_put(cur_cfg_comp->params);
c9313318
PP
2445 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2446 if (!cur_cfg_comp) {
2447 print_err_oom();
2448 goto end;
2449 }
2450
bdc61c70 2451 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE;
c42c79ea
PP
2452 break;
2453 }
2454 case OPT_OUTPUT_FORMAT:
2455 case OPT_SINK:
2456 {
c42c79ea
PP
2457 if (opt == OPT_OUTPUT_FORMAT) {
2458 if (!strcmp(arg, "text")) {
2459 /* Legacy CTF-text output format */
2460 if (legacy_output_format) {
2461 print_err_dup_legacy_output();
2462 goto error;
2463 }
2464
2465 legacy_output_format =
2466 LEGACY_OUTPUT_FORMAT_TEXT;
2467 break;
2468 } else if (!strcmp(arg, "dummy")) {
2469 /* Legacy dummy output format */
2470 if (legacy_output_format) {
2471 print_err_dup_legacy_output();
2472 goto error;
2473 }
2474
2475 legacy_output_format =
2476 LEGACY_OUTPUT_FORMAT_DUMMY;
2477 break;
2478 } else if (!strcmp(arg, "ctf-metadata")) {
2479 /* Legacy CTF-metadata output format */
2480 if (legacy_output_format) {
2481 print_err_dup_legacy_output();
2482 goto error;
2483 }
2484
2485 legacy_output_format =
2486 LEGACY_OUTPUT_FORMAT_CTF_METADATA;
2487 break;
2488 }
2489 }
2490
2491 /* Non-legacy: try to create a component config */
65582340 2492 if (cur_cfg_comp && !cur_is_implicit_source) {
bdc61c70
PP
2493 add_cfg_comp(cfg, cur_cfg_comp,
2494 cur_cfg_comp_dest);
2495 }
2496
2497 cur_cfg_comp = bt_config_component_from_arg(arg);
2498 if (!cur_cfg_comp) {
49849a47 2499 printf_err("Invalid format for --sink option's argument:\n %s\n",
c42c79ea
PP
2500 arg);
2501 goto error;
2502 }
65582340 2503 cur_is_implicit_source = false;
c42c79ea 2504
b07ffa28
PP
2505 assert(cur_base_params);
2506 bt_put(cur_cfg_comp->params);
c9313318
PP
2507 cur_cfg_comp->params = bt_value_copy(cur_base_params);
2508 if (!cur_cfg_comp) {
2509 print_err_oom();
2510 goto end;
2511 }
2512
bdc61c70 2513 cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK;
bdc61c70
PP
2514 break;
2515 }
2516 case OPT_PARAMS:
2517 {
2518 struct bt_value *params;
b07ffa28 2519 struct bt_value *params_to_set;
bdc61c70
PP
2520
2521 if (!cur_cfg_comp) {
65582340
MD
2522 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2523 DEFAULT_SOURCE_COMPONENT_NAME);
bdc61c70
PP
2524 goto error;
2525 }
2526
bdc61c70
PP
2527 params = bt_value_from_arg(arg);
2528 if (!params) {
2529 printf_err("Invalid format for --params option's argument:\n %s\n",
2530 arg);
2531 goto error;
2532 }
2533
560ff91c 2534 params_to_set = bt_value_map_extend(cur_cfg_comp->params,
b07ffa28
PP
2535 params);
2536 BT_PUT(params);
2537 if (!params_to_set) {
560ff91c 2538 printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n",
b07ffa28
PP
2539 arg);
2540 goto error;
2541 }
2542
2543 BT_MOVE(cur_cfg_comp->params, params_to_set);
c42c79ea
PP
2544 break;
2545 }
ad6a19bd
PP
2546 case OPT_PATH:
2547 if (!cur_cfg_comp) {
65582340
MD
2548 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2549 DEFAULT_SOURCE_COMPONENT_NAME);
ad6a19bd
PP
2550 goto error;
2551 }
2552
2553 assert(cur_cfg_comp->params);
2554
2555 if (bt_value_map_insert_string(cur_cfg_comp->params,
2556 "path", arg)) {
2557 print_err_oom();
2558 goto error;
2559 }
2560 break;
b07ffa28
PP
2561 case OPT_BASE_PARAMS:
2562 {
2563 struct bt_value *params = bt_value_from_arg(arg);
2564
2565 if (!params) {
2566 printf_err("Invalid format for --base-params option's argument:\n %s\n",
2567 arg);
2568 goto error;
2569 }
2570
2571 BT_MOVE(cur_base_params, params);
2572 break;
2573 }
2574 case OPT_RESET_BASE_PARAMS:
2575 BT_PUT(cur_base_params);
2576 cur_base_params = bt_value_map_create();
2577 if (!cur_base_params) {
2578 print_err_oom();
2579 goto error;
2580 }
2581 break;
c42c79ea
PP
2582 case OPT_NAMES:
2583 if (text_legacy_opts.names) {
2584 printf_err("Duplicate --names option\n");
2585 goto error;
2586 }
2587
2588 text_legacy_opts.names = names_from_arg(arg);
2589 if (!text_legacy_opts.names) {
2590 printf_err("Invalid --names option's argument\n");
2591 goto error;
2592 }
2593 break;
2594 case OPT_FIELDS:
2595 if (text_legacy_opts.fields) {
2596 printf_err("Duplicate --fields option\n");
2597 goto error;
2598 }
2599
2600 text_legacy_opts.fields = fields_from_arg(arg);
2601 if (!text_legacy_opts.fields) {
2602 printf_err("Invalid --fields option's argument\n");
2603 goto error;
2604 }
2605 break;
2606 case OPT_NO_DELTA:
2607 text_legacy_opts.no_delta = true;
2608 break;
2609 case OPT_CLOCK_CYCLES:
2610 text_legacy_opts.clock_cycles = true;
2611 break;
2612 case OPT_CLOCK_SECONDS:
2613 text_legacy_opts.clock_seconds = true;
2614 break;
2615 case OPT_CLOCK_DATE:
2616 text_legacy_opts.clock_date = true;
2617 break;
2618 case OPT_CLOCK_GMT:
2619 text_legacy_opts.clock_gmt = true;
2620 break;
2621 case OPT_DEBUG_INFO_FULL_PATH:
2622 text_legacy_opts.dbg_info_full_path = true;
2623 break;
2624 case OPT_CLOCK_OFFSET:
2625 {
2626 int64_t val;
2627
2628 if (ctf_legacy_opts.offset_s.is_set) {
2629 printf_err("Duplicate --clock-offset option\n");
2630 goto error;
2631 }
2632
2633 if (parse_int64(arg, &val)) {
2634 printf_err("Invalid --clock-offset option's argument\n");
2635 goto error;
2636 }
2637
2638 set_offset_value(&ctf_legacy_opts.offset_s, val);
2639 break;
2640 }
2641 case OPT_CLOCK_OFFSET_NS:
2642 {
2643 int64_t val;
2644
2645 if (ctf_legacy_opts.offset_ns.is_set) {
2646 printf_err("Duplicate --clock-offset-ns option\n");
2647 goto error;
2648 }
2649
2650 if (parse_int64(arg, &val)) {
2651 printf_err("Invalid --clock-offset-ns option's argument\n");
2652 goto error;
2653 }
2654
2655 set_offset_value(&ctf_legacy_opts.offset_ns, val);
2656 break;
2657 }
2658 case OPT_STREAM_INTERSECTION:
2659 ctf_legacy_opts.stream_intersection = true;
2660 break;
2661 case OPT_CLOCK_FORCE_CORRELATE:
2662 cfg->force_correlate = true;
2663 break;
0f9915c6
MD
2664 case OPT_BEGIN:
2665 if (!cur_cfg_comp) {
65582340
MD
2666 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2667 DEFAULT_SOURCE_COMPONENT_NAME);
0f9915c6
MD
2668 goto error;
2669 }
2670 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2671 printf_err("--begin option must follow a --source option\n");
2672 goto error;
2673 }
2674 if (bt_value_map_insert_string(cur_cfg_comp->params,
2675 "begin", arg)) {
2676 print_err_oom();
2677 goto error;
2678 }
2679 break;
2680 case OPT_END:
2681 if (!cur_cfg_comp) {
65582340
MD
2682 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2683 DEFAULT_SOURCE_COMPONENT_NAME);
0f9915c6
MD
2684 goto error;
2685 }
2686 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2687 printf_err("--end option must follow a --source option\n");
2688 goto error;
2689 }
2690 if (bt_value_map_insert_string(cur_cfg_comp->params,
2691 "end", arg)) {
2692 print_err_oom();
2693 goto error;
2694 }
2695 break;
2696 case OPT_TIMERANGE:
2697 {
2698 const char *begin, *end;
2699
2700 if (!cur_cfg_comp) {
65582340
MD
2701 printf_err("Can not apply parameter to unavailable default source component \"%s\".\n",
2702 DEFAULT_SOURCE_COMPONENT_NAME);
0f9915c6
MD
2703 goto error;
2704 }
2705 if (cur_cfg_comp_dest != BT_CONFIG_COMPONENT_DEST_SOURCE) {
2706 printf_err("--timerange option must follow a --source option\n");
2707 goto error;
2708 }
2709 if (split_timerange(arg, &begin, &end)) {
2710 printf_err("Invalid --timerange format, expecting: begin,end or [begin,end] (where [] are actual brackets)\n");
2711 goto error;
2712 }
2713 if (bt_value_map_insert_string(cur_cfg_comp->params,
2714 "begin", begin)) {
2715 print_err_oom();
2716 goto error;
2717 }
2718 if (bt_value_map_insert_string(cur_cfg_comp->params,
2719 "end", end)) {
2720 print_err_oom();
2721 goto error;
2722 }
2723 break;
2724 }
c42c79ea
PP
2725 case OPT_HELP:
2726 BT_PUT(cfg);
2727 print_usage(stdout);
2728 goto end;
2729 case OPT_HELP_LEGACY:
2730 BT_PUT(cfg);
2731 print_legacy_usage(stdout);
2732 goto end;
2733 case OPT_VERSION:
2734 BT_PUT(cfg);
2735 print_version();
2736 goto end;
2737 case OPT_LIST:
2738 cfg->do_list = true;
2739 goto end;
2740 case OPT_VERBOSE:
2741 cfg->verbose = true;
2742 break;
2743 case OPT_DEBUG:
2744 cfg->debug = true;
2745 break;
2746 default:
2747 printf_err("Unknown command-line option specified (option code %d)\n",
2748 opt);
2749 goto error;
2750 }
2751
2752 free(arg);
2753 arg = NULL;
2754 }
2755
bdc61c70 2756 /* Append current component configuration, if any */
65582340 2757 if (cur_cfg_comp && !cur_is_implicit_source) {
bdc61c70 2758 add_cfg_comp(cfg, cur_cfg_comp, cur_cfg_comp_dest);
65582340
MD
2759 }
2760 cur_cfg_comp = NULL;
2761
2762 if (use_implicit_source) {
2763 add_cfg_comp(cfg, implicit_source_comp,
2764 BT_CONFIG_COMPONENT_DEST_SOURCE);
2765 implicit_source_comp = NULL;
2766 } else {
2767 if (implicit_source_comp
2768 && !bt_value_map_is_empty(implicit_source_comp->params)) {
2769 printf_err("Arguments specified for implicit source, but an explicit source has been specified, overriding it\n");
2770 goto error;
2771 }
bdc61c70
PP
2772 }
2773
c42c79ea
PP
2774 /* Check for option parsing error */
2775 if (opt < -1) {
2776 printf_err("While parsing command-line options, at option %s: %s\n",
2777 poptBadOption(pc, 0), poptStrerror(opt));
2778 goto error;
2779 }
2780
2781 /* Consume leftover arguments as legacy input paths */
2782 while (true) {
2783 const char *input_path = poptGetArg(pc);
2784
2785 if (!input_path) {
2786 break;
2787 }
2788
2789 if (bt_value_array_append_string(legacy_input_paths,
2790 input_path)) {
2791 print_err_oom();
2792 goto error;
2793 }
2794 }
2795
2796 /* Validate legacy/non-legacy options */
2797 if (!validate_cfg(cfg, &legacy_input_format, &legacy_output_format,
2798 legacy_input_paths, &ctf_legacy_opts,
2799 &text_legacy_opts)) {
2800 printf_err("Command-line options form an invalid configuration\n");
2801 goto error;
2802 }
2803
2804 /*
2805 * If there's a legacy input format, convert it to source
2806 * component configurations.
2807 */
2808 if (legacy_input_format) {
2809 if (append_sources_from_legacy_opts(cfg->sources,
2810 legacy_input_format, &ctf_legacy_opts,
528debdf 2811 legacy_input_paths)) {
49849a47 2812 printf_err("Cannot convert legacy input format options to source component instance(s)\n");
c42c79ea
PP
2813 goto error;
2814 }
2815 }
2816
2817 /*
2818 * If there's a legacy output format, convert it to sink
2819 * component configurations.
2820 */
2821 if (legacy_output_format) {
2822 if (append_sinks_from_legacy_opts(cfg->sinks,
2823 legacy_output_format, &text_legacy_opts)) {
49849a47 2824 printf_err("Cannot convert legacy output format options to sink component instance(s)\n");
c42c79ea
PP
2825 goto error;
2826 }
2827 }
2828
2829 goto end;
2830
2831error:
2832 BT_PUT(cfg);
2833 cfg = NULL;
2834 *exit_code = 1;
2835
2836end:
2837 if (pc) {
2838 poptFreeContext(pc);
2839 }
2840
2841 if (text_legacy_opts.output) {
2842 g_string_free(text_legacy_opts.output, TRUE);
2843 }
2844
2845 if (text_legacy_opts.dbg_info_dir) {
2846 g_string_free(text_legacy_opts.dbg_info_dir, TRUE);
2847 }
2848
2849 if (text_legacy_opts.dbg_info_target_prefix) {
2850 g_string_free(text_legacy_opts.dbg_info_target_prefix, TRUE);
2851 }
2852
2853 free(arg);
65582340 2854 BT_PUT(implicit_source_comp);
bdc61c70 2855 BT_PUT(cur_cfg_comp);
b07ffa28 2856 BT_PUT(cur_base_params);
c42c79ea
PP
2857 BT_PUT(text_legacy_opts.names);
2858 BT_PUT(text_legacy_opts.fields);
2859 BT_PUT(legacy_input_paths);
2860 return cfg;
2861}
This page took 0.138368 seconds and 4 git commands to generate.