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