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