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