2 * SPDX-License-Identifier: MIT
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
6 * Babeltrace common functions
9 #define BT_LOG_OUTPUT_LEVEL log_level
10 #define BT_LOG_TAG "COMMON"
11 #include "logging/log.h"
16 #include <sys/types.h>
19 #include "common/assert.h"
27 #include "common/macros.h"
28 #include "common/common.h"
29 #include "compat/unistd.h"
33 #include <sys/ioctl.h>
36 #define SYSTEM_PLUGIN_PATH BABELTRACE_PLUGINS_DIR
37 #define HOME_ENV_VAR "HOME"
38 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins"
40 static const char *bt_common_color_code_reset
= "";
41 static const char *bt_common_color_code_bold
= "";
42 static const char *bt_common_color_code_fg_default
= "";
43 static const char *bt_common_color_code_fg_red
= "";
44 static const char *bt_common_color_code_fg_green
= "";
45 static const char *bt_common_color_code_fg_yellow
= "";
46 static const char *bt_common_color_code_fg_blue
= "";
47 static const char *bt_common_color_code_fg_magenta
= "";
48 static const char *bt_common_color_code_fg_cyan
= "";
49 static const char *bt_common_color_code_fg_light_gray
= "";
50 static const char *bt_common_color_code_fg_bright_red
= "";
51 static const char *bt_common_color_code_fg_bright_green
= "";
52 static const char *bt_common_color_code_fg_bright_yellow
= "";
53 static const char *bt_common_color_code_fg_bright_blue
= "";
54 static const char *bt_common_color_code_fg_bright_magenta
= "";
55 static const char *bt_common_color_code_fg_bright_cyan
= "";
56 static const char *bt_common_color_code_fg_bright_light_gray
= "";
57 static const char *bt_common_color_code_bg_default
= "";
58 static const char *bt_common_color_code_bg_red
= "";
59 static const char *bt_common_color_code_bg_green
= "";
60 static const char *bt_common_color_code_bg_yellow
= "";
61 static const char *bt_common_color_code_bg_blue
= "";
62 static const char *bt_common_color_code_bg_magenta
= "";
63 static const char *bt_common_color_code_bg_cyan
= "";
64 static const char *bt_common_color_code_bg_light_gray
= "";
67 * A color codes structure always filled with the proper color codes for the
70 static struct bt_common_color_codes color_codes
;
73 * A color codes structure always filled with empty strings, for when we want no
76 static struct bt_common_color_codes no_color_codes
= {
77 "", "", "", "", "", "", "", "", "", "",
78 "", "", "", "", "", "", "", "", "", "",
83 void __attribute__((constructor
)) bt_common_color_ctor(void)
85 const char *term_env_var
;
86 const char *bright_means_bold_env_var
;
87 bool bright_means_bold
= true;
88 const char *code_fg_bright_red
;
89 const char *code_fg_bright_green
;
90 const char *code_fg_bright_yellow
;
91 const char *code_fg_bright_blue
;
92 const char *code_fg_bright_magenta
;
93 const char *code_fg_bright_cyan
;
94 const char *code_fg_bright_light_gray
;
97 * Check whether or not the terminal supports having
98 * bold foreground colors which do _not_ become bright
99 * colors, that is, the lines
101 * $ echo -e "\033[31mTHIS\n\033[1mTHAT\033[0m"
103 * have the _same_ color, but `THAT` uses a bold font.
105 * This is the case of the kitty terminal emulator.
107 * It's also possible with GNOME Terminal since 3.27.2
108 * and xfce4-terminal since 0.8.7 (and GNOME VTE since
109 * 0.51.2), but it's user-configurable. Since we don't
110 * have this configuration value here, assume it's not
111 * the case to support old versions of GNOME Terminal.
113 * Any user can set the
114 * `BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD` environment
115 * variable to `0` to use the bright foreground color
116 * codes instead of making the normal foreground color
122 * `BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD` is `0`:
123 * Output bright colors using dedicated SGR codes
127 * Output bright colors with bold + SGR codes 30 to
130 term_env_var
= getenv("TERM");
132 if (term_env_var
&& strcmp(term_env_var
, "xterm-kitty") == 0) {
134 * The kitty terminal emulator supports
135 * non-bright bold foreground colors.
137 bright_means_bold
= false;
140 bright_means_bold_env_var
=
141 getenv("BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD");
143 if (bright_means_bold_env_var
) {
145 !(strcmp(bright_means_bold_env_var
, "0") == 0);
148 if (bright_means_bold
) {
149 code_fg_bright_red
= BT_COMMON_COLOR_FG_BOLD_RED
;
150 code_fg_bright_green
= BT_COMMON_COLOR_FG_BOLD_GREEN
;
151 code_fg_bright_yellow
= BT_COMMON_COLOR_FG_BOLD_YELLOW
;
152 code_fg_bright_blue
= BT_COMMON_COLOR_FG_BOLD_BLUE
;
153 code_fg_bright_magenta
= BT_COMMON_COLOR_FG_BOLD_MAGENTA
;
154 code_fg_bright_cyan
= BT_COMMON_COLOR_FG_BOLD_CYAN
;
155 code_fg_bright_light_gray
= BT_COMMON_COLOR_FG_BOLD_LIGHT_GRAY
;
157 code_fg_bright_red
= BT_COMMON_COLOR_FG_BRIGHT_RED
;
158 code_fg_bright_green
= BT_COMMON_COLOR_FG_BRIGHT_GREEN
;
159 code_fg_bright_yellow
= BT_COMMON_COLOR_FG_BRIGHT_YELLOW
;
160 code_fg_bright_blue
= BT_COMMON_COLOR_FG_BRIGHT_BLUE
;
161 code_fg_bright_magenta
= BT_COMMON_COLOR_FG_BRIGHT_MAGENTA
;
162 code_fg_bright_cyan
= BT_COMMON_COLOR_FG_BRIGHT_CYAN
;
163 code_fg_bright_light_gray
= BT_COMMON_COLOR_FG_BRIGHT_LIGHT_GRAY
;
166 if (bt_common_colors_supported()) {
167 bt_common_color_code_reset
= BT_COMMON_COLOR_RESET
;
168 bt_common_color_code_bold
= BT_COMMON_COLOR_BOLD
;
169 bt_common_color_code_fg_default
= BT_COMMON_COLOR_FG_DEFAULT
;
170 bt_common_color_code_fg_red
= BT_COMMON_COLOR_FG_RED
;
171 bt_common_color_code_fg_green
= BT_COMMON_COLOR_FG_GREEN
;
172 bt_common_color_code_fg_yellow
= BT_COMMON_COLOR_FG_YELLOW
;
173 bt_common_color_code_fg_blue
= BT_COMMON_COLOR_FG_BLUE
;
174 bt_common_color_code_fg_magenta
= BT_COMMON_COLOR_FG_MAGENTA
;
175 bt_common_color_code_fg_cyan
= BT_COMMON_COLOR_FG_CYAN
;
176 bt_common_color_code_fg_light_gray
= BT_COMMON_COLOR_FG_LIGHT_GRAY
;
178 bt_common_color_code_fg_bright_red
= code_fg_bright_red
;
179 bt_common_color_code_fg_bright_green
= code_fg_bright_green
;
180 bt_common_color_code_fg_bright_yellow
= code_fg_bright_yellow
;
181 bt_common_color_code_fg_bright_blue
= code_fg_bright_blue
;
182 bt_common_color_code_fg_bright_magenta
= code_fg_bright_magenta
;
183 bt_common_color_code_fg_bright_cyan
= code_fg_bright_cyan
;
184 bt_common_color_code_fg_bright_light_gray
= code_fg_bright_light_gray
;
186 bt_common_color_code_bg_default
= BT_COMMON_COLOR_BG_DEFAULT
;
187 bt_common_color_code_bg_red
= BT_COMMON_COLOR_BG_RED
;
188 bt_common_color_code_bg_green
= BT_COMMON_COLOR_BG_GREEN
;
189 bt_common_color_code_bg_yellow
= BT_COMMON_COLOR_BG_YELLOW
;
190 bt_common_color_code_bg_blue
= BT_COMMON_COLOR_BG_BLUE
;
191 bt_common_color_code_bg_magenta
= BT_COMMON_COLOR_BG_MAGENTA
;
192 bt_common_color_code_bg_cyan
= BT_COMMON_COLOR_BG_CYAN
;
193 bt_common_color_code_bg_light_gray
= BT_COMMON_COLOR_BG_LIGHT_GRAY
;
196 color_codes
.reset
= BT_COMMON_COLOR_RESET
;
197 color_codes
.bold
= BT_COMMON_COLOR_BOLD
;
198 color_codes
.fg_default
= BT_COMMON_COLOR_FG_DEFAULT
;
199 color_codes
.fg_red
= BT_COMMON_COLOR_FG_RED
;
200 color_codes
.fg_green
= BT_COMMON_COLOR_FG_GREEN
;
201 color_codes
.fg_yellow
= BT_COMMON_COLOR_FG_YELLOW
;
202 color_codes
.fg_blue
= BT_COMMON_COLOR_FG_BLUE
;
203 color_codes
.fg_magenta
= BT_COMMON_COLOR_FG_MAGENTA
;
204 color_codes
.fg_cyan
= BT_COMMON_COLOR_FG_CYAN
;
205 color_codes
.fg_light_gray
= BT_COMMON_COLOR_FG_LIGHT_GRAY
;
206 color_codes
.fg_bright_red
= code_fg_bright_red
;
207 color_codes
.fg_bright_green
= code_fg_bright_green
;
208 color_codes
.fg_bright_yellow
= code_fg_bright_yellow
;
209 color_codes
.fg_bright_blue
= code_fg_bright_blue
;
210 color_codes
.fg_bright_magenta
= code_fg_bright_magenta
;
211 color_codes
.fg_bright_cyan
= code_fg_bright_cyan
;
212 color_codes
.fg_bright_light_gray
= code_fg_bright_light_gray
;
213 color_codes
.bg_default
= BT_COMMON_COLOR_BG_DEFAULT
;
214 color_codes
.bg_red
= BT_COMMON_COLOR_BG_RED
;
215 color_codes
.bg_green
= BT_COMMON_COLOR_BG_GREEN
;
216 color_codes
.bg_yellow
= BT_COMMON_COLOR_BG_YELLOW
;
217 color_codes
.bg_blue
= BT_COMMON_COLOR_BG_BLUE
;
218 color_codes
.bg_magenta
= BT_COMMON_COLOR_BG_MAGENTA
;
219 color_codes
.bg_cyan
= BT_COMMON_COLOR_BG_CYAN
;
220 color_codes
.bg_light_gray
= BT_COMMON_COLOR_BG_LIGHT_GRAY
;
224 const char *bt_common_get_system_plugin_path(void)
226 return SYSTEM_PLUGIN_PATH
;
231 bool bt_common_is_setuid_setgid(void)
235 #else /* __MINGW32__ */
237 bool bt_common_is_setuid_setgid(void)
239 return (geteuid() != getuid() || getegid() != getgid());
241 #endif /* __MINGW32__ */
245 const char *bt_get_home_dir(int log_level
)
247 return g_get_home_dir();
249 #else /* __MINGW32__ */
251 char *bt_secure_getenv(const char *name
, int log_level
)
253 if (bt_common_is_setuid_setgid()) {
254 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
255 "name=\"%s\"", name
);
262 const char *bt_get_home_dir(int log_level
)
267 val
= bt_secure_getenv(HOME_ENV_VAR
, log_level
);
271 /* Fallback on password file. */
272 pwd
= getpwuid(getuid());
280 #endif /* __MINGW32__ */
283 char *bt_common_get_home_plugin_path(int log_level
)
286 const char *home_dir
;
289 home_dir
= bt_get_home_dir(log_level
);
294 length
= strlen(home_dir
) + strlen(HOME_PLUGIN_SUBPATH
) + 1;
296 if (length
>= PATH_MAX
) {
297 BT_LOGW("Home directory path is too long: "
298 "length=%zu, max-length=%u", length
, PATH_MAX
);
302 path
= malloc(PATH_MAX
);
307 strcpy(path
, home_dir
);
308 strcat(path
, HOME_PLUGIN_SUBPATH
);
315 int bt_common_append_plugin_path_dirs(const char *paths
, GPtrArray
*dirs
)
320 size_t init_dirs_len
;
323 init_dirs_len
= dirs
->len
;
326 /* Nothing to append */
331 end
= paths
+ strlen(paths
);
335 const char *next_sep
;
337 next_sep
= strchr(at
, G_SEARCHPATH_SEPARATOR
);
338 if (next_sep
== at
) {
340 * Empty path: try next character (supported
341 * to conform to the typical parsing of $PATH).
345 } else if (!next_sep
) {
346 /* No more separator: use the remaining */
347 next_sep
= paths
+ strlen(paths
);
350 path
= g_string_new(NULL
);
355 g_string_append_len(path
, at
, next_sep
- at
);
357 g_ptr_array_add(dirs
, path
);
365 /* Remove the new entries in dirs */
366 while (dirs
->len
> init_dirs_len
) {
367 g_ptr_array_remove_index(dirs
, init_dirs_len
);
375 bool isarealtty(int fd
)
378 struct stat tty_stats
;
385 if (fstat(fd
, &tty_stats
) == 0) {
386 if (!S_ISCHR(tty_stats
.st_mode
)) {
387 /* Not a character device: not a TTY */
399 bool bt_common_colors_supported(void)
401 static bool supports_colors
= false;
402 static bool supports_colors_set
= false;
403 const char *term_env_var
;
404 const char *term_color_env_var
;
406 if (supports_colors_set
) {
410 supports_colors_set
= true;
413 * `BABELTRACE_TERM_COLOR` environment variable always overrides
414 * the automatic color support detection.
416 term_color_env_var
= getenv("BABELTRACE_TERM_COLOR");
417 if (term_color_env_var
) {
418 if (g_ascii_strcasecmp(term_color_env_var
, "always") == 0) {
420 supports_colors
= true;
421 } else if (g_ascii_strcasecmp(term_color_env_var
, "never") == 0) {
422 /* Force no colors */
427 /* We need a compatible, known terminal */
428 term_env_var
= getenv("TERM");
433 if (strncmp(term_env_var
, "xterm", 5) != 0 &&
434 strncmp(term_env_var
, "rxvt", 4) != 0 &&
435 strncmp(term_env_var
, "konsole", 7) != 0 &&
436 strncmp(term_env_var
, "gnome", 5) != 0 &&
437 strncmp(term_env_var
, "screen", 5) != 0 &&
438 strncmp(term_env_var
, "tmux", 4) != 0 &&
439 strncmp(term_env_var
, "putty", 5) != 0) {
443 /* Both standard output and error streams need to be TTYs */
444 if (!isarealtty(STDOUT_FILENO
) || !isarealtty(STDERR_FILENO
)) {
448 supports_colors
= true;
451 return supports_colors
;
455 const char *bt_common_color_reset(void)
457 return bt_common_color_code_reset
;
461 const char *bt_common_color_bold(void)
463 return bt_common_color_code_bold
;
467 const char *bt_common_color_fg_default(void)
469 return bt_common_color_code_fg_default
;
473 const char *bt_common_color_fg_red(void)
475 return bt_common_color_code_fg_red
;
479 const char *bt_common_color_fg_green(void)
481 return bt_common_color_code_fg_green
;
485 const char *bt_common_color_fg_yellow(void)
487 return bt_common_color_code_fg_yellow
;
491 const char *bt_common_color_fg_blue(void)
493 return bt_common_color_code_fg_blue
;
497 const char *bt_common_color_fg_magenta(void)
499 return bt_common_color_code_fg_magenta
;
503 const char *bt_common_color_fg_cyan(void)
505 return bt_common_color_code_fg_cyan
;
509 const char *bt_common_color_fg_light_gray(void)
511 return bt_common_color_code_fg_light_gray
;
515 const char *bt_common_color_fg_bright_red(void)
517 return bt_common_color_code_fg_bright_red
;
521 const char *bt_common_color_fg_bright_green(void)
523 return bt_common_color_code_fg_bright_green
;
527 const char *bt_common_color_fg_bright_yellow(void)
529 return bt_common_color_code_fg_bright_yellow
;
533 const char *bt_common_color_fg_bright_blue(void)
535 return bt_common_color_code_fg_bright_blue
;
539 const char *bt_common_color_fg_bright_magenta(void)
541 return bt_common_color_code_fg_bright_magenta
;
545 const char *bt_common_color_fg_bright_cyan(void)
547 return bt_common_color_code_fg_bright_cyan
;
551 const char *bt_common_color_fg_bright_light_gray(void)
553 return bt_common_color_code_fg_bright_light_gray
;
557 const char *bt_common_color_bg_default(void)
559 return bt_common_color_code_bg_default
;
563 const char *bt_common_color_bg_red(void)
565 return bt_common_color_code_bg_red
;
569 const char *bt_common_color_bg_green(void)
571 return bt_common_color_code_bg_green
;
575 const char *bt_common_color_bg_yellow(void)
577 return bt_common_color_code_bg_yellow
;
581 const char *bt_common_color_bg_blue(void)
583 return bt_common_color_code_bg_blue
;
587 const char *bt_common_color_bg_magenta(void)
589 return bt_common_color_code_bg_magenta
;
593 const char *bt_common_color_bg_cyan(void)
595 return bt_common_color_code_bg_cyan
;
599 const char *bt_common_color_bg_light_gray(void)
601 return bt_common_color_code_bg_light_gray
;
605 void bt_common_color_get_codes(struct bt_common_color_codes
*codes
,
606 enum bt_common_color_when use_colors
)
608 if (use_colors
== BT_COMMON_COLOR_WHEN_ALWAYS
) {
609 *codes
= color_codes
;
610 } else if (use_colors
== BT_COMMON_COLOR_WHEN_NEVER
) {
611 *codes
= no_color_codes
;
613 BT_ASSERT(use_colors
== BT_COMMON_COLOR_WHEN_AUTO
);
615 if (bt_common_colors_supported()) {
616 *codes
= color_codes
;
618 *codes
= no_color_codes
;
624 GString
*bt_common_string_until(const char *input
, const char *escapable_chars
,
625 const char *end_chars
, size_t *end_pos
)
627 GString
*output
= g_string_new(NULL
);
630 const char *end_char
;
636 for (ch
= input
; *ch
!= '\0'; ch
++) {
638 bool continue_loop
= false;
641 /* `\` at the end of the string: append `\` */
642 g_string_append_c(output
, *ch
);
647 for (es_char
= escapable_chars
; *es_char
!= '\0'; es_char
++) {
648 if (ch
[1] == *es_char
) {
650 * `\` followed by an escapable
651 * character: append the escaped
654 g_string_append_c(output
, ch
[1]);
656 continue_loop
= true;
666 * `\` followed by a non-escapable character:
667 * append `\` and the character.
669 g_string_append_c(output
, *ch
);
670 g_string_append_c(output
, ch
[1]);
674 for (end_char
= end_chars
; *end_char
!= '\0'; end_char
++) {
675 if (*ch
== *end_char
) {
677 * End character found:
678 * terminate this loop.
684 /* Normal character: append */
685 g_string_append_c(output
, *ch
);
691 *end_pos
= ch
- input
;
698 g_string_free(output
, TRUE
);
707 GString
*bt_common_shell_quote(const char *input
, bool with_single_quotes
)
709 GString
*output
= g_string_new(NULL
);
711 bool no_quote
= true;
717 if (strlen(input
) == 0) {
718 if (with_single_quotes
) {
719 g_string_assign(output
, "''");
725 for (ch
= input
; *ch
!= '\0'; ch
++) {
728 if (!g_ascii_isalpha(c
) && !g_ascii_isdigit(c
) && c
!= '_' &&
729 c
!= '@' && c
!= '%' && c
!= '+' && c
!= '=' &&
730 c
!= ':' && c
!= ',' && c
!= '.' && c
!= '/' &&
738 g_string_assign(output
, input
);
742 if (with_single_quotes
) {
743 g_string_assign(output
, "'");
746 for (ch
= input
; *ch
!= '\0'; ch
++) {
748 g_string_append(output
, "'\"'\"'");
750 g_string_append_c(output
, *ch
);
754 if (with_single_quotes
) {
755 g_string_append_c(output
, '\'');
763 bool bt_common_string_is_printable(const char *input
)
766 bool printable
= true;
767 BT_ASSERT_DBG(input
);
769 for (ch
= input
; *ch
!= '\0'; ch
++) {
770 if (!isprint((unsigned char) *ch
) && *ch
!= '\n' && *ch
!= '\r' &&
771 *ch
!= '\t' && *ch
!= '\v') {
782 void bt_common_destroy_lttng_live_url_parts(
783 struct bt_common_lttng_live_url_parts
*parts
)
790 g_string_free(parts
->proto
, TRUE
);
794 if (parts
->hostname
) {
795 g_string_free(parts
->hostname
, TRUE
);
796 parts
->hostname
= NULL
;
799 if (parts
->target_hostname
) {
800 g_string_free(parts
->target_hostname
, TRUE
);
801 parts
->target_hostname
= NULL
;
804 if (parts
->session_name
) {
805 g_string_free(parts
->session_name
, TRUE
);
806 parts
->session_name
= NULL
;
814 struct bt_common_lttng_live_url_parts
bt_common_parse_lttng_live_url(
815 const char *url
, char *error_buf
, size_t error_buf_size
)
817 struct bt_common_lttng_live_url_parts parts
;
818 const char *at
= url
;
822 memset(&parts
, 0, sizeof(parts
));
826 parts
.proto
= bt_common_string_until(at
, "", ":", &end_pos
);
827 if (!parts
.proto
|| parts
.proto
->len
== 0) {
829 snprintf(error_buf
, error_buf_size
, "Missing protocol");
835 if (strcmp(parts
.proto
->str
, "net") == 0) {
836 g_string_assign(parts
.proto
, "net4");
839 if (strcmp(parts
.proto
->str
, "net4") != 0 &&
840 strcmp(parts
.proto
->str
, "net6") != 0) {
842 snprintf(error_buf
, error_buf_size
,
843 "Unknown protocol: `%s`", parts
.proto
->str
);
849 if (at
[end_pos
] != ':') {
851 snprintf(error_buf
, error_buf_size
,
852 "Expecting `:` after `%s`", parts
.proto
->str
);
861 if (strncmp(at
, "://", 3) != 0) {
863 snprintf(error_buf
, error_buf_size
,
864 "Expecting `://` after protocol");
874 parts
.hostname
= bt_common_string_until(at
, "", ":/", &end_pos
);
875 if (!parts
.hostname
|| parts
.hostname
->len
== 0) {
877 snprintf(error_buf
, error_buf_size
, "Missing hostname");
883 if (at
[end_pos
] == ':') {
888 port
= bt_common_string_until(at
, "", "/", &end_pos
);
889 if (!port
|| port
->len
== 0) {
891 snprintf(error_buf
, error_buf_size
, "Missing port");
897 if (sscanf(port
->str
, "%d", &parts
.port
) != 1) {
899 snprintf(error_buf
, error_buf_size
,
900 "Invalid port: `%s`", port
->str
);
903 g_string_free(port
, TRUE
);
907 g_string_free(port
, TRUE
);
909 if (parts
.port
< 0 || parts
.port
>= 65536) {
911 snprintf(error_buf
, error_buf_size
,
912 "Invalid port: %d", parts
.port
);
919 if (at
[end_pos
] == '\0') {
920 /* Relay daemon hostname and ports provided only */
927 if (strncmp(at
, "/host/", 6) != 0) {
929 snprintf(error_buf
, error_buf_size
,
930 "Expecting `/host/` after hostname or port");
938 /* Target hostname */
939 parts
.target_hostname
= bt_common_string_until(at
, "", "/", &end_pos
);
940 if (!parts
.target_hostname
|| parts
.target_hostname
->len
== 0) {
942 snprintf(error_buf
, error_buf_size
,
943 "Missing target hostname");
949 if (at
[end_pos
] == '\0') {
951 snprintf(error_buf
, error_buf_size
,
952 "Missing `/` after target hostname (`%s`)",
953 parts
.target_hostname
->str
);
963 parts
.session_name
= bt_common_string_until(at
, "", "/", &end_pos
);
964 if (!parts
.session_name
|| parts
.session_name
->len
== 0) {
966 snprintf(error_buf
, error_buf_size
,
967 "Missing session name");
973 if (at
[end_pos
] == '/') {
975 snprintf(error_buf
, error_buf_size
,
976 "Unexpected `/` after session name (`%s`)",
977 parts
.session_name
->str
);
986 bt_common_destroy_lttng_live_url_parts(&parts
);
993 void bt_common_normalize_star_glob_pattern(char *pattern
)
997 bool got_star
= false;
1001 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
1005 /* Avoid consecutive stars. */
1012 /* Copy backslash character. */
1027 /* Copy single character. */
1037 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
1039 return (p
- pattern
) == pattern_len
|| *p
== '\0';
1043 * Globbing matching function with the star feature only (`?` and
1044 * character sets are not supported). This matches `candidate` (plain
1045 * string) against `pattern`. A literal star can be escaped with `\` in
1048 * `pattern_len` or `candidate_len` can be greater than the actual
1049 * string length of `pattern` or `candidate` if the string is
1053 bool bt_common_star_glob_match(const char *pattern
, size_t pattern_len
,
1054 const char *candidate
, size_t candidate_len
) {
1055 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
1056 bool got_a_star
= false;
1063 * The concept here is to retry a match in the specific case
1064 * where we already got a star. The retry position for the
1065 * pattern is just after the most recent star, and the retry
1066 * position for the candidate is the character following the
1067 * last try's first character.
1071 * candidate: hi ev every onyx one
1073 * pattern: hi*every*one
1076 * candidate: hi ev every onyx one
1078 * pattern: hi*every*one
1081 * candidate: hi ev every onyx one
1083 * pattern: hi*every*one
1086 * candidate: hi ev every onyx one
1088 * pattern: hi*every*one
1091 * candidate: hi ev every onyx one
1093 * pattern: hi*every*one
1096 * candidate: hi ev every onyx one
1098 * pattern: hi*every*one
1101 * candidate: hi ev every onyx one
1103 * pattern: hi*every*one
1106 * candidate: hi ev every onyx one
1108 * pattern: hi*every*one
1111 * candidate: hi ev every onyx one
1113 * pattern: hi*every*one
1116 * candidate: hi ev every onyx one
1118 * pattern: hi*every*one
1121 * candidate: hi ev every onyx one
1123 * pattern: hi*every*one
1126 * candidate: hi ev every onyx one
1128 * pattern: hi*every*one
1131 * candidate: hi ev every onyx one
1133 * pattern: hi*every*one
1136 * candidate: hi ev every onyx one
1138 * pattern: hi*every*one
1141 * candidate: hi ev every onyx one
1143 * pattern: hi*every*one
1146 * candidate: hi ev every onyx one
1148 * pattern: hi*every*one
1151 * candidate: hi ev every onyx one
1153 * pattern: hi*every*one
1156 * candidate: hi ev every onyx one
1158 * pattern: hi*every*one
1161 * candidate: hi ev every onyx one
1163 * pattern: hi*every*one
1166 * candidate: hi ev every onyx one
1168 * pattern: hi*every*one
1171 * candidate: hi ev every onyx one
1173 * pattern: hi*every*one
1176 * candidate: hi ev every onyx one
1178 * pattern: hi*every*one
1181 * candidate: hi ev every onyx one
1183 * pattern: hi*every*one
1186 * candidate: hi ev every onyx one
1188 * pattern: hi*every*one
1191 * candidate: hi ev every onyx one
1193 * pattern: hi*every*one
1196 * candidate: hi ev every onyx one
1198 * pattern: hi*every*one
1201 * candidate: hi ev every onyx one
1203 * pattern: hi*every*one
1206 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
1209 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1210 goto end_of_pattern
;
1218 * Our first try starts at the current candidate
1219 * character and after the star in the pattern.
1224 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
1226 * Star at the end of the pattern at
1227 * this point: automatic match.
1234 /* Go to escaped character. */
1238 * Fall through the default case which compares
1239 * the escaped character now.
1243 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
1246 /* Character mismatch OR end of pattern. */
1249 * We didn't get any star yet,
1250 * so this first mismatch
1251 * automatically makes the whole
1258 * Next try: next candidate character,
1259 * original pattern character (following
1260 * the most recent star).
1268 /* Next pattern and candidate characters. */
1274 * We checked every candidate character and we're still in a
1275 * success state: the only pattern character allowed to remain
1278 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1283 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);
1288 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1291 GString
*norm_path
= NULL
;
1295 tmp
= _fullpath(NULL
, path
, PATH_MAX
);
1300 norm_path
= g_string_new(tmp
);
1308 g_string_free(norm_path
, TRUE
);
1317 void append_path_parts(const char *path
, GPtrArray
*parts
)
1319 const char *ch
= path
;
1320 const char *last
= path
;
1323 if (*ch
== G_DIR_SEPARATOR
|| *ch
== '\0') {
1324 if (ch
- last
> 0) {
1325 GString
*part
= g_string_new(NULL
);
1328 g_string_append_len(part
, last
, ch
- last
);
1329 g_ptr_array_add(parts
, part
);
1344 void destroy_gstring(void *gstring
)
1346 (void) g_string_free(gstring
, TRUE
);
1350 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1354 GPtrArray
*parts
= NULL
;
1357 norm_path
= g_string_new(G_DIR_SEPARATOR_S
);
1362 parts
= g_ptr_array_new_with_free_func(destroy_gstring
);
1367 if (path
[0] != G_DIR_SEPARATOR
) {
1368 /* Relative path: start with working directory */
1370 append_path_parts(wd
, parts
);
1372 gchar
*cd
= g_get_current_dir();
1374 append_path_parts(cd
, parts
);
1379 /* Append parts of the path parameter */
1380 append_path_parts(path
, parts
);
1382 /* Resolve special `..` and `.` parts */
1383 for (i
= 0; i
< parts
->len
; i
++) {
1384 GString
*part
= g_ptr_array_index(parts
, i
);
1386 if (strcmp(part
->str
, "..") == 0) {
1389 * First part of absolute path is `..`:
1395 /* Remove `..` and previous part */
1396 g_ptr_array_remove_index(parts
, i
- 1);
1397 g_ptr_array_remove_index(parts
, i
- 1);
1399 } else if (strcmp(part
->str
, ".") == 0) {
1401 g_ptr_array_remove_index(parts
, i
);
1406 /* Create normalized path with what's left */
1407 for (i
= 0; i
< parts
->len
; i
++) {
1408 GString
*part
= g_ptr_array_index(parts
, i
);
1410 g_string_append(norm_path
, part
->str
);
1412 if (i
< parts
->len
- 1) {
1413 g_string_append_c(norm_path
, G_DIR_SEPARATOR
);
1421 g_string_free(norm_path
, TRUE
);
1427 g_ptr_array_free(parts
, TRUE
);
1435 size_t bt_common_get_page_size(int log_level
)
1439 page_size
= bt_sysconf(_SC_PAGESIZE
);
1440 if (page_size
< 0) {
1441 BT_LOGF("Cannot get system's page size: ret=%d",
1449 #define BUF_STD_APPEND(...) \
1451 char _tmp_fmt[64]; \
1453 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1454 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1455 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1456 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1457 _Pragma("GCC diagnostic push") \
1458 _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \
1459 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
1460 _Pragma("GCC diagnostic pop") \
1461 BT_ASSERT_DBG(_count >= 0); \
1462 *buf_ch += MIN(_count, _size); \
1465 #define BUF_STD_APPEND_SINGLE_ARG(_type) \
1467 _type _arg = va_arg(*args, _type); \
1468 BUF_STD_APPEND(_arg); \
1471 static inline void handle_conversion_specifier_std(char *buf
, char **buf_ch
,
1472 size_t buf_size
, const char **out_fmt_ch
, va_list *args
)
1474 const char *fmt_ch
= *out_fmt_ch
;
1475 enum LENGTH_MODIFIER
{
1483 } length_mod
= LENGTH_MOD_NONE
;
1488 if (*fmt_ch
== '%') {
1514 if (*fmt_ch
< '0' || *fmt_ch
> '9') {
1522 if (*fmt_ch
== '.') {
1526 if (*fmt_ch
< '0' || *fmt_ch
> '9') {
1534 /* format (PRI*64) */
1535 if (strncmp(fmt_ch
, PRId64
, sizeof(PRId64
) - 1) == 0) {
1536 fmt_ch
+= sizeof(PRId64
) - 1;
1537 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1539 } else if (strncmp(fmt_ch
, PRIu64
, sizeof(PRIu64
) - 1) == 0) {
1540 fmt_ch
+= sizeof(PRIu64
) - 1;
1541 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1543 } else if (strncmp(fmt_ch
, PRIx64
, sizeof(PRIx64
) - 1) == 0) {
1544 fmt_ch
+= sizeof(PRIx64
) - 1;
1545 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1547 } else if (strncmp(fmt_ch
, PRIX64
, sizeof(PRIX64
) - 1) == 0) {
1548 fmt_ch
+= sizeof(PRIX64
) - 1;
1549 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1551 } else if (strncmp(fmt_ch
, PRIo64
, sizeof(PRIo64
) - 1) == 0) {
1552 fmt_ch
+= sizeof(PRIo64
) - 1;
1553 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1555 } else if (strncmp(fmt_ch
, PRIi64
, sizeof(PRIi64
) - 1) == 0) {
1556 fmt_ch
+= sizeof(PRIi64
) - 1;
1557 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1564 length_mod
= LENGTH_MOD_H
;
1567 if (*fmt_ch
== 'h') {
1568 length_mod
= LENGTH_MOD_HH
;
1574 length_mod
= LENGTH_MOD_LOW_L
;
1577 if (*fmt_ch
== 'l') {
1578 length_mod
= LENGTH_MOD_LOW_LL
;
1584 length_mod
= LENGTH_MOD_UP_L
;
1588 length_mod
= LENGTH_MOD_Z
;
1601 switch (length_mod
) {
1602 case LENGTH_MOD_NONE
:
1603 case LENGTH_MOD_LOW_L
:
1604 BUF_STD_APPEND_SINGLE_ARG(int);
1614 switch (length_mod
) {
1615 case LENGTH_MOD_NONE
:
1616 BUF_STD_APPEND_SINGLE_ARG(char *);
1618 case LENGTH_MOD_LOW_L
:
1619 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1629 switch (length_mod
) {
1630 case LENGTH_MOD_NONE
:
1633 BUF_STD_APPEND_SINGLE_ARG(int);
1635 case LENGTH_MOD_LOW_L
:
1636 BUF_STD_APPEND_SINGLE_ARG(long);
1638 case LENGTH_MOD_LOW_LL
:
1639 BUF_STD_APPEND_SINGLE_ARG(long long);
1642 BUF_STD_APPEND_SINGLE_ARG(size_t);
1654 switch (length_mod
) {
1655 case LENGTH_MOD_NONE
:
1658 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1660 case LENGTH_MOD_LOW_L
:
1661 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1663 case LENGTH_MOD_LOW_LL
:
1664 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1667 BUF_STD_APPEND_SINGLE_ARG(size_t);
1681 switch (length_mod
) {
1682 case LENGTH_MOD_NONE
:
1683 BUF_STD_APPEND_SINGLE_ARG(double);
1685 case LENGTH_MOD_UP_L
:
1686 BUF_STD_APPEND_SINGLE_ARG(long double);
1695 if (length_mod
== LENGTH_MOD_NONE
) {
1696 BUF_STD_APPEND_SINGLE_ARG(void *);
1706 *out_fmt_ch
= fmt_ch
;
1710 void bt_common_custom_vsnprintf(char *buf
, size_t buf_size
,
1712 bt_common_handle_custom_specifier_func handle_specifier
,
1713 void *priv_data
, const char *fmt
, va_list *args
)
1715 const char *fmt_ch
= fmt
;
1721 while (*fmt_ch
!= '\0') {
1724 BT_ASSERT_DBG(fmt_ch
[1] != '\0');
1726 if (fmt_ch
[1] == intro
) {
1727 handle_specifier(priv_data
, &buf_ch
,
1728 buf_size
- (size_t) (buf_ch
- buf
),
1731 handle_conversion_specifier_std(buf
, &buf_ch
,
1732 buf_size
, &fmt_ch
, args
);
1735 if (buf_ch
>= buf
+ buf_size
- 1) {
1742 if (buf_ch
>= buf
+ buf_size
- 1) {
1754 void bt_common_custom_snprintf(char *buf
, size_t buf_size
,
1756 bt_common_handle_custom_specifier_func handle_specifier
,
1757 void *priv_data
, const char *fmt
, ...)
1760 va_start(args
, fmt
);
1761 bt_common_custom_vsnprintf(buf
, buf_size
, intro
, handle_specifier
,
1762 priv_data
, fmt
, &args
);
1767 void bt_common_sep_digits(char *str
, unsigned int digits_per_group
, char sep
)
1776 BT_ASSERT_DBG(digits_per_group
> 0);
1777 BT_ASSERT_DBG(sep
!= '\0');
1779 /* Compute new length of `str` */
1780 orig_len
= strlen(str
);
1781 BT_ASSERT_DBG(orig_len
> 0);
1782 sep_count
= (orig_len
- 1) / digits_per_group
;
1783 new_len
= strlen(str
) + sep_count
;
1786 * Do the work in place. Have the reading pointer `rd` start at
1787 * the end of the original string, and the writing pointer `wr`
1788 * start at the end of the new string, making sure to also put a
1789 * null character there.
1791 rd
= str
+ orig_len
- 1;
1797 * Here's what the process looks like (3 digits per group):
1801 * Destination: 12345678#8
1806 * Destination: 1234567878
1811 * Destination: 1234567678
1816 * Destination: 123456,678
1821 * Destination: 123455,678
1826 * Destination: 123445,678
1831 * Destination: 123345,678
1836 * Destination: 12,345,678
1841 * Destination: 12,345,678
1846 * Destination: 12,345,678
1849 while (rd
!= str
- 1) {
1850 if (i
== digits_per_group
) {
1852 * Time to append the separator: decrement `wr`,
1853 * but keep `rd` as is.
1861 /* Copy read-side character to write-side character */
1870 GString
*bt_common_fold(const char *str
, unsigned int total_length
,
1871 unsigned int indent
)
1873 const unsigned int content_length
= total_length
- indent
;
1874 GString
*folded
= g_string_new(NULL
);
1875 GString
*tmp_line
= g_string_new(NULL
);
1876 gchar
**lines
= NULL
;
1877 gchar
**line_words
= NULL
;
1878 gchar
* const *line
;
1882 BT_ASSERT_DBG(indent
< total_length
);
1883 BT_ASSERT_DBG(tmp_line
);
1884 BT_ASSERT_DBG(folded
);
1886 if (strlen(str
) == 0) {
1887 /* Empty input string: empty output string */
1892 lines
= g_strsplit(str
, "\n", 0);
1893 BT_ASSERT_DBG(lines
);
1895 /* For each source line */
1896 for (line
= lines
; *line
; line
++) {
1897 gchar
* const *word
;
1900 * Append empty line without indenting if source line is
1903 if (strlen(*line
) == 0) {
1904 g_string_append_c(folded
, '\n');
1909 line_words
= g_strsplit(*line
, " ", 0);
1910 BT_ASSERT_DBG(line_words
);
1913 * Indent for first line (we know there's at least one
1914 * word at this point).
1916 for (i
= 0; i
< indent
; i
++) {
1917 g_string_append_c(folded
, ' ');
1920 /* Append words, folding when necessary */
1921 g_string_assign(tmp_line
, "");
1923 for (word
= line_words
; *word
; word
++) {
1925 * `tmp_line->len > 0` is in the condition so
1926 * that words that are larger than
1927 * `content_length` are at least written on
1930 * `tmp_line->len - 1` because the temporary
1931 * line always contains a trailing space which
1932 * won't be part of the line if we fold.
1934 if (tmp_line
->len
> 0 &&
1935 tmp_line
->len
- 1 + strlen(*word
) >= content_length
) {
1936 /* Fold (without trailing space) */
1937 g_string_append_len(folded
,
1938 tmp_line
->str
, tmp_line
->len
- 1);
1939 g_string_append_c(folded
, '\n');
1941 /* Indent new line */
1942 for (i
= 0; i
< indent
; i
++) {
1943 g_string_append_c(folded
, ' ');
1946 g_string_assign(tmp_line
, "");
1949 /* Append current word and space to temporary line */
1950 g_string_append(tmp_line
, *word
);
1951 g_string_append_c(tmp_line
, ' ');
1954 /* Append last line if any, without trailing space */
1955 if (tmp_line
->len
> 0) {
1956 g_string_append_len(folded
, tmp_line
->str
,
1960 /* Append source newline */
1961 g_string_append_c(folded
, '\n');
1963 /* Free array of this line's words */
1964 g_strfreev(line_words
);
1968 /* Remove trailing newline if any */
1969 if (folded
->str
[folded
->len
- 1] == '\n') {
1970 g_string_truncate(folded
, folded
->len
- 1);
1978 BT_ASSERT_DBG(!line_words
);
1981 g_string_free(tmp_line
, TRUE
);
1989 int bt_common_get_term_size(unsigned int *width
, unsigned int *height
)
1991 /* Not supported on Windows yet */
1994 #else /* __MINGW32__ */
1996 int bt_common_get_term_size(unsigned int *width
, unsigned int *height
)
1999 struct winsize winsize
;
2001 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winsize
) < 0) {
2007 *width
= (unsigned int) winsize
.ws_col
;
2011 *height
= (unsigned int) winsize
.ws_row
;
2017 #endif /* __MINGW32__ */
2020 int bt_common_g_string_append_printf(GString
*str
, const char *fmt
, ...)
2023 gsize len
, allocated_len
, available_len
;
2026 /* str->len excludes \0. */
2028 /* Explicitly exclude \0. */
2029 allocated_len
= str
->allocated_len
- 1;
2030 available_len
= allocated_len
- len
;
2032 str
->len
= allocated_len
;
2034 print_len
= vsnprintf(str
->str
+ len
, available_len
+ 1, fmt
, ap
);
2036 if (print_len
< 0) {
2039 if (G_UNLIKELY(available_len
< print_len
)) {
2041 g_string_set_size(str
, len
+ print_len
);
2043 print_len
= vsprintf(str
->str
+ len
, fmt
, ap
);
2046 str
->len
= len
+ print_len
;
2052 int bt_common_append_file_content_to_g_string(GString
*str
, FILE *fp
)
2054 const size_t chunk_size
= 4096;
2058 gsize orig_len
= str
->len
;
2062 buf
= g_malloc(chunk_size
);
2078 read_len
= fread(buf
, 1, chunk_size
, fp
);
2079 g_string_append_len(str
, buf
, read_len
);
2084 /* Remove what was appended */
2085 g_string_truncate(str
, orig_len
);
2093 void bt_common_abort(void)
2095 static const char * const exec_on_abort_env_name
=
2096 "BABELTRACE_EXEC_ON_ABORT";
2097 const char *env_exec_on_abort
;
2099 env_exec_on_abort
= getenv(exec_on_abort_env_name
);
2100 if (env_exec_on_abort
) {
2101 if (bt_common_is_setuid_setgid()) {
2105 (void) g_spawn_command_line_sync(env_exec_on_abort
,
2106 NULL
, NULL
, NULL
, NULL
);