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