Visibility hidden by default
[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)
244 {
245 return g_get_home_dir();
246 }
247 #else /* __MINGW32__ */
248 static
249 char *bt_secure_getenv(const char *name, int log_level)
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, const char *wd)
1250 {
1251 char *tmp;
1252 GString *norm_path = NULL;
1253
1254 BT_ASSERT(path);
1255
1256 tmp = _fullpath(NULL, path, PATH_MAX);
1257 if (!tmp) {
1258 goto error;
1259 }
1260
1261 norm_path = g_string_new(tmp);
1262 if (!norm_path) {
1263 goto error;
1264 }
1265
1266 goto end;
1267 error:
1268 if (norm_path) {
1269 g_string_free(norm_path, TRUE);
1270 norm_path = NULL;
1271 }
1272 end:
1273 free(tmp);
1274 return norm_path;
1275 }
1276 #else
1277 static
1278 void append_path_parts(const char *path, GPtrArray *parts)
1279 {
1280 const char *ch = path;
1281 const char *last = path;
1282
1283 while (true) {
1284 if (*ch == G_DIR_SEPARATOR || *ch == '\0') {
1285 if (ch - last > 0) {
1286 GString *part = g_string_new(NULL);
1287
1288 BT_ASSERT(part);
1289 g_string_append_len(part, last, ch - last);
1290 g_ptr_array_add(parts, part);
1291 }
1292
1293 if (*ch == '\0') {
1294 break;
1295 }
1296
1297 last = ch + 1;
1298 }
1299
1300 ch++;
1301 }
1302 }
1303
1304 static
1305 void destroy_gstring(void *gstring)
1306 {
1307 (void) g_string_free(gstring, TRUE);
1308 }
1309
1310 GString *bt_common_normalize_path(const char *path, const char *wd)
1311 {
1312 size_t i;
1313 GString *norm_path;
1314 GPtrArray *parts = NULL;
1315
1316 BT_ASSERT(path);
1317 norm_path = g_string_new(G_DIR_SEPARATOR_S);
1318 if (!norm_path) {
1319 goto error;
1320 }
1321
1322 parts = g_ptr_array_new_with_free_func(destroy_gstring);
1323 if (!parts) {
1324 goto error;
1325 }
1326
1327 if (path[0] != G_DIR_SEPARATOR) {
1328 /* Relative path: start with working directory */
1329 if (wd) {
1330 append_path_parts(wd, parts);
1331 } else {
1332 gchar *cd = g_get_current_dir();
1333
1334 append_path_parts(cd, parts);
1335 g_free(cd);
1336 }
1337 }
1338
1339 /* Append parts of the path parameter */
1340 append_path_parts(path, parts);
1341
1342 /* Resolve special `..` and `.` parts */
1343 for (i = 0; i < parts->len; i++) {
1344 GString *part = g_ptr_array_index(parts, i);
1345
1346 if (strcmp(part->str, "..") == 0) {
1347 if (i == 0) {
1348 /*
1349 * First part of absolute path is `..`:
1350 * this is invalid.
1351 */
1352 goto error;
1353 }
1354
1355 /* Remove `..` and previous part */
1356 g_ptr_array_remove_index(parts, i - 1);
1357 g_ptr_array_remove_index(parts, i - 1);
1358 i -= 2;
1359 } else if (strcmp(part->str, ".") == 0) {
1360 /* Remove `.` */
1361 g_ptr_array_remove_index(parts, i);
1362 i -= 1;
1363 }
1364 }
1365
1366 /* Create normalized path with what's left */
1367 for (i = 0; i < parts->len; i++) {
1368 GString *part = g_ptr_array_index(parts, i);
1369
1370 g_string_append(norm_path, part->str);
1371
1372 if (i < parts->len - 1) {
1373 g_string_append_c(norm_path, G_DIR_SEPARATOR);
1374 }
1375 }
1376
1377 goto end;
1378
1379 error:
1380 if (norm_path) {
1381 g_string_free(norm_path, TRUE);
1382 norm_path = NULL;
1383 }
1384
1385 end:
1386 if (parts) {
1387 g_ptr_array_free(parts, TRUE);
1388 }
1389
1390 return norm_path;
1391 }
1392 #endif
1393
1394 size_t bt_common_get_page_size(int log_level)
1395 {
1396 int page_size;
1397
1398 page_size = bt_sysconf(_SC_PAGESIZE);
1399 if (page_size < 0) {
1400 BT_LOGF("Cannot get system's page size: ret=%d",
1401 page_size);
1402 bt_common_abort();
1403 }
1404
1405 return page_size;
1406 }
1407
1408 #define BUF_STD_APPEND(...) \
1409 do { \
1410 char _tmp_fmt[64]; \
1411 int _count; \
1412 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1413 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1414 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1415 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1416 _Pragma("GCC diagnostic push") \
1417 _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") \
1418 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
1419 _Pragma("GCC diagnostic pop") \
1420 BT_ASSERT_DBG(_count >= 0); \
1421 *buf_ch += MIN(_count, _size); \
1422 } while (0)
1423
1424 #define BUF_STD_APPEND_SINGLE_ARG(_type) \
1425 do { \
1426 _type _arg = va_arg(*args, _type); \
1427 BUF_STD_APPEND(_arg); \
1428 } while (0)
1429
1430 static inline void handle_conversion_specifier_std(char *buf, char **buf_ch,
1431 size_t buf_size, const char **out_fmt_ch, va_list *args)
1432 {
1433 const char *fmt_ch = *out_fmt_ch;
1434 enum LENGTH_MODIFIER {
1435 LENGTH_MOD_H,
1436 LENGTH_MOD_HH,
1437 LENGTH_MOD_NONE,
1438 LENGTH_MOD_LOW_L,
1439 LENGTH_MOD_LOW_LL,
1440 LENGTH_MOD_UP_L,
1441 LENGTH_MOD_Z,
1442 } length_mod = LENGTH_MOD_NONE;
1443
1444 /* skip '%' */
1445 fmt_ch++;
1446
1447 if (*fmt_ch == '%') {
1448 fmt_ch++;
1449 **buf_ch = '%';
1450 (*buf_ch)++;
1451 goto update_rw_fmt;
1452 }
1453
1454 /* flags */
1455 for (;;) {
1456 switch (*fmt_ch) {
1457 case '-':
1458 case '+':
1459 case ' ':
1460 case '#':
1461 case '0':
1462 case '\'':
1463 fmt_ch++;
1464 continue;
1465 default:
1466 break;
1467 }
1468 break;
1469 }
1470
1471 /* width */
1472 for (;;) {
1473 if (*fmt_ch < '0' || *fmt_ch > '9') {
1474 break;
1475 }
1476
1477 fmt_ch++;
1478 }
1479
1480 /* precision */
1481 if (*fmt_ch == '.') {
1482 fmt_ch++;
1483
1484 for (;;) {
1485 if (*fmt_ch < '0' || *fmt_ch > '9') {
1486 break;
1487 }
1488
1489 fmt_ch++;
1490 }
1491 }
1492
1493 /* format (PRI*64) */
1494 if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) {
1495 fmt_ch += sizeof(PRId64) - 1;
1496 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1497 goto update_rw_fmt;
1498 } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) {
1499 fmt_ch += sizeof(PRIu64) - 1;
1500 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1501 goto update_rw_fmt;
1502 } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) {
1503 fmt_ch += sizeof(PRIx64) - 1;
1504 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1505 goto update_rw_fmt;
1506 } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) {
1507 fmt_ch += sizeof(PRIX64) - 1;
1508 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1509 goto update_rw_fmt;
1510 } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) {
1511 fmt_ch += sizeof(PRIo64) - 1;
1512 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1513 goto update_rw_fmt;
1514 } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) {
1515 fmt_ch += sizeof(PRIi64) - 1;
1516 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1517 goto update_rw_fmt;
1518 }
1519
1520 // length modifier
1521 switch (*fmt_ch) {
1522 case 'h':
1523 length_mod = LENGTH_MOD_H;
1524 fmt_ch++;
1525
1526 if (*fmt_ch == 'h') {
1527 length_mod = LENGTH_MOD_HH;
1528 fmt_ch++;
1529 break;
1530 }
1531 break;
1532 case 'l':
1533 length_mod = LENGTH_MOD_LOW_L;
1534 fmt_ch++;
1535
1536 if (*fmt_ch == 'l') {
1537 length_mod = LENGTH_MOD_LOW_LL;
1538 fmt_ch++;
1539 break;
1540 }
1541 break;
1542 case 'L':
1543 length_mod = LENGTH_MOD_UP_L;
1544 fmt_ch++;
1545 break;
1546 case 'z':
1547 length_mod = LENGTH_MOD_Z;
1548 fmt_ch++;
1549 break;
1550 default:
1551 break;
1552 }
1553
1554 // format
1555 switch (*fmt_ch) {
1556 case 'c':
1557 {
1558 fmt_ch++;
1559
1560 switch (length_mod) {
1561 case LENGTH_MOD_NONE:
1562 case LENGTH_MOD_LOW_L:
1563 BUF_STD_APPEND_SINGLE_ARG(int);
1564 break;
1565 default:
1566 bt_common_abort();
1567 }
1568 break;
1569 }
1570 case 's':
1571 fmt_ch++;
1572
1573 switch (length_mod) {
1574 case LENGTH_MOD_NONE:
1575 BUF_STD_APPEND_SINGLE_ARG(char *);
1576 break;
1577 case LENGTH_MOD_LOW_L:
1578 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1579 break;
1580 default:
1581 bt_common_abort();
1582 }
1583 break;
1584 case 'd':
1585 case 'i':
1586 fmt_ch++;
1587
1588 switch (length_mod) {
1589 case LENGTH_MOD_NONE:
1590 case LENGTH_MOD_H:
1591 case LENGTH_MOD_HH:
1592 BUF_STD_APPEND_SINGLE_ARG(int);
1593 break;
1594 case LENGTH_MOD_LOW_L:
1595 BUF_STD_APPEND_SINGLE_ARG(long);
1596 break;
1597 case LENGTH_MOD_LOW_LL:
1598 BUF_STD_APPEND_SINGLE_ARG(long long);
1599 break;
1600 case LENGTH_MOD_Z:
1601 BUF_STD_APPEND_SINGLE_ARG(size_t);
1602 break;
1603 default:
1604 bt_common_abort();
1605 }
1606 break;
1607 case 'o':
1608 case 'x':
1609 case 'X':
1610 case 'u':
1611 fmt_ch++;
1612
1613 switch (length_mod) {
1614 case LENGTH_MOD_NONE:
1615 case LENGTH_MOD_H:
1616 case LENGTH_MOD_HH:
1617 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1618 break;
1619 case LENGTH_MOD_LOW_L:
1620 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1621 break;
1622 case LENGTH_MOD_LOW_LL:
1623 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1624 break;
1625 case LENGTH_MOD_Z:
1626 BUF_STD_APPEND_SINGLE_ARG(size_t);
1627 break;
1628 default:
1629 bt_common_abort();
1630 }
1631 break;
1632 case 'f':
1633 case 'F':
1634 case 'e':
1635 case 'E':
1636 case 'g':
1637 case 'G':
1638 fmt_ch++;
1639
1640 switch (length_mod) {
1641 case LENGTH_MOD_NONE:
1642 BUF_STD_APPEND_SINGLE_ARG(double);
1643 break;
1644 case LENGTH_MOD_UP_L:
1645 BUF_STD_APPEND_SINGLE_ARG(long double);
1646 break;
1647 default:
1648 bt_common_abort();
1649 }
1650 break;
1651 case 'p':
1652 fmt_ch++;
1653
1654 if (length_mod == LENGTH_MOD_NONE) {
1655 BUF_STD_APPEND_SINGLE_ARG(void *);
1656 } else {
1657 bt_common_abort();
1658 }
1659 break;
1660 default:
1661 bt_common_abort();
1662 }
1663
1664 update_rw_fmt:
1665 *out_fmt_ch = fmt_ch;
1666 }
1667
1668 void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
1669 char intro,
1670 bt_common_handle_custom_specifier_func handle_specifier,
1671 void *priv_data, const char *fmt, va_list *args)
1672 {
1673 const char *fmt_ch = fmt;
1674 char *buf_ch = buf;
1675
1676 BT_ASSERT_DBG(buf);
1677 BT_ASSERT_DBG(fmt);
1678
1679 while (*fmt_ch != '\0') {
1680 switch (*fmt_ch) {
1681 case '%':
1682 BT_ASSERT_DBG(fmt_ch[1] != '\0');
1683
1684 if (fmt_ch[1] == intro) {
1685 handle_specifier(priv_data, &buf_ch,
1686 buf_size - (size_t) (buf_ch - buf),
1687 &fmt_ch, args);
1688 } else {
1689 handle_conversion_specifier_std(buf, &buf_ch,
1690 buf_size, &fmt_ch, args);
1691 }
1692
1693 if (buf_ch >= buf + buf_size - 1) {
1694 fmt_ch = "";
1695 }
1696 break;
1697 default:
1698 *buf_ch = *fmt_ch;
1699 buf_ch++;
1700 if (buf_ch >= buf + buf_size - 1) {
1701 fmt_ch = "";
1702 }
1703
1704 fmt_ch++;
1705 }
1706 }
1707
1708 *buf_ch = '\0';
1709 }
1710
1711 void bt_common_custom_snprintf(char *buf, size_t buf_size,
1712 char intro,
1713 bt_common_handle_custom_specifier_func handle_specifier,
1714 void *priv_data, const char *fmt, ...)
1715 {
1716 va_list args;
1717 va_start(args, fmt);
1718 bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier,
1719 priv_data, fmt, &args);
1720 va_end(args);
1721 }
1722
1723 void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep)
1724 {
1725 const char *rd;
1726 char *wr;
1727 uint64_t i = 0;
1728 uint64_t orig_len;
1729 uint64_t sep_count;
1730 uint64_t new_len;
1731
1732 BT_ASSERT_DBG(digits_per_group > 0);
1733 BT_ASSERT_DBG(sep != '\0');
1734
1735 /* Compute new length of `str` */
1736 orig_len = strlen(str);
1737 BT_ASSERT_DBG(orig_len > 0);
1738 sep_count = (orig_len - 1) / digits_per_group;
1739 new_len = strlen(str) + sep_count;
1740
1741 /*
1742 * Do the work in place. Have the reading pointer `rd` start at
1743 * the end of the original string, and the writing pointer `wr`
1744 * start at the end of the new string, making sure to also put a
1745 * null character there.
1746 */
1747 rd = str + orig_len - 1;
1748 wr = str + new_len;
1749 *wr = '\0';
1750 wr--;
1751
1752 /*
1753 * Here's what the process looks like (3 digits per group):
1754 *
1755 * Source: 12345678
1756 * ^
1757 * Destination: 12345678#8
1758 * ^
1759 *
1760 * Source: 12345678
1761 * ^
1762 * Destination: 1234567878
1763 * ^
1764 *
1765 * Source: 12345678
1766 * ^
1767 * Destination: 1234567678
1768 * ^
1769 *
1770 * Source: 12345678
1771 * ^
1772 * Destination: 123456,678
1773 * ^
1774 *
1775 * Source: 12345678
1776 * ^
1777 * Destination: 123455,678
1778 * ^
1779 *
1780 * Source: 12345678
1781 * ^
1782 * Destination: 123445,678
1783 * ^
1784 *
1785 * Source: 12345678
1786 * ^
1787 * Destination: 123345,678
1788 * ^
1789 *
1790 * Source: 12345678
1791 * ^
1792 * Destination: 12,345,678
1793 * ^
1794 *
1795 * Source: 12345678
1796 * ^
1797 * Destination: 12,345,678
1798 * ^
1799 *
1800 * Source: 12345678
1801 * ^
1802 * Destination: 12,345,678
1803 * ^
1804 */
1805 while (rd != str - 1) {
1806 if (i == digits_per_group) {
1807 /*
1808 * Time to append the separator: decrement `wr`,
1809 * but keep `rd` as is.
1810 */
1811 i = 0;
1812 *wr = sep;
1813 wr--;
1814 continue;
1815 }
1816
1817 /* Copy read-side character to write-side character */
1818 *wr = *rd;
1819 wr--;
1820 rd--;
1821 i++;
1822 }
1823 }
1824
1825 GString *bt_common_fold(const char *str, unsigned int total_length,
1826 unsigned int indent)
1827 {
1828 const unsigned int content_length = total_length - indent;
1829 GString *folded = g_string_new(NULL);
1830 GString *tmp_line = g_string_new(NULL);
1831 gchar **lines = NULL;
1832 gchar **line_words = NULL;
1833 gchar * const *line;
1834 unsigned int i;
1835
1836 BT_ASSERT_DBG(str);
1837 BT_ASSERT_DBG(indent < total_length);
1838 BT_ASSERT_DBG(tmp_line);
1839 BT_ASSERT_DBG(folded);
1840
1841 if (strlen(str) == 0) {
1842 /* Empty input string: empty output string */
1843 goto end;
1844 }
1845
1846 /* Split lines */
1847 lines = g_strsplit(str, "\n", 0);
1848 BT_ASSERT_DBG(lines);
1849
1850 /* For each source line */
1851 for (line = lines; *line; line++) {
1852 gchar * const *word;
1853
1854 /*
1855 * Append empty line without indenting if source line is
1856 * empty.
1857 */
1858 if (strlen(*line) == 0) {
1859 g_string_append_c(folded, '\n');
1860 continue;
1861 }
1862
1863 /* Split words */
1864 line_words = g_strsplit(*line, " ", 0);
1865 BT_ASSERT_DBG(line_words);
1866
1867 /*
1868 * Indent for first line (we know there's at least one
1869 * word at this point).
1870 */
1871 for (i = 0; i < indent; i++) {
1872 g_string_append_c(folded, ' ');
1873 }
1874
1875 /* Append words, folding when necessary */
1876 g_string_assign(tmp_line, "");
1877
1878 for (word = line_words; *word; word++) {
1879 /*
1880 * `tmp_line->len > 0` is in the condition so
1881 * that words that are larger than
1882 * `content_length` are at least written on
1883 * their own line.
1884 *
1885 * `tmp_line->len - 1` because the temporary
1886 * line always contains a trailing space which
1887 * won't be part of the line if we fold.
1888 */
1889 if (tmp_line->len > 0 &&
1890 tmp_line->len - 1 + strlen(*word) >= content_length) {
1891 /* Fold (without trailing space) */
1892 g_string_append_len(folded,
1893 tmp_line->str, tmp_line->len - 1);
1894 g_string_append_c(folded, '\n');
1895
1896 /* Indent new line */
1897 for (i = 0; i < indent; i++) {
1898 g_string_append_c(folded, ' ');
1899 }
1900
1901 g_string_assign(tmp_line, "");
1902 }
1903
1904 /* Append current word and space to temporary line */
1905 g_string_append(tmp_line, *word);
1906 g_string_append_c(tmp_line, ' ');
1907 }
1908
1909 /* Append last line if any, without trailing space */
1910 if (tmp_line->len > 0) {
1911 g_string_append_len(folded, tmp_line->str,
1912 tmp_line->len - 1);
1913 }
1914
1915 /* Append source newline */
1916 g_string_append_c(folded, '\n');
1917
1918 /* Free array of this line's words */
1919 g_strfreev(line_words);
1920 line_words = NULL;
1921 }
1922
1923 /* Remove trailing newline if any */
1924 if (folded->str[folded->len - 1] == '\n') {
1925 g_string_truncate(folded, folded->len - 1);
1926 }
1927
1928 end:
1929 if (lines) {
1930 g_strfreev(lines);
1931 }
1932
1933 BT_ASSERT_DBG(!line_words);
1934
1935 if (tmp_line) {
1936 g_string_free(tmp_line, TRUE);
1937 }
1938
1939 return folded;
1940 }
1941
1942 #ifdef __MINGW32__
1943 int bt_common_get_term_size(unsigned int *width, unsigned int *height)
1944 {
1945 /* Not supported on Windows yet */
1946 return -1;
1947 }
1948 #else /* __MINGW32__ */
1949 int bt_common_get_term_size(unsigned int *width, unsigned int *height)
1950 {
1951 int ret = 0;
1952 struct winsize winsize;
1953
1954 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) < 0) {
1955 ret = -1;
1956 goto end;
1957 }
1958
1959 if (width) {
1960 *width = (unsigned int) winsize.ws_col;
1961 }
1962
1963 if (height) {
1964 *height = (unsigned int) winsize.ws_row;
1965 }
1966
1967 end:
1968 return ret;
1969 }
1970 #endif /* __MINGW32__ */
1971
1972 int bt_common_g_string_append_printf(GString *str, const char *fmt, ...)
1973 {
1974 va_list ap;
1975 gsize len, allocated_len, available_len;
1976 int print_len;
1977
1978 /* str->len excludes \0. */
1979 len = str->len;
1980 /* Explicitly exclude \0. */
1981 allocated_len = str->allocated_len - 1;
1982 available_len = allocated_len - len;
1983
1984 str->len = allocated_len;
1985 va_start(ap, fmt);
1986 print_len = vsnprintf(str->str + len, available_len + 1, fmt, ap);
1987 va_end(ap);
1988 if (print_len < 0) {
1989 return print_len;
1990 }
1991 if (G_UNLIKELY(available_len < print_len)) {
1992 /* Resize. */
1993 g_string_set_size(str, len + print_len);
1994 va_start(ap, fmt);
1995 print_len = vsnprintf(str->str + len, print_len + 1, fmt, ap);
1996 va_end(ap);
1997 } else {
1998 str->len = len + print_len;
1999 }
2000 return print_len;
2001 }
2002
2003 int bt_common_append_file_content_to_g_string(GString *str, FILE *fp)
2004 {
2005 const size_t chunk_size = 4096;
2006 int ret = 0;
2007 char *buf;
2008 size_t read_len;
2009 gsize orig_len = str->len;
2010
2011 BT_ASSERT(str);
2012 BT_ASSERT(fp);
2013 buf = g_malloc(chunk_size);
2014 if (!buf) {
2015 ret = -1;
2016 goto end;
2017 }
2018
2019 while (true) {
2020 if (ferror(fp)) {
2021 ret = -1;
2022 goto end;
2023 }
2024
2025 if (feof(fp)) {
2026 break;
2027 }
2028
2029 read_len = fread(buf, 1, chunk_size, fp);
2030 g_string_append_len(str, buf, read_len);
2031 }
2032
2033 end:
2034 if (ret) {
2035 /* Remove what was appended */
2036 g_string_truncate(str, orig_len);
2037 }
2038
2039 g_free(buf);
2040 return ret;
2041 }
2042
2043 void bt_common_abort(void)
2044 {
2045 static const char * const exec_on_abort_env_name =
2046 "BABELTRACE_EXEC_ON_ABORT";
2047 const char *env_exec_on_abort;
2048
2049 env_exec_on_abort = getenv(exec_on_abort_env_name);
2050 if (env_exec_on_abort) {
2051 if (bt_common_is_setuid_setgid()) {
2052 goto do_abort;
2053 }
2054
2055 (void) g_spawn_command_line_sync(env_exec_on_abort,
2056 NULL, NULL, NULL, NULL);
2057 }
2058
2059 do_abort:
2060 abort();
2061 }
This page took 0.114126 seconds and 4 git commands to generate.