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