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