86c703411497d96518dd586723844ee54f88a3d4
[babeltrace.git] / src / common / common.c
1 /*
2 * Babeltrace common functions
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 #define BT_LOG_OUTPUT_LEVEL log_level
26 #define BT_LOG_TAG "COMMON"
27 #include "logging/log.h"
28
29 #include <unistd.h>
30 #include <string.h>
31 #include <inttypes.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include "common/assert.h"
36 #include <stdarg.h>
37 #include <ctype.h>
38 #include <glib.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <wchar.h>
42 #include <stdbool.h>
43 #include "common/macros.h"
44 #include "common/common.h"
45 #include "compat/unistd.h"
46
47 #ifndef __MINGW32__
48 #include <pwd.h>
49 #include <sys/ioctl.h>
50 #endif
51
52 #define SYSTEM_PLUGIN_PATH BABELTRACE_PLUGINS_DIR
53 #define HOME_ENV_VAR "HOME"
54 #define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins"
55
56 static const char *bt_common_color_code_reset = "";
57 static const char *bt_common_color_code_bold = "";
58 static const char *bt_common_color_code_fg_default = "";
59 static const char *bt_common_color_code_fg_red = "";
60 static const char *bt_common_color_code_fg_green = "";
61 static const char *bt_common_color_code_fg_yellow = "";
62 static const char *bt_common_color_code_fg_blue = "";
63 static const char *bt_common_color_code_fg_magenta = "";
64 static const char *bt_common_color_code_fg_cyan = "";
65 static const char *bt_common_color_code_fg_light_gray = "";
66 static const char *bt_common_color_code_fg_bright_red = "";
67 static const char *bt_common_color_code_fg_bright_green = "";
68 static const char *bt_common_color_code_fg_bright_yellow = "";
69 static const char *bt_common_color_code_fg_bright_blue = "";
70 static const char *bt_common_color_code_fg_bright_magenta = "";
71 static const char *bt_common_color_code_fg_bright_cyan = "";
72 static const char *bt_common_color_code_fg_bright_light_gray = "";
73 static const char *bt_common_color_code_bg_default = "";
74 static const char *bt_common_color_code_bg_red = "";
75 static const char *bt_common_color_code_bg_green = "";
76 static const char *bt_common_color_code_bg_yellow = "";
77 static const char *bt_common_color_code_bg_blue = "";
78 static const char *bt_common_color_code_bg_magenta = "";
79 static const char *bt_common_color_code_bg_cyan = "";
80 static const char *bt_common_color_code_bg_light_gray = "";
81
82 static
83 void __attribute__((constructor)) bt_common_color_ctor(void)
84 {
85 if (bt_common_colors_supported()) {
86 const char *term_env_var;
87 const char *bright_means_bold_env_var;
88 bool bright_means_bold = true;
89
90 bt_common_color_code_reset = BT_COMMON_COLOR_RESET;
91 bt_common_color_code_bold = BT_COMMON_COLOR_BOLD;
92 bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT;
93 bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED;
94 bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN;
95 bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW;
96 bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE;
97 bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA;
98 bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN;
99 bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY;
100
101 /*
102 * Check whether or not the terminal supports having
103 * bold foreground colors which do _not_ become bright
104 * colors, that is, the lines
105 *
106 * $ echo -e "\033[31mTHIS\n\033[1mTHAT\033[0m"
107 *
108 * have the _same_ color, but `THAT` uses a bold font.
109 *
110 * This is the case of the kitty terminal emulator.
111 *
112 * It's also possible with GNOME Terminal since 3.27.2
113 * and xfce4-terminal since 0.8.7 (and GNOME VTE since
114 * 0.51.2), but it's user-configurable. Since we don't
115 * have this configuration value here, assume it's not
116 * the case to support old versions of GNOME Terminal.
117 *
118 * Any user can set the
119 * `BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD` environment
120 * variable to `0` to use the bright foreground color
121 * codes instead of making the normal foreground color
122 * codes bold.
123 *
124 * Summary:
125 *
126 * With kitty or when
127 * `BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD` is `0`:
128 * Output bright colors using dedicated SGR codes
129 * 90 to 97.
130 *
131 * Otherwise:
132 * Output bright colors with bold + SGR codes 30 to
133 * 37.
134 */
135 term_env_var = getenv("TERM");
136 BT_ASSERT(term_env_var);
137
138 if (strcmp(term_env_var, "xterm-kitty") == 0) {
139 /*
140 * The kitty terminal emulator supports
141 * non-bright bold foreground colors.
142 */
143 bright_means_bold = false;
144 }
145
146 bright_means_bold_env_var =
147 getenv("BABELTRACE_TERM_COLOR_BRIGHT_MEANS_BOLD");
148
149 if (bright_means_bold_env_var) {
150 bright_means_bold =
151 !(strcmp(bright_means_bold_env_var, "0") == 0);
152 }
153
154 if (bright_means_bold) {
155 bt_common_color_code_fg_bright_red = BT_COMMON_COLOR_FG_BOLD_RED;
156 bt_common_color_code_fg_bright_green = BT_COMMON_COLOR_FG_BOLD_GREEN;
157 bt_common_color_code_fg_bright_yellow = BT_COMMON_COLOR_FG_BOLD_YELLOW;
158 bt_common_color_code_fg_bright_blue = BT_COMMON_COLOR_FG_BOLD_BLUE;
159 bt_common_color_code_fg_bright_magenta = BT_COMMON_COLOR_FG_BOLD_MAGENTA;
160 bt_common_color_code_fg_bright_cyan = BT_COMMON_COLOR_FG_BOLD_CYAN;
161 bt_common_color_code_fg_bright_light_gray = BT_COMMON_COLOR_FG_BOLD_LIGHT_GRAY;
162 } else {
163 bt_common_color_code_fg_bright_red = BT_COMMON_COLOR_FG_BRIGHT_RED;
164 bt_common_color_code_fg_bright_green = BT_COMMON_COLOR_FG_BRIGHT_GREEN;
165 bt_common_color_code_fg_bright_yellow = BT_COMMON_COLOR_FG_BRIGHT_YELLOW;
166 bt_common_color_code_fg_bright_blue = BT_COMMON_COLOR_FG_BRIGHT_BLUE;
167 bt_common_color_code_fg_bright_magenta = BT_COMMON_COLOR_FG_BRIGHT_MAGENTA;
168 bt_common_color_code_fg_bright_cyan = BT_COMMON_COLOR_FG_BRIGHT_CYAN;
169 bt_common_color_code_fg_bright_light_gray = BT_COMMON_COLOR_FG_BRIGHT_LIGHT_GRAY;
170 }
171
172 bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT;
173 bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED;
174 bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN;
175 bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW;
176 bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE;
177 bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA;
178 bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN;
179 bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY;
180 }
181 }
182
183 BT_HIDDEN
184 const char *bt_common_get_system_plugin_path(void)
185 {
186 return SYSTEM_PLUGIN_PATH;
187 }
188
189 #ifdef __MINGW32__
190 BT_HIDDEN
191 bool bt_common_is_setuid_setgid(void)
192 {
193 return false;
194 }
195 #else /* __MINGW32__ */
196 BT_HIDDEN
197 bool bt_common_is_setuid_setgid(void)
198 {
199 return (geteuid() != getuid() || getegid() != getgid());
200 }
201 #endif /* __MINGW32__ */
202
203 #ifdef __MINGW32__
204 static
205 const char *bt_get_home_dir(int log_level)
206 {
207 return g_get_home_dir();
208 }
209 #else /* __MINGW32__ */
210 static
211 char *bt_secure_getenv(const char *name, int log_level)
212 {
213 if (bt_common_is_setuid_setgid()) {
214 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
215 "name=\"%s\"", name);
216 return NULL;
217 }
218 return getenv(name);
219 }
220
221 static
222 const char *bt_get_home_dir(int log_level)
223 {
224 char *val = NULL;
225 struct passwd *pwd;
226
227 val = bt_secure_getenv(HOME_ENV_VAR, log_level);
228 if (val) {
229 goto end;
230 }
231 /* Fallback on password file. */
232 pwd = getpwuid(getuid());
233 if (!pwd) {
234 goto end;
235 }
236 val = pwd->pw_dir;
237 end:
238 return val;
239 }
240 #endif /* __MINGW32__ */
241
242 BT_HIDDEN
243 char *bt_common_get_home_plugin_path(int log_level)
244 {
245 char *path = NULL;
246 const char *home_dir;
247 size_t length;
248
249 home_dir = bt_get_home_dir(log_level);
250 if (!home_dir) {
251 goto end;
252 }
253
254 length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1;
255
256 if (length >= PATH_MAX) {
257 BT_LOGW("Home directory path is too long: "
258 "length=%zu, max-length=%u", length, PATH_MAX);
259 goto end;
260 }
261
262 path = malloc(PATH_MAX);
263 if (!path) {
264 goto end;
265 }
266
267 strcpy(path, home_dir);
268 strcat(path, HOME_PLUGIN_SUBPATH);
269
270 end:
271 return path;
272 }
273
274 BT_HIDDEN
275 int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
276 {
277 int ret = 0;
278 const char *at;
279 const char *end;
280 size_t init_dirs_len;
281
282 BT_ASSERT(dirs);
283 init_dirs_len = dirs->len;
284
285 if (!paths) {
286 /* Nothing to append */
287 goto end;
288 }
289
290 at = paths;
291 end = paths + strlen(paths);
292
293 while (at < end) {
294 GString *path;
295 const char *next_sep;
296
297 next_sep = strchr(at, G_SEARCHPATH_SEPARATOR);
298 if (next_sep == at) {
299 /*
300 * Empty path: try next character (supported
301 * to conform to the typical parsing of $PATH).
302 */
303 at++;
304 continue;
305 } else if (!next_sep) {
306 /* No more separator: use the remaining */
307 next_sep = paths + strlen(paths);
308 }
309
310 path = g_string_new(NULL);
311 if (!path) {
312 goto error;
313 }
314
315 g_string_append_len(path, at, next_sep - at);
316 at = next_sep + 1;
317 g_ptr_array_add(dirs, path);
318 }
319
320 goto end;
321
322 error:
323 ret = -1;
324
325 /* Remove the new entries in dirs */
326 while (dirs->len > init_dirs_len) {
327 g_ptr_array_remove_index(dirs, init_dirs_len);
328 }
329
330 end:
331 return ret;
332 }
333
334 static
335 bool isarealtty(int fd)
336 {
337 bool istty = false;
338 struct stat tty_stats;
339
340 if (!isatty(fd)) {
341 /* Not a TTY */
342 goto end;
343 }
344
345 if (fstat(fd, &tty_stats) == 0) {
346 if (!S_ISCHR(tty_stats.st_mode)) {
347 /* Not a character device: not a TTY */
348 goto end;
349 }
350 }
351
352 istty = true;
353
354 end:
355 return istty;
356 }
357
358 BT_HIDDEN
359 bool bt_common_colors_supported(void)
360 {
361 static bool supports_colors = false;
362 static bool supports_colors_set = false;
363 const char *term_env_var;
364 const char *term_color_env_var;
365
366 if (supports_colors_set) {
367 goto end;
368 }
369
370 supports_colors_set = true;
371
372 /*
373 * `BABELTRACE_TERM_COLOR` environment variable always overrides
374 * the automatic color support detection.
375 */
376 term_color_env_var = getenv("BABELTRACE_TERM_COLOR");
377 if (term_color_env_var) {
378 if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) {
379 /* Force colors */
380 supports_colors = true;
381 } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) {
382 /* Force no colors */
383 goto end;
384 }
385 }
386
387 /* We need a compatible, known terminal */
388 term_env_var = getenv("TERM");
389 if (!term_env_var) {
390 goto end;
391 }
392
393 if (strncmp(term_env_var, "xterm", 5) != 0 &&
394 strncmp(term_env_var, "rxvt", 4) != 0 &&
395 strncmp(term_env_var, "konsole", 7) != 0 &&
396 strncmp(term_env_var, "gnome", 5) != 0 &&
397 strncmp(term_env_var, "screen", 5) != 0 &&
398 strncmp(term_env_var, "tmux", 4) != 0 &&
399 strncmp(term_env_var, "putty", 5) != 0) {
400 goto end;
401 }
402
403 /* Both standard output and error streams need to be TTYs */
404 if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) {
405 goto end;
406 }
407
408 supports_colors = true;
409
410 end:
411 return supports_colors;
412 }
413
414 BT_HIDDEN
415 const char *bt_common_color_reset(void)
416 {
417 return bt_common_color_code_reset;
418 }
419
420 BT_HIDDEN
421 const char *bt_common_color_bold(void)
422 {
423 return bt_common_color_code_bold;
424 }
425
426 BT_HIDDEN
427 const char *bt_common_color_fg_default(void)
428 {
429 return bt_common_color_code_fg_default;
430 }
431
432 BT_HIDDEN
433 const char *bt_common_color_fg_red(void)
434 {
435 return bt_common_color_code_fg_red;
436 }
437
438 BT_HIDDEN
439 const char *bt_common_color_fg_green(void)
440 {
441 return bt_common_color_code_fg_green;
442 }
443
444 BT_HIDDEN
445 const char *bt_common_color_fg_yellow(void)
446 {
447 return bt_common_color_code_fg_yellow;
448 }
449
450 BT_HIDDEN
451 const char *bt_common_color_fg_blue(void)
452 {
453 return bt_common_color_code_fg_blue;
454 }
455
456 BT_HIDDEN
457 const char *bt_common_color_fg_magenta(void)
458 {
459 return bt_common_color_code_fg_magenta;
460 }
461
462 BT_HIDDEN
463 const char *bt_common_color_fg_cyan(void)
464 {
465 return bt_common_color_code_fg_cyan;
466 }
467
468 BT_HIDDEN
469 const char *bt_common_color_fg_light_gray(void)
470 {
471 return bt_common_color_code_fg_light_gray;
472 }
473
474 BT_HIDDEN
475 const char *bt_common_color_fg_bright_red(void)
476 {
477 return bt_common_color_code_fg_bright_red;
478 }
479
480 BT_HIDDEN
481 const char *bt_common_color_fg_bright_green(void)
482 {
483 return bt_common_color_code_fg_bright_green;
484 }
485
486 BT_HIDDEN
487 const char *bt_common_color_fg_bright_yellow(void)
488 {
489 return bt_common_color_code_fg_bright_yellow;
490 }
491
492 BT_HIDDEN
493 const char *bt_common_color_fg_bright_blue(void)
494 {
495 return bt_common_color_code_fg_bright_blue;
496 }
497
498 BT_HIDDEN
499 const char *bt_common_color_fg_bright_magenta(void)
500 {
501 return bt_common_color_code_fg_bright_magenta;
502 }
503
504 BT_HIDDEN
505 const char *bt_common_color_fg_bright_cyan(void)
506 {
507 return bt_common_color_code_fg_bright_cyan;
508 }
509
510 BT_HIDDEN
511 const char *bt_common_color_fg_bright_light_gray(void)
512 {
513 return bt_common_color_code_fg_bright_light_gray;
514 }
515
516 BT_HIDDEN
517 const char *bt_common_color_bg_default(void)
518 {
519 return bt_common_color_code_bg_default;
520 }
521
522 BT_HIDDEN
523 const char *bt_common_color_bg_red(void)
524 {
525 return bt_common_color_code_bg_red;
526 }
527
528 BT_HIDDEN
529 const char *bt_common_color_bg_green(void)
530 {
531 return bt_common_color_code_bg_green;
532 }
533
534 BT_HIDDEN
535 const char *bt_common_color_bg_yellow(void)
536 {
537 return bt_common_color_code_bg_yellow;
538 }
539
540 BT_HIDDEN
541 const char *bt_common_color_bg_blue(void)
542 {
543 return bt_common_color_code_bg_blue;
544 }
545
546 BT_HIDDEN
547 const char *bt_common_color_bg_magenta(void)
548 {
549 return bt_common_color_code_bg_magenta;
550 }
551
552 BT_HIDDEN
553 const char *bt_common_color_bg_cyan(void)
554 {
555 return bt_common_color_code_bg_cyan;
556 }
557
558 BT_HIDDEN
559 const char *bt_common_color_bg_light_gray(void)
560 {
561 return bt_common_color_code_bg_light_gray;
562 }
563
564 BT_HIDDEN
565 GString *bt_common_string_until(const char *input, const char *escapable_chars,
566 const char *end_chars, size_t *end_pos)
567 {
568 GString *output = g_string_new(NULL);
569 const char *ch;
570 const char *es_char;
571 const char *end_char;
572
573 if (!output) {
574 goto error;
575 }
576
577 for (ch = input; *ch != '\0'; ch++) {
578 if (*ch == '\\') {
579 bool continue_loop = false;
580
581 if (ch[1] == '\0') {
582 /* `\` at the end of the string: append `\` */
583 g_string_append_c(output, *ch);
584 ch++;
585 goto set_end_pos;
586 }
587
588 for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
589 if (ch[1] == *es_char) {
590 /*
591 * `\` followed by an escapable
592 * character: append the escaped
593 * character only.
594 */
595 g_string_append_c(output, ch[1]);
596 ch++;
597 continue_loop = true;
598 break;
599 }
600 }
601
602 if (continue_loop) {
603 continue;
604 }
605
606 /*
607 * `\` followed by a non-escapable character:
608 * append `\` and the character.
609 */
610 g_string_append_c(output, *ch);
611 g_string_append_c(output, ch[1]);
612 ch++;
613 continue;
614 } else {
615 for (end_char = end_chars; *end_char != '\0'; end_char++) {
616 if (*ch == *end_char) {
617 /*
618 * End character found:
619 * terminate this loop.
620 */
621 goto set_end_pos;
622 }
623 }
624
625 /* Normal character: append */
626 g_string_append_c(output, *ch);
627 }
628 }
629
630 set_end_pos:
631 if (end_pos) {
632 *end_pos = ch - input;
633 }
634
635 goto end;
636
637 error:
638 if (output) {
639 g_string_free(output, TRUE);
640 output = NULL;
641 }
642
643 end:
644 return output;
645 }
646
647 BT_HIDDEN
648 GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
649 {
650 GString *output = g_string_new(NULL);
651 const char *ch;
652 bool no_quote = true;
653
654 if (!output) {
655 goto end;
656 }
657
658 if (strlen(input) == 0) {
659 if (with_single_quotes) {
660 g_string_assign(output, "''");
661 }
662
663 goto end;
664 }
665
666 for (ch = input; *ch != '\0'; ch++) {
667 const char c = *ch;
668
669 if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
670 c != '@' && c != '%' && c != '+' && c != '=' &&
671 c != ':' && c != ',' && c != '.' && c != '/' &&
672 c != '-') {
673 no_quote = false;
674 break;
675 }
676 }
677
678 if (no_quote) {
679 g_string_assign(output, input);
680 goto end;
681 }
682
683 if (with_single_quotes) {
684 g_string_assign(output, "'");
685 }
686
687 for (ch = input; *ch != '\0'; ch++) {
688 if (*ch == '\'') {
689 g_string_append(output, "'\"'\"'");
690 } else {
691 g_string_append_c(output, *ch);
692 }
693 }
694
695 if (with_single_quotes) {
696 g_string_append_c(output, '\'');
697 }
698
699 end:
700 return output;
701 }
702
703 BT_HIDDEN
704 bool bt_common_string_is_printable(const char *input)
705 {
706 const char *ch;
707 bool printable = true;
708 BT_ASSERT_DBG(input);
709
710 for (ch = input; *ch != '\0'; ch++) {
711 if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
712 *ch != '\t' && *ch != '\v') {
713 printable = false;
714 goto end;
715 }
716 }
717
718 end:
719 return printable;
720 }
721
722 BT_HIDDEN
723 void bt_common_destroy_lttng_live_url_parts(
724 struct bt_common_lttng_live_url_parts *parts)
725 {
726 if (!parts) {
727 goto end;
728 }
729
730 if (parts->proto) {
731 g_string_free(parts->proto, TRUE);
732 parts->proto = NULL;
733 }
734
735 if (parts->hostname) {
736 g_string_free(parts->hostname, TRUE);
737 parts->hostname = NULL;
738 }
739
740 if (parts->target_hostname) {
741 g_string_free(parts->target_hostname, TRUE);
742 parts->target_hostname = NULL;
743 }
744
745 if (parts->session_name) {
746 g_string_free(parts->session_name, TRUE);
747 parts->session_name = NULL;
748 }
749
750 end:
751 return;
752 }
753
754 BT_HIDDEN
755 struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
756 const char *url, char *error_buf, size_t error_buf_size)
757 {
758 struct bt_common_lttng_live_url_parts parts;
759 const char *at = url;
760 size_t end_pos;
761
762 BT_ASSERT(url);
763 memset(&parts, 0, sizeof(parts));
764 parts.port = -1;
765
766 /* Protocol */
767 parts.proto = bt_common_string_until(at, "", ":", &end_pos);
768 if (!parts.proto || parts.proto->len == 0) {
769 if (error_buf) {
770 snprintf(error_buf, error_buf_size, "Missing protocol");
771 }
772
773 goto error;
774 }
775
776 if (strcmp(parts.proto->str, "net") == 0) {
777 g_string_assign(parts.proto, "net4");
778 }
779
780 if (strcmp(parts.proto->str, "net4") != 0 &&
781 strcmp(parts.proto->str, "net6") != 0) {
782 if (error_buf) {
783 snprintf(error_buf, error_buf_size,
784 "Unknown protocol: `%s`", parts.proto->str);
785 }
786
787 goto error;
788 }
789
790 if (at[end_pos] != ':') {
791 if (error_buf) {
792 snprintf(error_buf, error_buf_size,
793 "Expecting `:` after `%s`", parts.proto->str);
794 }
795
796 goto error;
797 }
798
799 at += end_pos;
800
801 /* `://` */
802 if (strncmp(at, "://", 3) != 0) {
803 if (error_buf) {
804 snprintf(error_buf, error_buf_size,
805 "Expecting `://` after protocol");
806 }
807
808 goto error;
809 }
810
811 /* Skip `://` */
812 at += 3;
813
814 /* Hostname */
815 parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
816 if (!parts.hostname || parts.hostname->len == 0) {
817 if (error_buf) {
818 snprintf(error_buf, error_buf_size, "Missing hostname");
819 }
820
821 goto error;
822 }
823
824 if (at[end_pos] == ':') {
825 /* Port */
826 GString *port;
827
828 at += end_pos + 1;
829 port = bt_common_string_until(at, "", "/", &end_pos);
830 if (!port || port->len == 0) {
831 if (error_buf) {
832 snprintf(error_buf, error_buf_size, "Missing port");
833 }
834
835 goto error;
836 }
837
838 if (sscanf(port->str, "%d", &parts.port) != 1) {
839 if (error_buf) {
840 snprintf(error_buf, error_buf_size,
841 "Invalid port: `%s`", port->str);
842 }
843
844 g_string_free(port, TRUE);
845 goto error;
846 }
847
848 g_string_free(port, TRUE);
849
850 if (parts.port < 0 || parts.port >= 65536) {
851 if (error_buf) {
852 snprintf(error_buf, error_buf_size,
853 "Invalid port: %d", parts.port);
854 }
855
856 goto error;
857 }
858 }
859
860 if (at[end_pos] == '\0') {
861 /* Relay daemon hostname and ports provided only */
862 goto end;
863 }
864
865 at += end_pos;
866
867 /* `/host/` */
868 if (strncmp(at, "/host/", 6) != 0) {
869 if (error_buf) {
870 snprintf(error_buf, error_buf_size,
871 "Expecting `/host/` after hostname or port");
872 }
873
874 goto error;
875 }
876
877 at += 6;
878
879 /* Target hostname */
880 parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
881 if (!parts.target_hostname || parts.target_hostname->len == 0) {
882 if (error_buf) {
883 snprintf(error_buf, error_buf_size,
884 "Missing target hostname");
885 }
886
887 goto error;
888 }
889
890 if (at[end_pos] == '\0') {
891 if (error_buf) {
892 snprintf(error_buf, error_buf_size,
893 "Missing `/` after target hostname (`%s`)",
894 parts.target_hostname->str);
895 }
896
897 goto error;
898 }
899
900 /* Skip `/` */
901 at += end_pos + 1;
902
903 /* Session name */
904 parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
905 if (!parts.session_name || parts.session_name->len == 0) {
906 if (error_buf) {
907 snprintf(error_buf, error_buf_size,
908 "Missing session name");
909 }
910
911 goto error;
912 }
913
914 if (at[end_pos] == '/') {
915 if (error_buf) {
916 snprintf(error_buf, error_buf_size,
917 "Unexpected `/` after session name (`%s`)",
918 parts.session_name->str);
919 }
920
921 goto error;
922 }
923
924 goto end;
925
926 error:
927 bt_common_destroy_lttng_live_url_parts(&parts);
928
929 end:
930 return parts;
931 }
932
933 BT_HIDDEN
934 void bt_common_normalize_star_glob_pattern(char *pattern)
935 {
936 const char *p;
937 char *np;
938 bool got_star = false;
939
940 BT_ASSERT(pattern);
941
942 for (p = pattern, np = pattern; *p != '\0'; p++) {
943 switch (*p) {
944 case '*':
945 if (got_star) {
946 /* Avoid consecutive stars. */
947 continue;
948 }
949
950 got_star = true;
951 break;
952 case '\\':
953 /* Copy backslash character. */
954 *np = *p;
955 np++;
956 p++;
957
958 if (*p == '\0') {
959 goto end;
960 }
961
962 /* fall-through */
963 default:
964 got_star = false;
965 break;
966 }
967
968 /* Copy single character. */
969 *np = *p;
970 np++;
971 }
972
973 end:
974 *np = '\0';
975 }
976
977 static inline
978 bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
979 {
980 return (p - pattern) == pattern_len || *p == '\0';
981 }
982
983 /*
984 * Globbing matching function with the star feature only (`?` and
985 * character sets are not supported). This matches `candidate` (plain
986 * string) against `pattern`. A literal star can be escaped with `\` in
987 * `pattern`.
988 *
989 * `pattern_len` or `candidate_len` can be greater than the actual
990 * string length of `pattern` or `candidate` if the string is
991 * null-terminated.
992 */
993 BT_HIDDEN
994 bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
995 const char *candidate, size_t candidate_len) {
996 const char *retry_c = candidate, *retry_p = pattern, *c, *p;
997 bool got_a_star = false;
998
999 retry:
1000 c = retry_c;
1001 p = retry_p;
1002
1003 /*
1004 * The concept here is to retry a match in the specific case
1005 * where we already got a star. The retry position for the
1006 * pattern is just after the most recent star, and the retry
1007 * position for the candidate is the character following the
1008 * last try's first character.
1009 *
1010 * Example:
1011 *
1012 * candidate: hi ev every onyx one
1013 * ^
1014 * pattern: hi*every*one
1015 * ^
1016 *
1017 * candidate: hi ev every onyx one
1018 * ^
1019 * pattern: hi*every*one
1020 * ^
1021 *
1022 * candidate: hi ev every onyx one
1023 * ^
1024 * pattern: hi*every*one
1025 * ^
1026 *
1027 * candidate: hi ev every onyx one
1028 * ^
1029 * pattern: hi*every*one
1030 * ^ MISMATCH
1031 *
1032 * candidate: hi ev every onyx one
1033 * ^
1034 * pattern: hi*every*one
1035 * ^
1036 *
1037 * candidate: hi ev every onyx one
1038 * ^^
1039 * pattern: hi*every*one
1040 * ^^
1041 *
1042 * candidate: hi ev every onyx one
1043 * ^ ^
1044 * pattern: hi*every*one
1045 * ^ ^ MISMATCH
1046 *
1047 * candidate: hi ev every onyx one
1048 * ^
1049 * pattern: hi*every*one
1050 * ^ MISMATCH
1051 *
1052 * candidate: hi ev every onyx one
1053 * ^
1054 * pattern: hi*every*one
1055 * ^ MISMATCH
1056 *
1057 * candidate: hi ev every onyx one
1058 * ^
1059 * pattern: hi*every*one
1060 * ^
1061 *
1062 * candidate: hi ev every onyx one
1063 * ^^
1064 * pattern: hi*every*one
1065 * ^^
1066 *
1067 * candidate: hi ev every onyx one
1068 * ^ ^
1069 * pattern: hi*every*one
1070 * ^ ^
1071 *
1072 * candidate: hi ev every onyx one
1073 * ^ ^
1074 * pattern: hi*every*one
1075 * ^ ^
1076 *
1077 * candidate: hi ev every onyx one
1078 * ^ ^
1079 * pattern: hi*every*one
1080 * ^ ^
1081 *
1082 * candidate: hi ev every onyx one
1083 * ^
1084 * pattern: hi*every*one
1085 * ^
1086 *
1087 * candidate: hi ev every onyx one
1088 * ^
1089 * pattern: hi*every*one
1090 * ^ MISMATCH
1091 *
1092 * candidate: hi ev every onyx one
1093 * ^
1094 * pattern: hi*every*one
1095 * ^
1096 *
1097 * candidate: hi ev every onyx one
1098 * ^^
1099 * pattern: hi*every*one
1100 * ^^
1101 *
1102 * candidate: hi ev every onyx one
1103 * ^ ^
1104 * pattern: hi*every*one
1105 * ^ ^ MISMATCH
1106 *
1107 * candidate: hi ev every onyx one
1108 * ^
1109 * pattern: hi*every*one
1110 * ^ MISMATCH
1111 *
1112 * candidate: hi ev every onyx one
1113 * ^
1114 * pattern: hi*every*one
1115 * ^ MISMATCH
1116 *
1117 * candidate: hi ev every onyx one
1118 * ^
1119 * pattern: hi*every*one
1120 * ^ MISMATCH
1121 *
1122 * candidate: hi ev every onyx one
1123 * ^
1124 * pattern: hi*every*one
1125 * ^ MISMATCH
1126 *
1127 * candidate: hi ev every onyx one
1128 * ^
1129 * pattern: hi*every*one
1130 * ^
1131 *
1132 * candidate: hi ev every onyx one
1133 * ^^
1134 * pattern: hi*every*one
1135 * ^^
1136 *
1137 * candidate: hi ev every onyx one
1138 * ^ ^
1139 * pattern: hi*every*one
1140 * ^ ^
1141 *
1142 * candidate: hi ev every onyx one
1143 * ^ ^
1144 * pattern: hi*every*one
1145 * ^ ^ SUCCESS
1146 */
1147 while ((c - candidate) < candidate_len && *c != '\0') {
1148 BT_ASSERT(*c);
1149
1150 if (at_end_of_pattern(p, pattern, pattern_len)) {
1151 goto end_of_pattern;
1152 }
1153
1154 switch (*p) {
1155 case '*':
1156 got_a_star = true;
1157
1158 /*
1159 * Our first try starts at the current candidate
1160 * character and after the star in the pattern.
1161 */
1162 retry_c = c;
1163 retry_p = p + 1;
1164
1165 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
1166 /*
1167 * Star at the end of the pattern at
1168 * this point: automatic match.
1169 */
1170 return true;
1171 }
1172
1173 goto retry;
1174 case '\\':
1175 /* Go to escaped character. */
1176 p++;
1177
1178 /*
1179 * Fall through the default case which compares
1180 * the escaped character now.
1181 */
1182 /* fall-through */
1183 default:
1184 if (at_end_of_pattern(p, pattern, pattern_len) ||
1185 *c != *p) {
1186 end_of_pattern:
1187 /* Character mismatch OR end of pattern. */
1188 if (!got_a_star) {
1189 /*
1190 * We didn't get any star yet,
1191 * so this first mismatch
1192 * automatically makes the whole
1193 * test fail.
1194 */
1195 return false;
1196 }
1197
1198 /*
1199 * Next try: next candidate character,
1200 * original pattern character (following
1201 * the most recent star).
1202 */
1203 retry_c++;
1204 goto retry;
1205 }
1206 break;
1207 }
1208
1209 /* Next pattern and candidate characters. */
1210 c++;
1211 p++;
1212 }
1213
1214 /*
1215 * We checked every candidate character and we're still in a
1216 * success state: the only pattern character allowed to remain
1217 * is a star.
1218 */
1219 if (at_end_of_pattern(p, pattern, pattern_len)) {
1220 return true;
1221 }
1222
1223 p++;
1224 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
1225 }
1226
1227 #ifdef __MINGW32__
1228 BT_HIDDEN
1229 GString *bt_common_normalize_path(const char *path, const char *wd)
1230 {
1231 char *tmp;
1232 GString *norm_path = NULL;
1233
1234 BT_ASSERT(path);
1235
1236 tmp = _fullpath(NULL, path, PATH_MAX);
1237 if (!tmp) {
1238 goto error;
1239 }
1240
1241 norm_path = g_string_new(tmp);
1242 if (!norm_path) {
1243 goto error;
1244 }
1245
1246 goto end;
1247 error:
1248 if (norm_path) {
1249 g_string_free(norm_path, TRUE);
1250 norm_path = NULL;
1251 }
1252 end:
1253 free(tmp);
1254 return norm_path;
1255 }
1256 #else
1257 static
1258 void append_path_parts(const char *path, GPtrArray *parts)
1259 {
1260 const char *ch = path;
1261 const char *last = path;
1262
1263 while (true) {
1264 if (*ch == G_DIR_SEPARATOR || *ch == '\0') {
1265 if (ch - last > 0) {
1266 GString *part = g_string_new(NULL);
1267
1268 BT_ASSERT(part);
1269 g_string_append_len(part, last, ch - last);
1270 g_ptr_array_add(parts, part);
1271 }
1272
1273 if (*ch == '\0') {
1274 break;
1275 }
1276
1277 last = ch + 1;
1278 }
1279
1280 ch++;
1281 }
1282 }
1283
1284 static
1285 void destroy_gstring(void *gstring)
1286 {
1287 (void) g_string_free(gstring, TRUE);
1288 }
1289
1290 BT_HIDDEN
1291 GString *bt_common_normalize_path(const char *path, const char *wd)
1292 {
1293 size_t i;
1294 GString *norm_path;
1295 GPtrArray *parts = NULL;
1296
1297 BT_ASSERT(path);
1298 norm_path = g_string_new(G_DIR_SEPARATOR_S);
1299 if (!norm_path) {
1300 goto error;
1301 }
1302
1303 parts = g_ptr_array_new_with_free_func(destroy_gstring);
1304 if (!parts) {
1305 goto error;
1306 }
1307
1308 if (path[0] != G_DIR_SEPARATOR) {
1309 /* Relative path: start with working directory */
1310 if (wd) {
1311 append_path_parts(wd, parts);
1312 } else {
1313 gchar *cd = g_get_current_dir();
1314
1315 append_path_parts(cd, parts);
1316 g_free(cd);
1317 }
1318 }
1319
1320 /* Append parts of the path parameter */
1321 append_path_parts(path, parts);
1322
1323 /* Resolve special `..` and `.` parts */
1324 for (i = 0; i < parts->len; i++) {
1325 GString *part = g_ptr_array_index(parts, i);
1326
1327 if (strcmp(part->str, "..") == 0) {
1328 if (i == 0) {
1329 /*
1330 * First part of absolute path is `..`:
1331 * this is invalid.
1332 */
1333 goto error;
1334 }
1335
1336 /* Remove `..` and previous part */
1337 g_ptr_array_remove_index(parts, i - 1);
1338 g_ptr_array_remove_index(parts, i - 1);
1339 i -= 2;
1340 } else if (strcmp(part->str, ".") == 0) {
1341 /* Remove `.` */
1342 g_ptr_array_remove_index(parts, i);
1343 i -= 1;
1344 }
1345 }
1346
1347 /* Create normalized path with what's left */
1348 for (i = 0; i < parts->len; i++) {
1349 GString *part = g_ptr_array_index(parts, i);
1350
1351 g_string_append(norm_path, part->str);
1352
1353 if (i < parts->len - 1) {
1354 g_string_append_c(norm_path, G_DIR_SEPARATOR);
1355 }
1356 }
1357
1358 goto end;
1359
1360 error:
1361 if (norm_path) {
1362 g_string_free(norm_path, TRUE);
1363 norm_path = NULL;
1364 }
1365
1366 end:
1367 if (parts) {
1368 g_ptr_array_free(parts, TRUE);
1369 }
1370
1371 return norm_path;
1372 }
1373 #endif
1374
1375 BT_HIDDEN
1376 size_t bt_common_get_page_size(int log_level)
1377 {
1378 int page_size;
1379
1380 page_size = bt_sysconf(_SC_PAGESIZE);
1381 if (page_size < 0) {
1382 BT_LOGF("Cannot get system's page size: ret=%d",
1383 page_size);
1384 bt_common_abort();
1385 }
1386
1387 return page_size;
1388 }
1389
1390 #define BUF_STD_APPEND(...) \
1391 do { \
1392 char _tmp_fmt[64]; \
1393 int _count; \
1394 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1395 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1396 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1397 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1398 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
1399 BT_ASSERT_DBG(_count >= 0); \
1400 *buf_ch += MIN(_count, _size); \
1401 } while (0)
1402
1403 #define BUF_STD_APPEND_SINGLE_ARG(_type) \
1404 do { \
1405 _type _arg = va_arg(*args, _type); \
1406 BUF_STD_APPEND(_arg); \
1407 } while (0)
1408
1409 static inline void handle_conversion_specifier_std(char *buf, char **buf_ch,
1410 size_t buf_size, const char **out_fmt_ch, va_list *args)
1411 {
1412 const char *fmt_ch = *out_fmt_ch;
1413 enum LENGTH_MODIFIER {
1414 LENGTH_MOD_H,
1415 LENGTH_MOD_HH,
1416 LENGTH_MOD_NONE,
1417 LENGTH_MOD_LOW_L,
1418 LENGTH_MOD_LOW_LL,
1419 LENGTH_MOD_UP_L,
1420 LENGTH_MOD_Z,
1421 } length_mod = LENGTH_MOD_NONE;
1422
1423 /* skip '%' */
1424 fmt_ch++;
1425
1426 if (*fmt_ch == '%') {
1427 fmt_ch++;
1428 **buf_ch = '%';
1429 (*buf_ch)++;
1430 goto update_rw_fmt;
1431 }
1432
1433 /* flags */
1434 for (;;) {
1435 switch (*fmt_ch) {
1436 case '-':
1437 case '+':
1438 case ' ':
1439 case '#':
1440 case '0':
1441 case '\'':
1442 fmt_ch++;
1443 continue;
1444 default:
1445 break;
1446 }
1447 break;
1448 }
1449
1450 /* width */
1451 for (;;) {
1452 if (*fmt_ch < '0' || *fmt_ch > '9') {
1453 break;
1454 }
1455
1456 fmt_ch++;
1457 }
1458
1459 /* precision */
1460 if (*fmt_ch == '.') {
1461 fmt_ch++;
1462
1463 for (;;) {
1464 if (*fmt_ch < '0' || *fmt_ch > '9') {
1465 break;
1466 }
1467
1468 fmt_ch++;
1469 }
1470 }
1471
1472 /* format (PRI*64) */
1473 if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) {
1474 fmt_ch += sizeof(PRId64) - 1;
1475 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1476 goto update_rw_fmt;
1477 } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) {
1478 fmt_ch += sizeof(PRIu64) - 1;
1479 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1480 goto update_rw_fmt;
1481 } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) {
1482 fmt_ch += sizeof(PRIx64) - 1;
1483 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1484 goto update_rw_fmt;
1485 } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) {
1486 fmt_ch += sizeof(PRIX64) - 1;
1487 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1488 goto update_rw_fmt;
1489 } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) {
1490 fmt_ch += sizeof(PRIo64) - 1;
1491 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1492 goto update_rw_fmt;
1493 } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) {
1494 fmt_ch += sizeof(PRIi64) - 1;
1495 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1496 goto update_rw_fmt;
1497 }
1498
1499 // length modifier
1500 switch (*fmt_ch) {
1501 case 'h':
1502 length_mod = LENGTH_MOD_H;
1503 fmt_ch++;
1504
1505 if (*fmt_ch == 'h') {
1506 length_mod = LENGTH_MOD_HH;
1507 fmt_ch++;
1508 break;
1509 }
1510 break;
1511 case 'l':
1512 length_mod = LENGTH_MOD_LOW_L;
1513 fmt_ch++;
1514
1515 if (*fmt_ch == 'l') {
1516 length_mod = LENGTH_MOD_LOW_LL;
1517 fmt_ch++;
1518 break;
1519 }
1520 break;
1521 case 'L':
1522 length_mod = LENGTH_MOD_UP_L;
1523 fmt_ch++;
1524 break;
1525 case 'z':
1526 length_mod = LENGTH_MOD_Z;
1527 fmt_ch++;
1528 break;
1529 default:
1530 break;
1531 }
1532
1533 // format
1534 switch (*fmt_ch) {
1535 case 'c':
1536 {
1537 fmt_ch++;
1538
1539 switch (length_mod) {
1540 case LENGTH_MOD_NONE:
1541 case LENGTH_MOD_LOW_L:
1542 BUF_STD_APPEND_SINGLE_ARG(int);
1543 break;
1544 default:
1545 bt_common_abort();
1546 }
1547 break;
1548 }
1549 case 's':
1550 fmt_ch++;
1551
1552 switch (length_mod) {
1553 case LENGTH_MOD_NONE:
1554 BUF_STD_APPEND_SINGLE_ARG(char *);
1555 break;
1556 case LENGTH_MOD_LOW_L:
1557 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1558 break;
1559 default:
1560 bt_common_abort();
1561 }
1562 break;
1563 case 'd':
1564 case 'i':
1565 fmt_ch++;
1566
1567 switch (length_mod) {
1568 case LENGTH_MOD_NONE:
1569 case LENGTH_MOD_H:
1570 case LENGTH_MOD_HH:
1571 BUF_STD_APPEND_SINGLE_ARG(int);
1572 break;
1573 case LENGTH_MOD_LOW_L:
1574 BUF_STD_APPEND_SINGLE_ARG(long);
1575 break;
1576 case LENGTH_MOD_LOW_LL:
1577 BUF_STD_APPEND_SINGLE_ARG(long long);
1578 break;
1579 case LENGTH_MOD_Z:
1580 BUF_STD_APPEND_SINGLE_ARG(size_t);
1581 break;
1582 default:
1583 bt_common_abort();
1584 }
1585 break;
1586 case 'o':
1587 case 'x':
1588 case 'X':
1589 case 'u':
1590 fmt_ch++;
1591
1592 switch (length_mod) {
1593 case LENGTH_MOD_NONE:
1594 case LENGTH_MOD_H:
1595 case LENGTH_MOD_HH:
1596 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1597 break;
1598 case LENGTH_MOD_LOW_L:
1599 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1600 break;
1601 case LENGTH_MOD_LOW_LL:
1602 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1603 break;
1604 case LENGTH_MOD_Z:
1605 BUF_STD_APPEND_SINGLE_ARG(size_t);
1606 break;
1607 default:
1608 bt_common_abort();
1609 }
1610 break;
1611 case 'f':
1612 case 'F':
1613 case 'e':
1614 case 'E':
1615 case 'g':
1616 case 'G':
1617 fmt_ch++;
1618
1619 switch (length_mod) {
1620 case LENGTH_MOD_NONE:
1621 BUF_STD_APPEND_SINGLE_ARG(double);
1622 break;
1623 case LENGTH_MOD_UP_L:
1624 BUF_STD_APPEND_SINGLE_ARG(long double);
1625 break;
1626 default:
1627 bt_common_abort();
1628 }
1629 break;
1630 case 'p':
1631 fmt_ch++;
1632
1633 if (length_mod == LENGTH_MOD_NONE) {
1634 BUF_STD_APPEND_SINGLE_ARG(void *);
1635 } else {
1636 bt_common_abort();
1637 }
1638 break;
1639 default:
1640 bt_common_abort();
1641 }
1642
1643 update_rw_fmt:
1644 *out_fmt_ch = fmt_ch;
1645 }
1646
1647 BT_HIDDEN
1648 void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
1649 char intro,
1650 bt_common_handle_custom_specifier_func handle_specifier,
1651 void *priv_data, const char *fmt, va_list *args)
1652 {
1653 const char *fmt_ch = fmt;
1654 char *buf_ch = buf;
1655
1656 BT_ASSERT_DBG(buf);
1657 BT_ASSERT_DBG(fmt);
1658
1659 while (*fmt_ch != '\0') {
1660 switch (*fmt_ch) {
1661 case '%':
1662 BT_ASSERT_DBG(fmt_ch[1] != '\0');
1663
1664 if (fmt_ch[1] == intro) {
1665 handle_specifier(priv_data, &buf_ch,
1666 buf_size - (size_t) (buf_ch - buf),
1667 &fmt_ch, args);
1668 } else {
1669 handle_conversion_specifier_std(buf, &buf_ch,
1670 buf_size, &fmt_ch, args);
1671 }
1672
1673 if (buf_ch >= buf + buf_size - 1) {
1674 fmt_ch = "";
1675 }
1676 break;
1677 default:
1678 *buf_ch = *fmt_ch;
1679 buf_ch++;
1680 if (buf_ch >= buf + buf_size - 1) {
1681 fmt_ch = "";
1682 }
1683
1684 fmt_ch++;
1685 }
1686 }
1687
1688 *buf_ch = '\0';
1689 }
1690
1691 BT_HIDDEN
1692 void bt_common_custom_snprintf(char *buf, size_t buf_size,
1693 char intro,
1694 bt_common_handle_custom_specifier_func handle_specifier,
1695 void *priv_data, const char *fmt, ...)
1696 {
1697 va_list args;
1698 va_start(args, fmt);
1699 bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier,
1700 priv_data, fmt, &args);
1701 va_end(args);
1702 }
1703
1704 BT_HIDDEN
1705 void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep)
1706 {
1707 const char *rd;
1708 char *wr;
1709 uint64_t i = 0;
1710 uint64_t orig_len;
1711 uint64_t sep_count;
1712 uint64_t new_len;
1713
1714 BT_ASSERT_DBG(digits_per_group > 0);
1715 BT_ASSERT_DBG(sep != '\0');
1716
1717 /* Compute new length of `str` */
1718 orig_len = strlen(str);
1719 BT_ASSERT_DBG(orig_len > 0);
1720 sep_count = (orig_len - 1) / digits_per_group;
1721 new_len = strlen(str) + sep_count;
1722
1723 /*
1724 * Do the work in place. Have the reading pointer `rd` start at
1725 * the end of the original string, and the writing pointer `wr`
1726 * start at the end of the new string, making sure to also put a
1727 * null character there.
1728 */
1729 rd = str + orig_len - 1;
1730 wr = str + new_len;
1731 *wr = '\0';
1732 wr--;
1733
1734 /*
1735 * Here's what the process looks like (3 digits per group):
1736 *
1737 * Source: 12345678
1738 * ^
1739 * Destination: 12345678#8
1740 * ^
1741 *
1742 * Source: 12345678
1743 * ^
1744 * Destination: 1234567878
1745 * ^
1746 *
1747 * Source: 12345678
1748 * ^
1749 * Destination: 1234567678
1750 * ^
1751 *
1752 * Source: 12345678
1753 * ^
1754 * Destination: 123456,678
1755 * ^
1756 *
1757 * Source: 12345678
1758 * ^
1759 * Destination: 123455,678
1760 * ^
1761 *
1762 * Source: 12345678
1763 * ^
1764 * Destination: 123445,678
1765 * ^
1766 *
1767 * Source: 12345678
1768 * ^
1769 * Destination: 123345,678
1770 * ^
1771 *
1772 * Source: 12345678
1773 * ^
1774 * Destination: 12,345,678
1775 * ^
1776 *
1777 * Source: 12345678
1778 * ^
1779 * Destination: 12,345,678
1780 * ^
1781 *
1782 * Source: 12345678
1783 * ^
1784 * Destination: 12,345,678
1785 * ^
1786 */
1787 while (rd != str - 1) {
1788 if (i == digits_per_group) {
1789 /*
1790 * Time to append the separator: decrement `wr`,
1791 * but keep `rd` as is.
1792 */
1793 i = 0;
1794 *wr = sep;
1795 wr--;
1796 continue;
1797 }
1798
1799 /* Copy read-side character to write-side character */
1800 *wr = *rd;
1801 wr--;
1802 rd--;
1803 i++;
1804 }
1805 }
1806
1807 BT_HIDDEN
1808 GString *bt_common_fold(const char *str, unsigned int total_length,
1809 unsigned int indent)
1810 {
1811 const unsigned int content_length = total_length - indent;
1812 GString *folded = g_string_new(NULL);
1813 GString *tmp_line = g_string_new(NULL);
1814 gchar **lines = NULL;
1815 gchar **line_words = NULL;
1816 gchar * const *line;
1817 unsigned int i;
1818
1819 BT_ASSERT_DBG(str);
1820 BT_ASSERT_DBG(indent < total_length);
1821 BT_ASSERT_DBG(tmp_line);
1822 BT_ASSERT_DBG(folded);
1823
1824 if (strlen(str) == 0) {
1825 /* Empty input string: empty output string */
1826 goto end;
1827 }
1828
1829 /* Split lines */
1830 lines = g_strsplit(str, "\n", 0);
1831 BT_ASSERT_DBG(lines);
1832
1833 /* For each source line */
1834 for (line = lines; *line; line++) {
1835 gchar * const *word;
1836
1837 /*
1838 * Append empty line without indenting if source line is
1839 * empty.
1840 */
1841 if (strlen(*line) == 0) {
1842 g_string_append_c(folded, '\n');
1843 continue;
1844 }
1845
1846 /* Split words */
1847 line_words = g_strsplit(*line, " ", 0);
1848 BT_ASSERT_DBG(line_words);
1849
1850 /*
1851 * Indent for first line (we know there's at least one
1852 * word at this point).
1853 */
1854 for (i = 0; i < indent; i++) {
1855 g_string_append_c(folded, ' ');
1856 }
1857
1858 /* Append words, folding when necessary */
1859 g_string_assign(tmp_line, "");
1860
1861 for (word = line_words; *word; word++) {
1862 /*
1863 * `tmp_line->len > 0` is in the condition so
1864 * that words that are larger than
1865 * `content_length` are at least written on
1866 * their own line.
1867 *
1868 * `tmp_line->len - 1` because the temporary
1869 * line always contains a trailing space which
1870 * won't be part of the line if we fold.
1871 */
1872 if (tmp_line->len > 0 &&
1873 tmp_line->len - 1 + strlen(*word) >= content_length) {
1874 /* Fold (without trailing space) */
1875 g_string_append_len(folded,
1876 tmp_line->str, tmp_line->len - 1);
1877 g_string_append_c(folded, '\n');
1878
1879 /* Indent new line */
1880 for (i = 0; i < indent; i++) {
1881 g_string_append_c(folded, ' ');
1882 }
1883
1884 g_string_assign(tmp_line, "");
1885 }
1886
1887 /* Append current word and space to temporary line */
1888 g_string_append(tmp_line, *word);
1889 g_string_append_c(tmp_line, ' ');
1890 }
1891
1892 /* Append last line if any, without trailing space */
1893 if (tmp_line->len > 0) {
1894 g_string_append_len(folded, tmp_line->str,
1895 tmp_line->len - 1);
1896 }
1897
1898 /* Append source newline */
1899 g_string_append_c(folded, '\n');
1900
1901 /* Free array of this line's words */
1902 g_strfreev(line_words);
1903 line_words = NULL;
1904 }
1905
1906 /* Remove trailing newline if any */
1907 if (folded->str[folded->len - 1] == '\n') {
1908 g_string_truncate(folded, folded->len - 1);
1909 }
1910
1911 end:
1912 if (lines) {
1913 g_strfreev(lines);
1914 }
1915
1916 BT_ASSERT_DBG(!line_words);
1917
1918 if (tmp_line) {
1919 g_string_free(tmp_line, TRUE);
1920 }
1921
1922 return folded;
1923 }
1924
1925 #ifdef __MINGW32__
1926 BT_HIDDEN
1927 int bt_common_get_term_size(unsigned int *width, unsigned int *height)
1928 {
1929 /* Not supported on Windows yet */
1930 return -1;
1931 }
1932 #else /* __MINGW32__ */
1933 BT_HIDDEN
1934 int bt_common_get_term_size(unsigned int *width, unsigned int *height)
1935 {
1936 int ret = 0;
1937 struct winsize winsize;
1938
1939 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) < 0) {
1940 ret = -1;
1941 goto end;
1942 }
1943
1944 if (width) {
1945 *width = (unsigned int) winsize.ws_col;
1946 }
1947
1948 if (height) {
1949 *height = (unsigned int) winsize.ws_row;
1950 }
1951
1952 end:
1953 return ret;
1954 }
1955 #endif /* __MINGW32__ */
1956
1957 BT_HIDDEN
1958 int bt_common_g_string_append_printf(GString *str, const char *fmt, ...)
1959 {
1960 va_list ap;
1961 gsize len, allocated_len, available_len;
1962 int print_len;
1963
1964 /* str->len excludes \0. */
1965 len = str->len;
1966 /* Explicitly exclude \0. */
1967 allocated_len = str->allocated_len - 1;
1968 available_len = allocated_len - len;
1969
1970 str->len = allocated_len;
1971 va_start(ap, fmt);
1972 print_len = vsnprintf(str->str + len, available_len + 1, fmt, ap);
1973 va_end(ap);
1974 if (print_len < 0) {
1975 return print_len;
1976 }
1977 if (G_UNLIKELY(available_len < print_len)) {
1978 /* Resize. */
1979 g_string_set_size(str, len + print_len);
1980 va_start(ap, fmt);
1981 print_len = vsprintf(str->str + len, fmt, ap);
1982 va_end(ap);
1983 } else {
1984 str->len = len + print_len;
1985 }
1986 return print_len;
1987 }
1988
1989 BT_HIDDEN
1990 int bt_common_append_file_content_to_g_string(GString *str, FILE *fp)
1991 {
1992 const size_t chunk_size = 4096;
1993 int ret = 0;
1994 char *buf;
1995 size_t read_len;
1996 gsize orig_len = str->len;
1997
1998 BT_ASSERT(str);
1999 BT_ASSERT(fp);
2000 buf = g_malloc(chunk_size);
2001 if (!buf) {
2002 ret = -1;
2003 goto end;
2004 }
2005
2006 while (true) {
2007 if (ferror(fp)) {
2008 ret = -1;
2009 goto end;
2010 }
2011
2012 if (feof(fp)) {
2013 break;
2014 }
2015
2016 read_len = fread(buf, 1, chunk_size, fp);
2017 g_string_append_len(str, buf, read_len);
2018 }
2019
2020 end:
2021 if (ret) {
2022 /* Remove what was appended */
2023 g_string_truncate(str, orig_len);
2024 }
2025
2026 g_free(buf);
2027 return ret;
2028 }
2029
2030 BT_HIDDEN
2031 void bt_common_abort(void)
2032 {
2033 static const char * const exec_on_abort_env_name =
2034 "BABELTRACE_EXEC_ON_ABORT";
2035 const char *env_exec_on_abort;
2036
2037 env_exec_on_abort = getenv(exec_on_abort_env_name);
2038 if (env_exec_on_abort) {
2039 if (bt_common_is_setuid_setgid()) {
2040 goto do_abort;
2041 }
2042
2043 (void) g_spawn_command_line_sync(env_exec_on_abort,
2044 NULL, NULL, NULL, NULL);
2045 }
2046
2047 do_abort:
2048 abort();
2049 }
This page took 0.109957 seconds and 3 git commands to generate.