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