Make API CTF-agnostic
[babeltrace.git] / common / common.c
CommitLineData
1670bffd
PP
1/*
2 * Babeltrace common functions
3 *
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
b4565e8b
PP
25#define BT_LOG_TAG "COMMON"
26#include "logging.h"
27
290725f7 28#include <unistd.h>
1670bffd 29#include <string.h>
85cd02cf 30#include <inttypes.h>
1670bffd 31#include <sys/types.h>
fcc7e8dd 32#include <sys/stat.h>
1670bffd 33#include <unistd.h>
f6ccaed9 34#include <babeltrace/assert-internal.h>
85cd02cf 35#include <stdarg.h>
db0f160a 36#include <ctype.h>
1670bffd 37#include <glib.h>
108e5a1e 38#include <stdlib.h>
85cd02cf
PP
39#include <stdio.h>
40#include <wchar.h>
fcc7e8dd 41#include <stdbool.h>
1670bffd 42#include <babeltrace/babeltrace-internal.h>
ad96d936 43#include <babeltrace/common-internal.h>
108e5a1e 44#include <babeltrace/compat/unistd-internal.h>
1670bffd 45
2006c005
MJ
46#ifndef __MINGW32__
47#include <pwd.h>
48#endif
49
1670bffd
PP
50#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
51#define HOME_ENV_VAR "HOME"
52#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
53
6c4a0731
PP
54static const char *bt_common_color_code_reset = "";
55static const char *bt_common_color_code_bold = "";
56static const char *bt_common_color_code_fg_default = "";
57static const char *bt_common_color_code_fg_red = "";
58static const char *bt_common_color_code_fg_green = "";
59static const char *bt_common_color_code_fg_yellow = "";
60static const char *bt_common_color_code_fg_blue = "";
61static const char *bt_common_color_code_fg_magenta = "";
62static const char *bt_common_color_code_fg_cyan = "";
63static const char *bt_common_color_code_fg_light_gray = "";
64static const char *bt_common_color_code_bg_default = "";
65static const char *bt_common_color_code_bg_red = "";
66static const char *bt_common_color_code_bg_green = "";
67static const char *bt_common_color_code_bg_yellow = "";
68static const char *bt_common_color_code_bg_blue = "";
69static const char *bt_common_color_code_bg_magenta = "";
70static const char *bt_common_color_code_bg_cyan = "";
71static const char *bt_common_color_code_bg_light_gray = "";
72
73static
74void __attribute__((constructor)) bt_common_color_ctor(void)
75{
76 if (bt_common_colors_supported()) {
77 bt_common_color_code_reset = BT_COMMON_COLOR_RESET;
78 bt_common_color_code_bold = BT_COMMON_COLOR_BOLD;
79 bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT;
80 bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED;
81 bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN;
82 bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW;
83 bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE;
84 bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA;
85 bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN;
86 bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY;
87 bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT;
88 bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED;
89 bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN;
90 bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW;
91 bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE;
92 bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA;
93 bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN;
94 bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY;
95 }
96}
97
1670bffd
PP
98BT_HIDDEN
99const char *bt_common_get_system_plugin_path(void)
100{
101 return SYSTEM_PLUGIN_PATH;
102}
103
a613ad68
MJ
104#ifdef __MINGW32__
105BT_HIDDEN
106bool bt_common_is_setuid_setgid(void)
107{
108 return false;
109}
110#else /* __MINGW32__ */
1670bffd
PP
111BT_HIDDEN
112bool bt_common_is_setuid_setgid(void)
113{
114 return (geteuid() != getuid() || getegid() != getgid());
115}
a613ad68 116#endif /* __MINGW32__ */
1670bffd 117
2006c005
MJ
118static
119char *bt_secure_getenv(const char *name)
1670bffd
PP
120{
121 if (bt_common_is_setuid_setgid()) {
b4565e8b
PP
122 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
123 "name=\"%s\"", name);
1670bffd
PP
124 return NULL;
125 }
126 return getenv(name);
127}
128
2006c005
MJ
129#ifdef __MINGW32__
130static
131const char *bt_get_home_dir(void)
132{
133 return g_get_home_dir();
134}
135#else /* __MINGW32__ */
136static
137const char *bt_get_home_dir(void)
1670bffd
PP
138{
139 char *val = NULL;
140 struct passwd *pwd;
141
142 val = bt_secure_getenv(HOME_ENV_VAR);
143 if (val) {
144 goto end;
145 }
146 /* Fallback on password file. */
147 pwd = getpwuid(getuid());
148 if (!pwd) {
149 goto end;
150 }
151 val = pwd->pw_dir;
152end:
153 return val;
154}
2006c005 155#endif /* __MINGW32__ */
1670bffd
PP
156
157BT_HIDDEN
158char *bt_common_get_home_plugin_path(void)
159{
160 char *path = NULL;
161 const char *home_dir;
b4565e8b 162 size_t length;
1670bffd 163
2006c005 164 home_dir = bt_get_home_dir();
1670bffd
PP
165 if (!home_dir) {
166 goto end;
167 }
168
b4565e8b
PP
169 length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1;
170
171 if (length >= PATH_MAX) {
172 BT_LOGW("Home directory path is too long: length=%zu",
173 length);
1670bffd
PP
174 goto end;
175 }
176
177 path = malloc(PATH_MAX);
178 if (!path) {
179 goto end;
180 }
181
182 strcpy(path, home_dir);
183 strcat(path, HOME_PLUGIN_SUBPATH);
184
185end:
186 return path;
187}
188
189BT_HIDDEN
190int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
191{
192 int ret = 0;
193 const char *at;
194 const char *end;
195 size_t init_dirs_len;
196
f6ccaed9 197 BT_ASSERT(dirs);
1670bffd
PP
198 init_dirs_len = dirs->len;
199
200 if (!paths) {
201 /* Nothing to append */
202 goto end;
203 }
204
205 at = paths;
206 end = paths + strlen(paths);
207
208 while (at < end) {
209 GString *path;
52238017 210 const char *next_sep;
1670bffd 211
52238017
MJ
212 next_sep = strchr(at, G_SEARCHPATH_SEPARATOR);
213 if (next_sep == at) {
1670bffd
PP
214 /*
215 * Empty path: try next character (supported
216 * to conform to the typical parsing of $PATH).
217 */
218 at++;
219 continue;
52238017
MJ
220 } else if (!next_sep) {
221 /* No more separator: use the remaining */
222 next_sep = paths + strlen(paths);
1670bffd
PP
223 }
224
225 path = g_string_new(NULL);
226 if (!path) {
227 goto error;
228 }
229
52238017
MJ
230 g_string_append_len(path, at, next_sep - at);
231 at = next_sep + 1;
1670bffd
PP
232 g_ptr_array_add(dirs, path);
233 }
234
235 goto end;
236
237error:
238 ret = -1;
239
240 /* Remove the new entries in dirs */
241 while (dirs->len > init_dirs_len) {
242 g_ptr_array_remove_index(dirs, init_dirs_len);
243 }
244
245end:
246 return ret;
247}
290725f7 248
fcc7e8dd
PP
249static
250bool isarealtty(int fd)
251{
252 bool istty = false;
253 struct stat tty_stats;
254
255 if (!isatty(fd)) {
256 /* Not a TTY */
257 goto end;
258 }
259
260 if (fstat(fd, &tty_stats) == 0) {
261 if (!S_ISCHR(tty_stats.st_mode)) {
262 /* Not a character device: not a TTY */
263 goto end;
264 }
265 }
266
267 istty = true;
268
269end:
270 return istty;
271}
272
ad96d936
PP
273BT_HIDDEN
274bool bt_common_colors_supported(void)
290725f7
PP
275{
276 static bool supports_colors = false;
277 static bool supports_colors_set = false;
fcc7e8dd
PP
278 const char *term_env_var;
279 const char *term_color_env_var;
290725f7
PP
280
281 if (supports_colors_set) {
282 goto end;
283 }
284
285 supports_colors_set = true;
286
fcc7e8dd
PP
287 /*
288 * `BABELTRACE_TERM_COLOR` environment variable always overrides
289 * the automatic color support detection.
290 */
291 term_color_env_var = getenv("BABELTRACE_TERM_COLOR");
292 if (term_color_env_var) {
293 if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) {
294 /* Force colors */
295 supports_colors = true;
296 } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) {
297 /* Force no colors */
298 goto end;
299 }
62128320
PP
300 }
301
fcc7e8dd
PP
302 /* We need a compatible, known terminal */
303 term_env_var = getenv("TERM");
304 if (!term_env_var) {
290725f7
PP
305 goto end;
306 }
307
fcc7e8dd
PP
308 if (strncmp(term_env_var, "xterm", 5) != 0 &&
309 strncmp(term_env_var, "rxvt", 4) != 0 &&
310 strncmp(term_env_var, "konsole", 7) != 0 &&
311 strncmp(term_env_var, "gnome", 5) != 0 &&
312 strncmp(term_env_var, "screen", 5) != 0 &&
313 strncmp(term_env_var, "tmux", 4) != 0 &&
314 strncmp(term_env_var, "putty", 5) != 0) {
290725f7
PP
315 goto end;
316 }
317
fcc7e8dd
PP
318 /* Both standard output and error streams need to be TTYs */
319 if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) {
290725f7
PP
320 goto end;
321 }
322
323 supports_colors = true;
324
325end:
326 return supports_colors;
327}
328
329BT_HIDDEN
330const char *bt_common_color_reset(void)
331{
6c4a0731 332 return bt_common_color_code_reset;
290725f7
PP
333}
334
335BT_HIDDEN
336const char *bt_common_color_bold(void)
337{
6c4a0731 338 return bt_common_color_code_bold;
290725f7
PP
339}
340
341BT_HIDDEN
342const char *bt_common_color_fg_default(void)
343{
6c4a0731 344 return bt_common_color_code_fg_default;
290725f7
PP
345}
346
347BT_HIDDEN
348const char *bt_common_color_fg_red(void)
349{
6c4a0731 350 return bt_common_color_code_fg_red;
290725f7
PP
351}
352
353BT_HIDDEN
354const char *bt_common_color_fg_green(void)
355{
6c4a0731 356 return bt_common_color_code_fg_green;
290725f7
PP
357}
358
359BT_HIDDEN
360const char *bt_common_color_fg_yellow(void)
361{
6c4a0731 362 return bt_common_color_code_fg_yellow;
290725f7
PP
363}
364
365BT_HIDDEN
366const char *bt_common_color_fg_blue(void)
367{
6c4a0731 368 return bt_common_color_code_fg_blue;
290725f7
PP
369}
370
371BT_HIDDEN
372const char *bt_common_color_fg_magenta(void)
373{
6c4a0731 374 return bt_common_color_code_fg_magenta;
290725f7
PP
375}
376
377BT_HIDDEN
378const char *bt_common_color_fg_cyan(void)
379{
6c4a0731 380 return bt_common_color_code_fg_cyan;
290725f7
PP
381}
382
383BT_HIDDEN
384const char *bt_common_color_fg_light_gray(void)
385{
6c4a0731 386 return bt_common_color_code_fg_light_gray;
290725f7
PP
387}
388
389BT_HIDDEN
390const char *bt_common_color_bg_default(void)
391{
6c4a0731 392 return bt_common_color_code_bg_default;
290725f7
PP
393}
394
395BT_HIDDEN
396const char *bt_common_color_bg_red(void)
397{
6c4a0731 398 return bt_common_color_code_bg_red;
290725f7
PP
399}
400
401BT_HIDDEN
402const char *bt_common_color_bg_green(void)
403{
6c4a0731 404 return bt_common_color_code_bg_green;
290725f7
PP
405}
406
407BT_HIDDEN
408const char *bt_common_color_bg_yellow(void)
409{
6c4a0731 410 return bt_common_color_code_bg_yellow;
290725f7
PP
411}
412
413BT_HIDDEN
414const char *bt_common_color_bg_blue(void)
415{
6c4a0731 416 return bt_common_color_code_bg_blue;
290725f7
PP
417}
418
419BT_HIDDEN
420const char *bt_common_color_bg_magenta(void)
421{
6c4a0731 422 return bt_common_color_code_bg_magenta;
290725f7
PP
423}
424
425BT_HIDDEN
426const char *bt_common_color_bg_cyan(void)
427{
6c4a0731 428 return bt_common_color_code_bg_cyan;
290725f7
PP
429}
430
431BT_HIDDEN
432const char *bt_common_color_bg_light_gray(void)
433{
6c4a0731 434 return bt_common_color_code_bg_light_gray;
290725f7 435}
db0f160a
PP
436
437BT_HIDDEN
438GString *bt_common_string_until(const char *input, const char *escapable_chars,
439 const char *end_chars, size_t *end_pos)
440{
441 GString *output = g_string_new(NULL);
442 const char *ch;
443 const char *es_char;
444 const char *end_char;
445
446 if (!output) {
447 goto error;
448 }
449
450 for (ch = input; *ch != '\0'; ch++) {
451 if (*ch == '\\') {
452 bool continue_loop = false;
453
454 if (ch[1] == '\0') {
455 /* `\` at the end of the string: append `\` */
456 g_string_append_c(output, *ch);
457 ch++;
458 goto set_end_pos;
459 }
460
461 for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
462 if (ch[1] == *es_char) {
463 /*
464 * `\` followed by an escapable
465 * character: append the escaped
466 * character only.
467 */
468 g_string_append_c(output, ch[1]);
469 ch++;
470 continue_loop = true;
471 break;
472 }
473 }
474
475 if (continue_loop) {
476 continue;
477 }
478
479 /*
480 * `\` followed by a non-escapable character:
481 * append `\` and the character.
482 */
483 g_string_append_c(output, *ch);
484 g_string_append_c(output, ch[1]);
485 ch++;
486 continue;
487 } else {
488 for (end_char = end_chars; *end_char != '\0'; end_char++) {
489 if (*ch == *end_char) {
490 /*
491 * End character found:
492 * terminate this loop.
493 */
494 goto set_end_pos;
495 }
496 }
497
498 /* Normal character: append */
499 g_string_append_c(output, *ch);
500 }
501 }
502
503set_end_pos:
504 if (end_pos) {
505 *end_pos = ch - input;
506 }
507
508 goto end;
509
510error:
511 if (output) {
512 g_string_free(output, TRUE);
513 }
514
515end:
516 return output;
517}
518
519BT_HIDDEN
36b405c6 520GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
db0f160a
PP
521{
522 GString *output = g_string_new(NULL);
523 const char *ch;
524 bool no_quote = true;
525
526 if (!output) {
527 goto end;
528 }
529
530 if (strlen(input) == 0) {
36b405c6
PP
531 if (with_single_quotes) {
532 g_string_assign(output, "''");
533 }
534
db0f160a
PP
535 goto end;
536 }
537
538 for (ch = input; *ch != '\0'; ch++) {
539 const char c = *ch;
540
541 if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
542 c != '@' && c != '%' && c != '+' && c != '=' &&
543 c != ':' && c != ',' && c != '.' && c != '/' &&
544 c != '-') {
545 no_quote = false;
546 break;
547 }
548 }
549
550 if (no_quote) {
551 g_string_assign(output, input);
552 goto end;
553 }
554
36b405c6
PP
555 if (with_single_quotes) {
556 g_string_assign(output, "'");
557 }
db0f160a
PP
558
559 for (ch = input; *ch != '\0'; ch++) {
560 if (*ch == '\'') {
561 g_string_append(output, "'\"'\"'");
562 } else {
563 g_string_append_c(output, *ch);
564 }
565 }
566
36b405c6
PP
567 if (with_single_quotes) {
568 g_string_append_c(output, '\'');
569 }
db0f160a
PP
570
571end:
572 return output;
573}
574
575BT_HIDDEN
576bool bt_common_string_is_printable(const char *input)
577{
578 const char *ch;
579 bool printable = true;
f6ccaed9 580 BT_ASSERT(input);
db0f160a
PP
581
582 for (ch = input; *ch != '\0'; ch++) {
583 if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
584 *ch != '\t' && *ch != '\v') {
585 printable = false;
586 goto end;
587 }
588 }
589
590end:
591 return printable;
592}
593
594BT_HIDDEN
595void bt_common_destroy_lttng_live_url_parts(
596 struct bt_common_lttng_live_url_parts *parts)
597{
598 if (!parts) {
599 goto end;
600 }
601
602 if (parts->proto) {
603 g_string_free(parts->proto, TRUE);
604 parts->proto = NULL;
605 }
606
607 if (parts->hostname) {
608 g_string_free(parts->hostname, TRUE);
609 parts->hostname = NULL;
610 }
611
612 if (parts->target_hostname) {
613 g_string_free(parts->target_hostname, TRUE);
614 parts->target_hostname = NULL;
615 }
616
617 if (parts->session_name) {
618 g_string_free(parts->session_name, TRUE);
619 parts->session_name = NULL;
620 }
621
622end:
623 return;
624}
625
626BT_HIDDEN
627struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
628 const char *url, char *error_buf, size_t error_buf_size)
629{
630 struct bt_common_lttng_live_url_parts parts;
631 const char *at = url;
632 size_t end_pos;
633
f6ccaed9 634 BT_ASSERT(url);
db0f160a
PP
635 memset(&parts, 0, sizeof(parts));
636 parts.port = -1;
637
638 /* Protocol */
639 parts.proto = bt_common_string_until(at, "", ":", &end_pos);
640 if (!parts.proto || parts.proto->len == 0) {
641 if (error_buf) {
642 snprintf(error_buf, error_buf_size, "Missing protocol");
643 }
644
645 goto error;
646 }
647
648 if (strcmp(parts.proto->str, "net") == 0) {
649 g_string_assign(parts.proto, "net4");
650 }
651
652 if (strcmp(parts.proto->str, "net4") != 0 &&
653 strcmp(parts.proto->str, "net6") != 0) {
654 if (error_buf) {
655 snprintf(error_buf, error_buf_size,
656 "Unknown protocol: `%s`", parts.proto->str);
657 }
658
659 goto error;
660 }
661
662 if (at[end_pos] != ':') {
663 if (error_buf) {
664 snprintf(error_buf, error_buf_size,
665 "Expecting `:` after `%s`", parts.proto->str);
666 }
667
668 goto error;
669 }
670
671 at += end_pos;
672
673 /* :// */
674 if (strncmp(at, "://", 3) != 0) {
675 if (error_buf) {
676 snprintf(error_buf, error_buf_size,
677 "Expecting `://` after protocol");
678 }
679
680 goto error;
681 }
682
683 at += 3;
684
685 /* Hostname */
686 parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
687 if (!parts.hostname || parts.hostname->len == 0) {
688 if (error_buf) {
689 snprintf(error_buf, error_buf_size, "Missing hostname");
690 }
691
692 goto error;
693 }
694
695 if (at[end_pos] == ':') {
696 /* Port */
697 GString *port;
698
699 at += end_pos + 1;
700 port = bt_common_string_until(at, "", "/", &end_pos);
701 if (!port || port->len == 0) {
702 if (error_buf) {
703 snprintf(error_buf, error_buf_size, "Missing port");
704 }
705
706 goto error;
707 }
708
709 if (sscanf(port->str, "%d", &parts.port) != 1) {
710 if (error_buf) {
711 snprintf(error_buf, error_buf_size,
712 "Invalid port: `%s`", port->str);
713 }
714
715 g_string_free(port, TRUE);
716 goto error;
717 }
718
719 g_string_free(port, TRUE);
720
721 if (parts.port < 0 || parts.port >= 65536) {
722 if (error_buf) {
723 snprintf(error_buf, error_buf_size,
724 "Invalid port: %d", parts.port);
725 }
726
727 goto error;
728 }
729 }
730
731 if (at[end_pos] == '\0') {
732 goto end;
733 }
734
735 at += end_pos;
736
737 /* /host/ */
738 if (strncmp(at, "/host/", 6) != 0) {
739 if (error_buf) {
740 snprintf(error_buf, error_buf_size,
741 "Expecting `/host/` after hostname or port");
742 }
743
744 goto error;
745 }
746
747 at += 6;
748
749 /* Target hostname */
750 parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
751 if (!parts.target_hostname || parts.target_hostname->len == 0) {
752 if (error_buf) {
753 snprintf(error_buf, error_buf_size,
754 "Missing target hostname");
755 }
756
757 goto error;
758 }
759
760 if (at[end_pos] == '\0') {
761 goto end;
762 }
763
764 at += end_pos + 1;
765
766 /* Session name */
767 parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
768 if (!parts.session_name || parts.session_name->len == 0) {
769 if (error_buf) {
770 snprintf(error_buf, error_buf_size,
771 "Missing session name");
772 }
773
774 goto error;
775 }
776
777 if (at[end_pos] == '/') {
778 if (error_buf) {
779 snprintf(error_buf, error_buf_size,
780 "Unexpected `/` after session name (`%s`)",
781 parts.session_name->str);
782 }
783
784 goto error;
785 }
786
787 goto end;
788
789error:
790 bt_common_destroy_lttng_live_url_parts(&parts);
791
792end:
793 return parts;
794}
9009cc24
PP
795
796BT_HIDDEN
797void bt_common_normalize_star_glob_pattern(char *pattern)
798{
799 const char *p;
800 char *np;
801 bool got_star = false;
802
f6ccaed9 803 BT_ASSERT(pattern);
9009cc24
PP
804
805 for (p = pattern, np = pattern; *p != '\0'; p++) {
806 switch (*p) {
807 case '*':
808 if (got_star) {
809 /* Avoid consecutive stars. */
810 continue;
811 }
812
813 got_star = true;
814 break;
815 case '\\':
816 /* Copy backslash character. */
817 *np = *p;
818 np++;
819 p++;
820
821 if (*p == '\0') {
822 goto end;
823 }
824
825 /* Fall through default case. */
826 default:
827 got_star = false;
828 break;
829 }
830
831 /* Copy single character. */
832 *np = *p;
833 np++;
834 }
835
836end:
837 *np = '\0';
838}
839
840static inline
841bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
842{
843 return (p - pattern) == pattern_len || *p == '\0';
844}
845
846/*
847 * Globbing matching function with the star feature only (`?` and
848 * character sets are not supported). This matches `candidate` (plain
849 * string) against `pattern`. A literal star can be escaped with `\` in
850 * `pattern`.
851 *
852 * `pattern_len` or `candidate_len` can be greater than the actual
853 * string length of `pattern` or `candidate` if the string is
854 * null-terminated.
855 */
856BT_HIDDEN
857bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
858 const char *candidate, size_t candidate_len) {
859 const char *retry_c = candidate, *retry_p = pattern, *c, *p;
860 bool got_a_star = false;
861
862retry:
863 c = retry_c;
864 p = retry_p;
865
866 /*
867 * The concept here is to retry a match in the specific case
868 * where we already got a star. The retry position for the
869 * pattern is just after the most recent star, and the retry
870 * position for the candidate is the character following the
871 * last try's first character.
872 *
873 * Example:
874 *
875 * candidate: hi ev every onyx one
876 * ^
877 * pattern: hi*every*one
878 * ^
879 *
880 * candidate: hi ev every onyx one
881 * ^
882 * pattern: hi*every*one
883 * ^
884 *
885 * candidate: hi ev every onyx one
886 * ^
887 * pattern: hi*every*one
888 * ^
889 *
890 * candidate: hi ev every onyx one
891 * ^
892 * pattern: hi*every*one
893 * ^ MISMATCH
894 *
895 * candidate: hi ev every onyx one
896 * ^
897 * pattern: hi*every*one
898 * ^
899 *
900 * candidate: hi ev every onyx one
901 * ^^
902 * pattern: hi*every*one
903 * ^^
904 *
905 * candidate: hi ev every onyx one
906 * ^ ^
907 * pattern: hi*every*one
908 * ^ ^ MISMATCH
909 *
910 * candidate: hi ev every onyx one
911 * ^
912 * pattern: hi*every*one
913 * ^ MISMATCH
914 *
915 * candidate: hi ev every onyx one
916 * ^
917 * pattern: hi*every*one
918 * ^ MISMATCH
919 *
920 * candidate: hi ev every onyx one
921 * ^
922 * pattern: hi*every*one
923 * ^
924 *
925 * candidate: hi ev every onyx one
926 * ^^
927 * pattern: hi*every*one
928 * ^^
929 *
930 * candidate: hi ev every onyx one
931 * ^ ^
932 * pattern: hi*every*one
933 * ^ ^
934 *
935 * candidate: hi ev every onyx one
936 * ^ ^
937 * pattern: hi*every*one
938 * ^ ^
939 *
940 * candidate: hi ev every onyx one
941 * ^ ^
942 * pattern: hi*every*one
943 * ^ ^
944 *
945 * candidate: hi ev every onyx one
946 * ^
947 * pattern: hi*every*one
948 * ^
949 *
950 * candidate: hi ev every onyx one
951 * ^
952 * pattern: hi*every*one
953 * ^ MISMATCH
954 *
955 * candidate: hi ev every onyx one
956 * ^
957 * pattern: hi*every*one
958 * ^
959 *
960 * candidate: hi ev every onyx one
961 * ^^
962 * pattern: hi*every*one
963 * ^^
964 *
965 * candidate: hi ev every onyx one
966 * ^ ^
967 * pattern: hi*every*one
968 * ^ ^ MISMATCH
969 *
970 * candidate: hi ev every onyx one
971 * ^
972 * pattern: hi*every*one
973 * ^ MISMATCH
974 *
975 * candidate: hi ev every onyx one
976 * ^
977 * pattern: hi*every*one
978 * ^ MISMATCH
979 *
980 * candidate: hi ev every onyx one
981 * ^
982 * pattern: hi*every*one
983 * ^ MISMATCH
984 *
985 * candidate: hi ev every onyx one
986 * ^
987 * pattern: hi*every*one
988 * ^ MISMATCH
989 *
990 * candidate: hi ev every onyx one
991 * ^
992 * pattern: hi*every*one
993 * ^
994 *
995 * candidate: hi ev every onyx one
996 * ^^
997 * pattern: hi*every*one
998 * ^^
999 *
1000 * candidate: hi ev every onyx one
1001 * ^ ^
1002 * pattern: hi*every*one
1003 * ^ ^
1004 *
1005 * candidate: hi ev every onyx one
1006 * ^ ^
1007 * pattern: hi*every*one
1008 * ^ ^ SUCCESS
1009 */
1010 while ((c - candidate) < candidate_len && *c != '\0') {
f6ccaed9 1011 BT_ASSERT(*c);
9009cc24
PP
1012
1013 if (at_end_of_pattern(p, pattern, pattern_len)) {
1014 goto end_of_pattern;
1015 }
1016
1017 switch (*p) {
1018 case '*':
1019 got_a_star = true;
1020
1021 /*
1022 * Our first try starts at the current candidate
1023 * character and after the star in the pattern.
1024 */
1025 retry_c = c;
1026 retry_p = p + 1;
1027
1028 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
1029 /*
1030 * Star at the end of the pattern at
1031 * this point: automatic match.
1032 */
1033 return true;
1034 }
1035
1036 goto retry;
1037 case '\\':
1038 /* Go to escaped character. */
1039 p++;
1040
1041 /*
1042 * Fall through the default case which compares
1043 * the escaped character now.
1044 */
1045 default:
1046 if (at_end_of_pattern(p, pattern, pattern_len) ||
1047 *c != *p) {
1048end_of_pattern:
1049 /* Character mismatch OR end of pattern. */
1050 if (!got_a_star) {
1051 /*
1052 * We didn't get any star yet,
1053 * so this first mismatch
1054 * automatically makes the whole
1055 * test fail.
1056 */
1057 return false;
1058 }
1059
1060 /*
1061 * Next try: next candidate character,
1062 * original pattern character (following
1063 * the most recent star).
1064 */
1065 retry_c++;
1066 goto retry;
1067 }
1068 break;
1069 }
1070
1071 /* Next pattern and candidate characters. */
1072 c++;
1073 p++;
1074 }
1075
1076 /*
1077 * We checked every candidate character and we're still in a
1078 * success state: the only pattern character allowed to remain
1079 * is a star.
1080 */
1081 if (at_end_of_pattern(p, pattern, pattern_len)) {
1082 return true;
1083 }
1084
1085 p++;
1086 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
1087}
e49a18d1
PP
1088
1089static
1090void append_path_parts(const char *path, GPtrArray *parts)
1091{
1092 const char *ch = path;
1093 const char *last = path;
1094
1095 while (true) {
1096 if (*ch == G_DIR_SEPARATOR || *ch == '\0') {
1097 if (ch - last > 0) {
1098 GString *part = g_string_new(NULL);
1099
f6ccaed9 1100 BT_ASSERT(part);
e49a18d1
PP
1101 g_string_append_len(part, last, ch - last);
1102 g_ptr_array_add(parts, part);
1103 }
1104
1105 if (*ch == '\0') {
1106 break;
1107 }
1108
1109 last = ch + 1;
1110 }
1111
1112 ch++;
1113 }
1114}
1115
1116static
1117void destroy_gstring(void *gstring)
1118{
1119 (void) g_string_free(gstring, TRUE);
1120}
1121
1cd3decc
MJ
1122#ifdef __MINGW32__
1123BT_HIDDEN
1124GString *bt_common_normalize_path(const char *path, const char *wd)
1125{
1126 char *tmp;
1127 GString *norm_path = NULL;
1128
f6ccaed9 1129 BT_ASSERT(path);
1cd3decc
MJ
1130
1131 tmp = _fullpath(NULL, path, PATH_MAX);
1132 if (!tmp) {
1133 goto error;
1134 }
1135
1136 norm_path = g_string_new(tmp);
1137 if (!norm_path) {
1138 goto error;
1139 }
1140
1141 goto end;
1142error:
1143 if (norm_path) {
1144 g_string_free(norm_path, TRUE);
1145 norm_path = NULL;
1146 }
1147end:
1148 if (tmp) {
1149 free(tmp);
1150 }
1151 return norm_path;
1152}
1153#else
e49a18d1
PP
1154BT_HIDDEN
1155GString *bt_common_normalize_path(const char *path, const char *wd)
1156{
1157 size_t i;
1158 GString *norm_path;
1159 GPtrArray *parts = NULL;
1160
f6ccaed9 1161 BT_ASSERT(path);
e49a18d1
PP
1162 norm_path = g_string_new(G_DIR_SEPARATOR_S);
1163 if (!norm_path) {
1164 goto error;
1165 }
1166
1167 parts = g_ptr_array_new_with_free_func(destroy_gstring);
1168 if (!parts) {
1169 goto error;
1170 }
1171
1172 if (path[0] != G_DIR_SEPARATOR) {
1173 /* Relative path: start with working directory */
1174 if (wd) {
1175 append_path_parts(wd, parts);
1176 } else {
1177 gchar *cd = g_get_current_dir();
1178
1179 append_path_parts(cd, parts);
1180 g_free(cd);
1181 }
1182 }
1183
1184 /* Append parts of the path parameter */
1185 append_path_parts(path, parts);
1186
1187 /* Resolve special `..` and `.` parts */
1188 for (i = 0; i < parts->len; i++) {
1189 GString *part = g_ptr_array_index(parts, i);
1190
1191 if (strcmp(part->str, "..") == 0) {
1192 if (i == 0) {
1193 /*
1194 * First part of absolute path is `..`:
1195 * this is invalid.
1196 */
1197 goto error;
1198 }
1199
1200 /* Remove `..` and previous part */
1201 g_ptr_array_remove_index(parts, i - 1);
1202 g_ptr_array_remove_index(parts, i - 1);
1203 i -= 2;
1204 } else if (strcmp(part->str, ".") == 0) {
1205 /* Remove `.` */
1206 g_ptr_array_remove_index(parts, i);
1207 i -= 1;
1208 }
1209 }
1210
1211 /* Create normalized path with what's left */
1212 for (i = 0; i < parts->len; i++) {
1213 GString *part = g_ptr_array_index(parts, i);
1214
1215 g_string_append(norm_path, part->str);
1216
1217 if (i < parts->len - 1) {
1218 g_string_append_c(norm_path, G_DIR_SEPARATOR);
1219 }
1220 }
1221
1222 goto end;
1223
1224error:
1225 if (norm_path) {
1226 g_string_free(norm_path, TRUE);
1227 norm_path = NULL;
1228 }
1229
1230end:
1231 if (parts) {
1232 g_ptr_array_free(parts, TRUE);
1233 }
1234
1235 return norm_path;
1236}
1cd3decc 1237#endif
108e5a1e
MJ
1238
1239BT_HIDDEN
1240size_t bt_common_get_page_size(void)
1241{
1242 int page_size;
1243
1244 page_size = bt_sysconf(_SC_PAGESIZE);
1245 if (page_size < 0) {
b4565e8b
PP
1246 BT_LOGF("Cannot get system's page size: ret=%d",
1247 page_size);
108e5a1e
MJ
1248 abort();
1249 }
1250
1251 return page_size;
1252}
85cd02cf
PP
1253
1254#define BUF_STD_APPEND(...) \
1255 do { \
1256 char _tmp_fmt[64]; \
1257 int _count; \
1258 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1259 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1260 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1261 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1262 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
f6ccaed9 1263 BT_ASSERT(_count >= 0); \
85cd02cf
PP
1264 *buf_ch += MIN(_count, _size); \
1265 } while (0)
1266
1267#define BUF_STD_APPEND_SINGLE_ARG(_type) \
1268 do { \
1269 _type _arg = va_arg(*args, _type); \
1270 BUF_STD_APPEND(_arg); \
1271 } while (0)
1272
1273static inline void handle_conversion_specifier_std(char *buf, char **buf_ch,
1274 size_t buf_size, const char **out_fmt_ch, va_list *args)
1275{
1276 const char *fmt_ch = *out_fmt_ch;
1277 enum LENGTH_MODIFIER {
1278 LENGTH_MOD_H,
1279 LENGTH_MOD_HH,
1280 LENGTH_MOD_NONE,
1281 LENGTH_MOD_LOW_L,
1282 LENGTH_MOD_LOW_LL,
1283 LENGTH_MOD_UP_L,
1284 LENGTH_MOD_Z,
1285 } length_mod = LENGTH_MOD_NONE;
1286
1287 /* skip '%' */
1288 fmt_ch++;
1289
1290 if (*fmt_ch == '%') {
1291 fmt_ch++;
1292 **buf_ch = '%';
1293 (*buf_ch)++;
1294 goto update_rw_fmt;
1295 }
1296
1297 /* flags */
1298 for (;;) {
1299 switch (*fmt_ch) {
1300 case '-':
1301 case '+':
1302 case ' ':
1303 case '#':
1304 case '0':
1305 case '\'':
1306 fmt_ch++;
1307 continue;
1308 default:
1309 break;
1310 }
1311 break;
1312 }
1313
1314 /* width */
1315 for (;;) {
1316 if (*fmt_ch < '0' || *fmt_ch > '9') {
1317 break;
1318 }
1319
1320 fmt_ch++;
1321 }
1322
1323 /* precision */
1324 if (*fmt_ch == '.') {
1325 fmt_ch++;
1326
1327 for (;;) {
1328 if (*fmt_ch < '0' || *fmt_ch > '9') {
1329 break;
1330 }
1331
1332 fmt_ch++;
1333 }
1334 }
1335
1336 /* format (PRI*64) */
44c440bc 1337 if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) {
85cd02cf
PP
1338 fmt_ch += sizeof(PRId64);
1339 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1340 goto update_rw_fmt;
44c440bc 1341 } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) {
85cd02cf
PP
1342 fmt_ch += sizeof(PRIu64);
1343 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1344 goto update_rw_fmt;
44c440bc 1345 } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) {
85cd02cf
PP
1346 fmt_ch += sizeof(PRIx64);
1347 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1348 goto update_rw_fmt;
44c440bc 1349 } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) {
85cd02cf
PP
1350 fmt_ch += sizeof(PRIX64);
1351 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1352 goto update_rw_fmt;
44c440bc 1353 } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) {
85cd02cf
PP
1354 fmt_ch += sizeof(PRIo64);
1355 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1356 goto update_rw_fmt;
44c440bc 1357 } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) {
85cd02cf
PP
1358 fmt_ch += sizeof(PRIi64);
1359 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1360 goto update_rw_fmt;
1361 }
1362
1363 // length modifier
1364 switch (*fmt_ch) {
1365 case 'h':
1366 length_mod = LENGTH_MOD_H;
1367 fmt_ch++;
1368
1369 if (*fmt_ch == 'h') {
1370 length_mod = LENGTH_MOD_HH;
1371 fmt_ch++;
1372 break;
1373 }
1374 break;
1375 case 'l':
1376 length_mod = LENGTH_MOD_LOW_L;
1377 fmt_ch++;
1378
1379 if (*fmt_ch == 'l') {
1380 length_mod = LENGTH_MOD_LOW_LL;
1381 fmt_ch++;
1382 break;
1383 }
1384 break;
1385 case 'L':
1386 length_mod = LENGTH_MOD_UP_L;
1387 fmt_ch++;
1388 break;
1389 case 'z':
1390 length_mod = LENGTH_MOD_Z;
1391 fmt_ch++;
1392 break;
1393 default:
1394 break;
1395 }
1396
1397 // format
1398 switch (*fmt_ch) {
1399 case 'c':
1400 {
1401 fmt_ch++;
1402
1403 switch (length_mod) {
1404 case LENGTH_MOD_NONE:
1405 BUF_STD_APPEND_SINGLE_ARG(int);
1406 break;
1407 case LENGTH_MOD_LOW_L:
1408 BUF_STD_APPEND_SINGLE_ARG(wint_t);
1409 break;
1410 default:
1411 abort();
1412 }
1413 break;
1414 }
1415 case 's':
1416 fmt_ch++;
1417
1418 switch (length_mod) {
1419 case LENGTH_MOD_NONE:
1420 BUF_STD_APPEND_SINGLE_ARG(char *);
1421 break;
1422 case LENGTH_MOD_LOW_L:
1423 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1424 break;
1425 default:
1426 abort();
1427 }
1428 break;
1429 case 'd':
1430 case 'i':
1431 fmt_ch++;
1432
1433 switch (length_mod) {
1434 case LENGTH_MOD_NONE:
1435 case LENGTH_MOD_H:
1436 case LENGTH_MOD_HH:
1437 BUF_STD_APPEND_SINGLE_ARG(int);
1438 break;
1439 case LENGTH_MOD_LOW_L:
1440 BUF_STD_APPEND_SINGLE_ARG(long);
1441 break;
1442 case LENGTH_MOD_LOW_LL:
1443 BUF_STD_APPEND_SINGLE_ARG(long long);
1444 break;
1445 case LENGTH_MOD_Z:
1446 BUF_STD_APPEND_SINGLE_ARG(size_t);
1447 break;
1448 default:
1449 abort();
1450 }
1451 break;
1452 case 'o':
1453 case 'x':
1454 case 'X':
1455 case 'u':
1456 fmt_ch++;
1457
1458 switch (length_mod) {
1459 case LENGTH_MOD_NONE:
1460 case LENGTH_MOD_H:
1461 case LENGTH_MOD_HH:
1462 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1463 break;
1464 case LENGTH_MOD_LOW_L:
1465 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1466 break;
1467 case LENGTH_MOD_LOW_LL:
1468 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1469 break;
1470 case LENGTH_MOD_Z:
1471 BUF_STD_APPEND_SINGLE_ARG(size_t);
1472 break;
1473 default:
1474 abort();
1475 }
1476 break;
1477 case 'f':
1478 case 'F':
1479 case 'e':
1480 case 'E':
1481 case 'g':
1482 case 'G':
1483 fmt_ch++;
1484
1485 switch (length_mod) {
1486 case LENGTH_MOD_NONE:
1487 BUF_STD_APPEND_SINGLE_ARG(double);
1488 break;
1489 case LENGTH_MOD_UP_L:
1490 BUF_STD_APPEND_SINGLE_ARG(long double);
1491 break;
1492 default:
1493 abort();
1494 }
1495 break;
1496 case 'p':
1497 fmt_ch++;
1498
1499 if (length_mod == LENGTH_MOD_NONE) {
1500 BUF_STD_APPEND_SINGLE_ARG(void *);
1501 } else {
1502 abort();
1503 }
1504 break;
1505 default:
1506 abort();
1507 }
1508
1509update_rw_fmt:
1510 *out_fmt_ch = fmt_ch;
1511}
1512
1513BT_HIDDEN
1514void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
1515 char intro,
1516 bt_common_handle_custom_specifier_func handle_specifier,
1517 void *priv_data, const char *fmt, va_list *args)
1518{
1519 const char *fmt_ch = fmt;
1520 char *buf_ch = buf;
1521
f6ccaed9
PP
1522 BT_ASSERT(buf);
1523 BT_ASSERT(fmt);
1524 BT_ASSERT(*args);
85cd02cf
PP
1525
1526 while (*fmt_ch != '\0') {
1527 switch (*fmt_ch) {
1528 case '%':
f6ccaed9 1529 BT_ASSERT(fmt_ch[1] != '\0');
85cd02cf
PP
1530
1531 if (fmt_ch[1] == intro) {
1532 handle_specifier(priv_data, &buf_ch,
1533 buf_size - (size_t) (buf_ch - buf),
1534 &fmt_ch, args);
1535 } else {
1536 handle_conversion_specifier_std(buf, &buf_ch,
1537 buf_size, &fmt_ch, args);
1538 }
1539
1540 if (buf_ch >= buf + buf_size - 1) {
1541 fmt_ch = "";
1542 }
1543 break;
1544 default:
1545 *buf_ch = *fmt_ch;
1546 buf_ch++;
1547 if (buf_ch >= buf + buf_size - 1) {
1548 fmt_ch = "";
1549 }
1550
1551 fmt_ch++;
1552 }
1553 }
1554
1555 *buf_ch = '\0';
1556}
1557
1558BT_HIDDEN
1559void bt_common_custom_snprintf(char *buf, size_t buf_size,
1560 char intro,
1561 bt_common_handle_custom_specifier_func handle_specifier,
1562 void *priv_data, const char *fmt, ...)
1563{
1564 va_list args;
1565 va_start(args, fmt);
1566 bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier,
1567 priv_data, fmt, &args);
1568 va_end(args);
1569}
This page took 0.093222 seconds and 4 git commands to generate.