2 * Babeltrace common functions
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
25 #define BT_LOG_TAG "COMMON"
31 #include <sys/types.h>
34 #include "common/assert.h"
42 #include "common/macros.h"
43 #include "common/common.h"
44 #include "compat/unistd.h"
50 #define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace2/plugins"
51 #define HOME_ENV_VAR "HOME"
52 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins"
54 static const char *bt_common_color_code_reset
= "";
55 static const char *bt_common_color_code_bold
= "";
56 static const char *bt_common_color_code_fg_default
= "";
57 static const char *bt_common_color_code_fg_red
= "";
58 static const char *bt_common_color_code_fg_green
= "";
59 static const char *bt_common_color_code_fg_yellow
= "";
60 static const char *bt_common_color_code_fg_blue
= "";
61 static const char *bt_common_color_code_fg_magenta
= "";
62 static const char *bt_common_color_code_fg_cyan
= "";
63 static const char *bt_common_color_code_fg_light_gray
= "";
64 static const char *bt_common_color_code_bg_default
= "";
65 static const char *bt_common_color_code_bg_red
= "";
66 static const char *bt_common_color_code_bg_green
= "";
67 static const char *bt_common_color_code_bg_yellow
= "";
68 static const char *bt_common_color_code_bg_blue
= "";
69 static const char *bt_common_color_code_bg_magenta
= "";
70 static const char *bt_common_color_code_bg_cyan
= "";
71 static const char *bt_common_color_code_bg_light_gray
= "";
74 void __attribute__((constructor
)) bt_common_color_ctor(void)
76 if (bt_common_colors_supported()) {
77 bt_common_color_code_reset
= BT_COMMON_COLOR_RESET
;
78 bt_common_color_code_bold
= BT_COMMON_COLOR_BOLD
;
79 bt_common_color_code_fg_default
= BT_COMMON_COLOR_FG_DEFAULT
;
80 bt_common_color_code_fg_red
= BT_COMMON_COLOR_FG_RED
;
81 bt_common_color_code_fg_green
= BT_COMMON_COLOR_FG_GREEN
;
82 bt_common_color_code_fg_yellow
= BT_COMMON_COLOR_FG_YELLOW
;
83 bt_common_color_code_fg_blue
= BT_COMMON_COLOR_FG_BLUE
;
84 bt_common_color_code_fg_magenta
= BT_COMMON_COLOR_FG_MAGENTA
;
85 bt_common_color_code_fg_cyan
= BT_COMMON_COLOR_FG_CYAN
;
86 bt_common_color_code_fg_light_gray
= BT_COMMON_COLOR_FG_LIGHT_GRAY
;
87 bt_common_color_code_bg_default
= BT_COMMON_COLOR_BG_DEFAULT
;
88 bt_common_color_code_bg_red
= BT_COMMON_COLOR_BG_RED
;
89 bt_common_color_code_bg_green
= BT_COMMON_COLOR_BG_GREEN
;
90 bt_common_color_code_bg_yellow
= BT_COMMON_COLOR_BG_YELLOW
;
91 bt_common_color_code_bg_blue
= BT_COMMON_COLOR_BG_BLUE
;
92 bt_common_color_code_bg_magenta
= BT_COMMON_COLOR_BG_MAGENTA
;
93 bt_common_color_code_bg_cyan
= BT_COMMON_COLOR_BG_CYAN
;
94 bt_common_color_code_bg_light_gray
= BT_COMMON_COLOR_BG_LIGHT_GRAY
;
99 const char *bt_common_get_system_plugin_path(void)
101 return SYSTEM_PLUGIN_PATH
;
106 bool bt_common_is_setuid_setgid(void)
110 #else /* __MINGW32__ */
112 bool bt_common_is_setuid_setgid(void)
114 return (geteuid() != getuid() || getegid() != getgid());
116 #endif /* __MINGW32__ */
119 char *bt_secure_getenv(const char *name
)
121 if (bt_common_is_setuid_setgid()) {
122 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
123 "name=\"%s\"", name
);
131 const char *bt_get_home_dir(void)
133 return g_get_home_dir();
135 #else /* __MINGW32__ */
137 const char *bt_get_home_dir(void)
142 val
= bt_secure_getenv(HOME_ENV_VAR
);
146 /* Fallback on password file. */
147 pwd
= getpwuid(getuid());
155 #endif /* __MINGW32__ */
158 char *bt_common_get_home_plugin_path(void)
161 const char *home_dir
;
164 home_dir
= bt_get_home_dir();
169 length
= strlen(home_dir
) + strlen(HOME_PLUGIN_SUBPATH
) + 1;
171 if (length
>= PATH_MAX
) {
172 BT_LOGW("Home directory path is too long: "
173 "length=%zu, max-length=%u", length
, PATH_MAX
);
177 path
= malloc(PATH_MAX
);
182 strcpy(path
, home_dir
);
183 strcat(path
, HOME_PLUGIN_SUBPATH
);
190 int bt_common_append_plugin_path_dirs(const char *paths
, GPtrArray
*dirs
)
195 size_t init_dirs_len
;
198 init_dirs_len
= dirs
->len
;
201 /* Nothing to append */
206 end
= paths
+ strlen(paths
);
210 const char *next_sep
;
212 next_sep
= strchr(at
, G_SEARCHPATH_SEPARATOR
);
213 if (next_sep
== at
) {
215 * Empty path: try next character (supported
216 * to conform to the typical parsing of $PATH).
220 } else if (!next_sep
) {
221 /* No more separator: use the remaining */
222 next_sep
= paths
+ strlen(paths
);
225 path
= g_string_new(NULL
);
230 g_string_append_len(path
, at
, next_sep
- at
);
232 g_ptr_array_add(dirs
, path
);
240 /* Remove the new entries in dirs */
241 while (dirs
->len
> init_dirs_len
) {
242 g_ptr_array_remove_index(dirs
, init_dirs_len
);
250 bool isarealtty(int fd
)
253 struct stat tty_stats
;
260 if (fstat(fd
, &tty_stats
) == 0) {
261 if (!S_ISCHR(tty_stats
.st_mode
)) {
262 /* Not a character device: not a TTY */
274 bool bt_common_colors_supported(void)
276 static bool supports_colors
= false;
277 static bool supports_colors_set
= false;
278 const char *term_env_var
;
279 const char *term_color_env_var
;
281 if (supports_colors_set
) {
285 supports_colors_set
= true;
288 * `BABELTRACE_TERM_COLOR` environment variable always overrides
289 * the automatic color support detection.
291 term_color_env_var
= getenv("BABELTRACE_TERM_COLOR");
292 if (term_color_env_var
) {
293 if (g_ascii_strcasecmp(term_color_env_var
, "always") == 0) {
295 supports_colors
= true;
296 } else if (g_ascii_strcasecmp(term_color_env_var
, "never") == 0) {
297 /* Force no colors */
302 /* We need a compatible, known terminal */
303 term_env_var
= getenv("TERM");
308 if (strncmp(term_env_var
, "xterm", 5) != 0 &&
309 strncmp(term_env_var
, "rxvt", 4) != 0 &&
310 strncmp(term_env_var
, "konsole", 7) != 0 &&
311 strncmp(term_env_var
, "gnome", 5) != 0 &&
312 strncmp(term_env_var
, "screen", 5) != 0 &&
313 strncmp(term_env_var
, "tmux", 4) != 0 &&
314 strncmp(term_env_var
, "putty", 5) != 0) {
318 /* Both standard output and error streams need to be TTYs */
319 if (!isarealtty(STDOUT_FILENO
) || !isarealtty(STDERR_FILENO
)) {
323 supports_colors
= true;
326 return supports_colors
;
330 const char *bt_common_color_reset(void)
332 return bt_common_color_code_reset
;
336 const char *bt_common_color_bold(void)
338 return bt_common_color_code_bold
;
342 const char *bt_common_color_fg_default(void)
344 return bt_common_color_code_fg_default
;
348 const char *bt_common_color_fg_red(void)
350 return bt_common_color_code_fg_red
;
354 const char *bt_common_color_fg_green(void)
356 return bt_common_color_code_fg_green
;
360 const char *bt_common_color_fg_yellow(void)
362 return bt_common_color_code_fg_yellow
;
366 const char *bt_common_color_fg_blue(void)
368 return bt_common_color_code_fg_blue
;
372 const char *bt_common_color_fg_magenta(void)
374 return bt_common_color_code_fg_magenta
;
378 const char *bt_common_color_fg_cyan(void)
380 return bt_common_color_code_fg_cyan
;
384 const char *bt_common_color_fg_light_gray(void)
386 return bt_common_color_code_fg_light_gray
;
390 const char *bt_common_color_bg_default(void)
392 return bt_common_color_code_bg_default
;
396 const char *bt_common_color_bg_red(void)
398 return bt_common_color_code_bg_red
;
402 const char *bt_common_color_bg_green(void)
404 return bt_common_color_code_bg_green
;
408 const char *bt_common_color_bg_yellow(void)
410 return bt_common_color_code_bg_yellow
;
414 const char *bt_common_color_bg_blue(void)
416 return bt_common_color_code_bg_blue
;
420 const char *bt_common_color_bg_magenta(void)
422 return bt_common_color_code_bg_magenta
;
426 const char *bt_common_color_bg_cyan(void)
428 return bt_common_color_code_bg_cyan
;
432 const char *bt_common_color_bg_light_gray(void)
434 return bt_common_color_code_bg_light_gray
;
438 GString
*bt_common_string_until(const char *input
, const char *escapable_chars
,
439 const char *end_chars
, size_t *end_pos
)
441 GString
*output
= g_string_new(NULL
);
444 const char *end_char
;
450 for (ch
= input
; *ch
!= '\0'; ch
++) {
452 bool continue_loop
= false;
455 /* `\` at the end of the string: append `\` */
456 g_string_append_c(output
, *ch
);
461 for (es_char
= escapable_chars
; *es_char
!= '\0'; es_char
++) {
462 if (ch
[1] == *es_char
) {
464 * `\` followed by an escapable
465 * character: append the escaped
468 g_string_append_c(output
, ch
[1]);
470 continue_loop
= true;
480 * `\` followed by a non-escapable character:
481 * append `\` and the character.
483 g_string_append_c(output
, *ch
);
484 g_string_append_c(output
, ch
[1]);
488 for (end_char
= end_chars
; *end_char
!= '\0'; end_char
++) {
489 if (*ch
== *end_char
) {
491 * End character found:
492 * terminate this loop.
498 /* Normal character: append */
499 g_string_append_c(output
, *ch
);
505 *end_pos
= ch
- input
;
512 g_string_free(output
, TRUE
);
520 GString
*bt_common_shell_quote(const char *input
, bool with_single_quotes
)
522 GString
*output
= g_string_new(NULL
);
524 bool no_quote
= true;
530 if (strlen(input
) == 0) {
531 if (with_single_quotes
) {
532 g_string_assign(output
, "''");
538 for (ch
= input
; *ch
!= '\0'; ch
++) {
541 if (!g_ascii_isalpha(c
) && !g_ascii_isdigit(c
) && c
!= '_' &&
542 c
!= '@' && c
!= '%' && c
!= '+' && c
!= '=' &&
543 c
!= ':' && c
!= ',' && c
!= '.' && c
!= '/' &&
551 g_string_assign(output
, input
);
555 if (with_single_quotes
) {
556 g_string_assign(output
, "'");
559 for (ch
= input
; *ch
!= '\0'; ch
++) {
561 g_string_append(output
, "'\"'\"'");
563 g_string_append_c(output
, *ch
);
567 if (with_single_quotes
) {
568 g_string_append_c(output
, '\'');
576 bool bt_common_string_is_printable(const char *input
)
579 bool printable
= true;
582 for (ch
= input
; *ch
!= '\0'; ch
++) {
583 if (!isprint(*ch
) && *ch
!= '\n' && *ch
!= '\r' &&
584 *ch
!= '\t' && *ch
!= '\v') {
595 void bt_common_destroy_lttng_live_url_parts(
596 struct bt_common_lttng_live_url_parts
*parts
)
603 g_string_free(parts
->proto
, TRUE
);
607 if (parts
->hostname
) {
608 g_string_free(parts
->hostname
, TRUE
);
609 parts
->hostname
= NULL
;
612 if (parts
->target_hostname
) {
613 g_string_free(parts
->target_hostname
, TRUE
);
614 parts
->target_hostname
= NULL
;
617 if (parts
->session_name
) {
618 g_string_free(parts
->session_name
, TRUE
);
619 parts
->session_name
= NULL
;
627 struct bt_common_lttng_live_url_parts
bt_common_parse_lttng_live_url(
628 const char *url
, char *error_buf
, size_t error_buf_size
)
630 struct bt_common_lttng_live_url_parts parts
;
631 const char *at
= url
;
635 memset(&parts
, 0, sizeof(parts
));
639 parts
.proto
= bt_common_string_until(at
, "", ":", &end_pos
);
640 if (!parts
.proto
|| parts
.proto
->len
== 0) {
642 snprintf(error_buf
, error_buf_size
, "Missing protocol");
648 if (strcmp(parts
.proto
->str
, "net") == 0) {
649 g_string_assign(parts
.proto
, "net4");
652 if (strcmp(parts
.proto
->str
, "net4") != 0 &&
653 strcmp(parts
.proto
->str
, "net6") != 0) {
655 snprintf(error_buf
, error_buf_size
,
656 "Unknown protocol: `%s`", parts
.proto
->str
);
662 if (at
[end_pos
] != ':') {
664 snprintf(error_buf
, error_buf_size
,
665 "Expecting `:` after `%s`", parts
.proto
->str
);
674 if (strncmp(at
, "://", 3) != 0) {
676 snprintf(error_buf
, error_buf_size
,
677 "Expecting `://` after protocol");
686 parts
.hostname
= bt_common_string_until(at
, "", ":/", &end_pos
);
687 if (!parts
.hostname
|| parts
.hostname
->len
== 0) {
689 snprintf(error_buf
, error_buf_size
, "Missing hostname");
695 if (at
[end_pos
] == ':') {
700 port
= bt_common_string_until(at
, "", "/", &end_pos
);
701 if (!port
|| port
->len
== 0) {
703 snprintf(error_buf
, error_buf_size
, "Missing port");
709 if (sscanf(port
->str
, "%d", &parts
.port
) != 1) {
711 snprintf(error_buf
, error_buf_size
,
712 "Invalid port: `%s`", port
->str
);
715 g_string_free(port
, TRUE
);
719 g_string_free(port
, TRUE
);
721 if (parts
.port
< 0 || parts
.port
>= 65536) {
723 snprintf(error_buf
, error_buf_size
,
724 "Invalid port: %d", parts
.port
);
731 if (at
[end_pos
] == '\0') {
738 if (strncmp(at
, "/host/", 6) != 0) {
740 snprintf(error_buf
, error_buf_size
,
741 "Expecting `/host/` after hostname or port");
749 /* Target hostname */
750 parts
.target_hostname
= bt_common_string_until(at
, "", "/", &end_pos
);
751 if (!parts
.target_hostname
|| parts
.target_hostname
->len
== 0) {
753 snprintf(error_buf
, error_buf_size
,
754 "Missing target hostname");
760 if (at
[end_pos
] == '\0') {
767 parts
.session_name
= bt_common_string_until(at
, "", "/", &end_pos
);
768 if (!parts
.session_name
|| parts
.session_name
->len
== 0) {
770 snprintf(error_buf
, error_buf_size
,
771 "Missing session name");
777 if (at
[end_pos
] == '/') {
779 snprintf(error_buf
, error_buf_size
,
780 "Unexpected `/` after session name (`%s`)",
781 parts
.session_name
->str
);
790 bt_common_destroy_lttng_live_url_parts(&parts
);
797 void bt_common_normalize_star_glob_pattern(char *pattern
)
801 bool got_star
= false;
805 for (p
= pattern
, np
= pattern
; *p
!= '\0'; p
++) {
809 /* Avoid consecutive stars. */
816 /* Copy backslash character. */
831 /* Copy single character. */
841 bool at_end_of_pattern(const char *p
, const char *pattern
, size_t pattern_len
)
843 return (p
- pattern
) == pattern_len
|| *p
== '\0';
847 * Globbing matching function with the star feature only (`?` and
848 * character sets are not supported). This matches `candidate` (plain
849 * string) against `pattern`. A literal star can be escaped with `\` in
852 * `pattern_len` or `candidate_len` can be greater than the actual
853 * string length of `pattern` or `candidate` if the string is
857 bool bt_common_star_glob_match(const char *pattern
, size_t pattern_len
,
858 const char *candidate
, size_t candidate_len
) {
859 const char *retry_c
= candidate
, *retry_p
= pattern
, *c
, *p
;
860 bool got_a_star
= false;
867 * The concept here is to retry a match in the specific case
868 * where we already got a star. The retry position for the
869 * pattern is just after the most recent star, and the retry
870 * position for the candidate is the character following the
871 * last try's first character.
875 * candidate: hi ev every onyx one
877 * pattern: hi*every*one
880 * candidate: hi ev every onyx one
882 * pattern: hi*every*one
885 * candidate: hi ev every onyx one
887 * pattern: hi*every*one
890 * candidate: hi ev every onyx one
892 * pattern: hi*every*one
895 * candidate: hi ev every onyx one
897 * pattern: hi*every*one
900 * candidate: hi ev every onyx one
902 * pattern: hi*every*one
905 * candidate: hi ev every onyx one
907 * pattern: hi*every*one
910 * candidate: hi ev every onyx one
912 * pattern: hi*every*one
915 * candidate: hi ev every onyx one
917 * pattern: hi*every*one
920 * candidate: hi ev every onyx one
922 * pattern: hi*every*one
925 * candidate: hi ev every onyx one
927 * pattern: hi*every*one
930 * candidate: hi ev every onyx one
932 * pattern: hi*every*one
935 * candidate: hi ev every onyx one
937 * pattern: hi*every*one
940 * candidate: hi ev every onyx one
942 * pattern: hi*every*one
945 * candidate: hi ev every onyx one
947 * pattern: hi*every*one
950 * candidate: hi ev every onyx one
952 * pattern: hi*every*one
955 * candidate: hi ev every onyx one
957 * pattern: hi*every*one
960 * candidate: hi ev every onyx one
962 * pattern: hi*every*one
965 * candidate: hi ev every onyx one
967 * pattern: hi*every*one
970 * candidate: hi ev every onyx one
972 * pattern: hi*every*one
975 * candidate: hi ev every onyx one
977 * pattern: hi*every*one
980 * candidate: hi ev every onyx one
982 * pattern: hi*every*one
985 * candidate: hi ev every onyx one
987 * pattern: hi*every*one
990 * candidate: hi ev every onyx one
992 * pattern: hi*every*one
995 * candidate: hi ev every onyx one
997 * pattern: hi*every*one
1000 * candidate: hi ev every onyx one
1002 * pattern: hi*every*one
1005 * candidate: hi ev every onyx one
1007 * pattern: hi*every*one
1010 while ((c
- candidate
) < candidate_len
&& *c
!= '\0') {
1013 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1014 goto end_of_pattern
;
1022 * Our first try starts at the current candidate
1023 * character and after the star in the pattern.
1028 if (at_end_of_pattern(retry_p
, pattern
, pattern_len
)) {
1030 * Star at the end of the pattern at
1031 * this point: automatic match.
1038 /* Go to escaped character. */
1042 * Fall through the default case which compares
1043 * the escaped character now.
1047 if (at_end_of_pattern(p
, pattern
, pattern_len
) ||
1050 /* Character mismatch OR end of pattern. */
1053 * We didn't get any star yet,
1054 * so this first mismatch
1055 * automatically makes the whole
1062 * Next try: next candidate character,
1063 * original pattern character (following
1064 * the most recent star).
1072 /* Next pattern and candidate characters. */
1078 * We checked every candidate character and we're still in a
1079 * success state: the only pattern character allowed to remain
1082 if (at_end_of_pattern(p
, pattern
, pattern_len
)) {
1087 return p
[-1] == '*' && at_end_of_pattern(p
, pattern
, pattern_len
);
1091 void append_path_parts(const char *path
, GPtrArray
*parts
)
1093 const char *ch
= path
;
1094 const char *last
= path
;
1097 if (*ch
== G_DIR_SEPARATOR
|| *ch
== '\0') {
1098 if (ch
- last
> 0) {
1099 GString
*part
= g_string_new(NULL
);
1102 g_string_append_len(part
, last
, ch
- last
);
1103 g_ptr_array_add(parts
, part
);
1118 void destroy_gstring(void *gstring
)
1120 (void) g_string_free(gstring
, TRUE
);
1125 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1128 GString
*norm_path
= NULL
;
1132 tmp
= _fullpath(NULL
, path
, PATH_MAX
);
1137 norm_path
= g_string_new(tmp
);
1145 g_string_free(norm_path
, TRUE
);
1156 GString
*bt_common_normalize_path(const char *path
, const char *wd
)
1160 GPtrArray
*parts
= NULL
;
1163 norm_path
= g_string_new(G_DIR_SEPARATOR_S
);
1168 parts
= g_ptr_array_new_with_free_func(destroy_gstring
);
1173 if (path
[0] != G_DIR_SEPARATOR
) {
1174 /* Relative path: start with working directory */
1176 append_path_parts(wd
, parts
);
1178 gchar
*cd
= g_get_current_dir();
1180 append_path_parts(cd
, parts
);
1185 /* Append parts of the path parameter */
1186 append_path_parts(path
, parts
);
1188 /* Resolve special `..` and `.` parts */
1189 for (i
= 0; i
< parts
->len
; i
++) {
1190 GString
*part
= g_ptr_array_index(parts
, i
);
1192 if (strcmp(part
->str
, "..") == 0) {
1195 * First part of absolute path is `..`:
1201 /* Remove `..` and previous part */
1202 g_ptr_array_remove_index(parts
, i
- 1);
1203 g_ptr_array_remove_index(parts
, i
- 1);
1205 } else if (strcmp(part
->str
, ".") == 0) {
1207 g_ptr_array_remove_index(parts
, i
);
1212 /* Create normalized path with what's left */
1213 for (i
= 0; i
< parts
->len
; i
++) {
1214 GString
*part
= g_ptr_array_index(parts
, i
);
1216 g_string_append(norm_path
, part
->str
);
1218 if (i
< parts
->len
- 1) {
1219 g_string_append_c(norm_path
, G_DIR_SEPARATOR
);
1227 g_string_free(norm_path
, TRUE
);
1233 g_ptr_array_free(parts
, TRUE
);
1241 size_t bt_common_get_page_size(void)
1245 page_size
= bt_sysconf(_SC_PAGESIZE
);
1246 if (page_size
< 0) {
1247 BT_LOGF("Cannot get system's page size: ret=%d",
1255 #define BUF_STD_APPEND(...) \
1257 char _tmp_fmt[64]; \
1259 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1260 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1261 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1262 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1263 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
1264 BT_ASSERT(_count >= 0); \
1265 *buf_ch += MIN(_count, _size); \
1268 #define BUF_STD_APPEND_SINGLE_ARG(_type) \
1270 _type _arg = va_arg(*args, _type); \
1271 BUF_STD_APPEND(_arg); \
1274 static inline void handle_conversion_specifier_std(char *buf
, char **buf_ch
,
1275 size_t buf_size
, const char **out_fmt_ch
, va_list *args
)
1277 const char *fmt_ch
= *out_fmt_ch
;
1278 enum LENGTH_MODIFIER
{
1286 } length_mod
= LENGTH_MOD_NONE
;
1291 if (*fmt_ch
== '%') {
1317 if (*fmt_ch
< '0' || *fmt_ch
> '9') {
1325 if (*fmt_ch
== '.') {
1329 if (*fmt_ch
< '0' || *fmt_ch
> '9') {
1337 /* format (PRI*64) */
1338 if (strncmp(fmt_ch
, PRId64
, sizeof(PRId64
) - 1) == 0) {
1339 fmt_ch
+= sizeof(PRId64
) - 1;
1340 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1342 } else if (strncmp(fmt_ch
, PRIu64
, sizeof(PRIu64
) - 1) == 0) {
1343 fmt_ch
+= sizeof(PRIu64
) - 1;
1344 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1346 } else if (strncmp(fmt_ch
, PRIx64
, sizeof(PRIx64
) - 1) == 0) {
1347 fmt_ch
+= sizeof(PRIx64
) - 1;
1348 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1350 } else if (strncmp(fmt_ch
, PRIX64
, sizeof(PRIX64
) - 1) == 0) {
1351 fmt_ch
+= sizeof(PRIX64
) - 1;
1352 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1354 } else if (strncmp(fmt_ch
, PRIo64
, sizeof(PRIo64
) - 1) == 0) {
1355 fmt_ch
+= sizeof(PRIo64
) - 1;
1356 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1358 } else if (strncmp(fmt_ch
, PRIi64
, sizeof(PRIi64
) - 1) == 0) {
1359 fmt_ch
+= sizeof(PRIi64
) - 1;
1360 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1367 length_mod
= LENGTH_MOD_H
;
1370 if (*fmt_ch
== 'h') {
1371 length_mod
= LENGTH_MOD_HH
;
1377 length_mod
= LENGTH_MOD_LOW_L
;
1380 if (*fmt_ch
== 'l') {
1381 length_mod
= LENGTH_MOD_LOW_LL
;
1387 length_mod
= LENGTH_MOD_UP_L
;
1391 length_mod
= LENGTH_MOD_Z
;
1404 switch (length_mod
) {
1405 case LENGTH_MOD_NONE
:
1406 BUF_STD_APPEND_SINGLE_ARG(int);
1408 case LENGTH_MOD_LOW_L
:
1409 BUF_STD_APPEND_SINGLE_ARG(wint_t);
1419 switch (length_mod
) {
1420 case LENGTH_MOD_NONE
:
1421 BUF_STD_APPEND_SINGLE_ARG(char *);
1423 case LENGTH_MOD_LOW_L
:
1424 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1434 switch (length_mod
) {
1435 case LENGTH_MOD_NONE
:
1438 BUF_STD_APPEND_SINGLE_ARG(int);
1440 case LENGTH_MOD_LOW_L
:
1441 BUF_STD_APPEND_SINGLE_ARG(long);
1443 case LENGTH_MOD_LOW_LL
:
1444 BUF_STD_APPEND_SINGLE_ARG(long long);
1447 BUF_STD_APPEND_SINGLE_ARG(size_t);
1459 switch (length_mod
) {
1460 case LENGTH_MOD_NONE
:
1463 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1465 case LENGTH_MOD_LOW_L
:
1466 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1468 case LENGTH_MOD_LOW_LL
:
1469 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1472 BUF_STD_APPEND_SINGLE_ARG(size_t);
1486 switch (length_mod
) {
1487 case LENGTH_MOD_NONE
:
1488 BUF_STD_APPEND_SINGLE_ARG(double);
1490 case LENGTH_MOD_UP_L
:
1491 BUF_STD_APPEND_SINGLE_ARG(long double);
1500 if (length_mod
== LENGTH_MOD_NONE
) {
1501 BUF_STD_APPEND_SINGLE_ARG(void *);
1511 *out_fmt_ch
= fmt_ch
;
1515 void bt_common_custom_vsnprintf(char *buf
, size_t buf_size
,
1517 bt_common_handle_custom_specifier_func handle_specifier
,
1518 void *priv_data
, const char *fmt
, va_list *args
)
1520 const char *fmt_ch
= fmt
;
1526 while (*fmt_ch
!= '\0') {
1529 BT_ASSERT(fmt_ch
[1] != '\0');
1531 if (fmt_ch
[1] == intro
) {
1532 handle_specifier(priv_data
, &buf_ch
,
1533 buf_size
- (size_t) (buf_ch
- buf
),
1536 handle_conversion_specifier_std(buf
, &buf_ch
,
1537 buf_size
, &fmt_ch
, args
);
1540 if (buf_ch
>= buf
+ buf_size
- 1) {
1547 if (buf_ch
>= buf
+ buf_size
- 1) {
1559 void bt_common_custom_snprintf(char *buf
, size_t buf_size
,
1561 bt_common_handle_custom_specifier_func handle_specifier
,
1562 void *priv_data
, const char *fmt
, ...)
1565 va_start(args
, fmt
);
1566 bt_common_custom_vsnprintf(buf
, buf_size
, intro
, handle_specifier
,
1567 priv_data
, fmt
, &args
);
1572 void bt_common_sep_digits(char *str
, unsigned int digits_per_group
, char sep
)
1581 BT_ASSERT(digits_per_group
> 0);
1582 BT_ASSERT(sep
!= '\0');
1584 /* Compute new length of `str` */
1585 orig_len
= strlen(str
);
1586 BT_ASSERT(orig_len
> 0);
1587 sep_count
= (orig_len
- 1) / digits_per_group
;
1588 new_len
= strlen(str
) + sep_count
;
1591 * Do the work in place. Have the reading pointer `rd` start at
1592 * the end of the original string, and the writing pointer `wr`
1593 * start at the end of the new string, making sure to also put a
1594 * null character there.
1596 rd
= str
+ orig_len
- 1;
1602 * Here's what the process looks like (3 digits per group):
1606 * Destination: 12345678#8
1611 * Destination: 1234567878
1616 * Destination: 1234567678
1621 * Destination: 123456,678
1626 * Destination: 123455,678
1631 * Destination: 123445,678
1636 * Destination: 123345,678
1641 * Destination: 12,345,678
1646 * Destination: 12,345,678
1651 * Destination: 12,345,678
1654 while (rd
!= str
- 1) {
1655 if (i
== digits_per_group
) {
1657 * Time to append the separator: decrement `wr`,
1658 * but keep `rd` as is.
1666 /* Copy read-side character to write-side character */