Logging: use a TLS buffer and increase its size
[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
PP
29#include <string.h>
30#include <sys/types.h>
fcc7e8dd 31#include <sys/stat.h>
1670bffd
PP
32#include <unistd.h>
33#include <assert.h>
db0f160a 34#include <ctype.h>
1670bffd 35#include <glib.h>
108e5a1e 36#include <stdlib.h>
fcc7e8dd 37#include <stdbool.h>
1670bffd 38#include <babeltrace/babeltrace-internal.h>
ad96d936 39#include <babeltrace/common-internal.h>
108e5a1e 40#include <babeltrace/compat/unistd-internal.h>
1670bffd 41
2006c005
MJ
42#ifndef __MINGW32__
43#include <pwd.h>
44#endif
45
1670bffd
PP
46#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
47#define HOME_ENV_VAR "HOME"
48#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
49
6c4a0731
PP
50static const char *bt_common_color_code_reset = "";
51static const char *bt_common_color_code_bold = "";
52static const char *bt_common_color_code_fg_default = "";
53static const char *bt_common_color_code_fg_red = "";
54static const char *bt_common_color_code_fg_green = "";
55static const char *bt_common_color_code_fg_yellow = "";
56static const char *bt_common_color_code_fg_blue = "";
57static const char *bt_common_color_code_fg_magenta = "";
58static const char *bt_common_color_code_fg_cyan = "";
59static const char *bt_common_color_code_fg_light_gray = "";
60static const char *bt_common_color_code_bg_default = "";
61static const char *bt_common_color_code_bg_red = "";
62static const char *bt_common_color_code_bg_green = "";
63static const char *bt_common_color_code_bg_yellow = "";
64static const char *bt_common_color_code_bg_blue = "";
65static const char *bt_common_color_code_bg_magenta = "";
66static const char *bt_common_color_code_bg_cyan = "";
67static const char *bt_common_color_code_bg_light_gray = "";
68
69static
70void __attribute__((constructor)) bt_common_color_ctor(void)
71{
72 if (bt_common_colors_supported()) {
73 bt_common_color_code_reset = BT_COMMON_COLOR_RESET;
74 bt_common_color_code_bold = BT_COMMON_COLOR_BOLD;
75 bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT;
76 bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED;
77 bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN;
78 bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW;
79 bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE;
80 bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA;
81 bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN;
82 bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY;
83 bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT;
84 bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED;
85 bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN;
86 bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW;
87 bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE;
88 bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA;
89 bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN;
90 bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY;
91 }
92}
93
1670bffd
PP
94BT_HIDDEN
95const char *bt_common_get_system_plugin_path(void)
96{
97 return SYSTEM_PLUGIN_PATH;
98}
99
a613ad68
MJ
100#ifdef __MINGW32__
101BT_HIDDEN
102bool bt_common_is_setuid_setgid(void)
103{
104 return false;
105}
106#else /* __MINGW32__ */
1670bffd
PP
107BT_HIDDEN
108bool bt_common_is_setuid_setgid(void)
109{
110 return (geteuid() != getuid() || getegid() != getgid());
111}
a613ad68 112#endif /* __MINGW32__ */
1670bffd 113
2006c005
MJ
114static
115char *bt_secure_getenv(const char *name)
1670bffd
PP
116{
117 if (bt_common_is_setuid_setgid()) {
b4565e8b
PP
118 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
119 "name=\"%s\"", name);
1670bffd
PP
120 return NULL;
121 }
122 return getenv(name);
123}
124
2006c005
MJ
125#ifdef __MINGW32__
126static
127const char *bt_get_home_dir(void)
128{
129 return g_get_home_dir();
130}
131#else /* __MINGW32__ */
132static
133const char *bt_get_home_dir(void)
1670bffd
PP
134{
135 char *val = NULL;
136 struct passwd *pwd;
137
138 val = bt_secure_getenv(HOME_ENV_VAR);
139 if (val) {
140 goto end;
141 }
142 /* Fallback on password file. */
143 pwd = getpwuid(getuid());
144 if (!pwd) {
145 goto end;
146 }
147 val = pwd->pw_dir;
148end:
149 return val;
150}
2006c005 151#endif /* __MINGW32__ */
1670bffd
PP
152
153BT_HIDDEN
154char *bt_common_get_home_plugin_path(void)
155{
156 char *path = NULL;
157 const char *home_dir;
b4565e8b 158 size_t length;
1670bffd 159
2006c005 160 home_dir = bt_get_home_dir();
1670bffd
PP
161 if (!home_dir) {
162 goto end;
163 }
164
b4565e8b
PP
165 length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1;
166
167 if (length >= PATH_MAX) {
168 BT_LOGW("Home directory path is too long: length=%zu",
169 length);
1670bffd
PP
170 goto end;
171 }
172
173 path = malloc(PATH_MAX);
174 if (!path) {
175 goto end;
176 }
177
178 strcpy(path, home_dir);
179 strcat(path, HOME_PLUGIN_SUBPATH);
180
181end:
182 return path;
183}
184
185BT_HIDDEN
186int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
187{
188 int ret = 0;
189 const char *at;
190 const char *end;
191 size_t init_dirs_len;
192
193 assert(dirs);
194 init_dirs_len = dirs->len;
195
196 if (!paths) {
197 /* Nothing to append */
198 goto end;
199 }
200
201 at = paths;
202 end = paths + strlen(paths);
203
204 while (at < end) {
205 GString *path;
52238017 206 const char *next_sep;
1670bffd 207
52238017
MJ
208 next_sep = strchr(at, G_SEARCHPATH_SEPARATOR);
209 if (next_sep == at) {
1670bffd
PP
210 /*
211 * Empty path: try next character (supported
212 * to conform to the typical parsing of $PATH).
213 */
214 at++;
215 continue;
52238017
MJ
216 } else if (!next_sep) {
217 /* No more separator: use the remaining */
218 next_sep = paths + strlen(paths);
1670bffd
PP
219 }
220
221 path = g_string_new(NULL);
222 if (!path) {
223 goto error;
224 }
225
52238017
MJ
226 g_string_append_len(path, at, next_sep - at);
227 at = next_sep + 1;
1670bffd
PP
228 g_ptr_array_add(dirs, path);
229 }
230
231 goto end;
232
233error:
234 ret = -1;
235
236 /* Remove the new entries in dirs */
237 while (dirs->len > init_dirs_len) {
238 g_ptr_array_remove_index(dirs, init_dirs_len);
239 }
240
241end:
242 return ret;
243}
290725f7 244
fcc7e8dd
PP
245static
246bool isarealtty(int fd)
247{
248 bool istty = false;
249 struct stat tty_stats;
250
251 if (!isatty(fd)) {
252 /* Not a TTY */
253 goto end;
254 }
255
256 if (fstat(fd, &tty_stats) == 0) {
257 if (!S_ISCHR(tty_stats.st_mode)) {
258 /* Not a character device: not a TTY */
259 goto end;
260 }
261 }
262
263 istty = true;
264
265end:
266 return istty;
267}
268
ad96d936
PP
269BT_HIDDEN
270bool bt_common_colors_supported(void)
290725f7
PP
271{
272 static bool supports_colors = false;
273 static bool supports_colors_set = false;
fcc7e8dd
PP
274 const char *term_env_var;
275 const char *term_color_env_var;
290725f7
PP
276
277 if (supports_colors_set) {
278 goto end;
279 }
280
281 supports_colors_set = true;
282
fcc7e8dd
PP
283 /*
284 * `BABELTRACE_TERM_COLOR` environment variable always overrides
285 * the automatic color support detection.
286 */
287 term_color_env_var = getenv("BABELTRACE_TERM_COLOR");
288 if (term_color_env_var) {
289 if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) {
290 /* Force colors */
291 supports_colors = true;
292 } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) {
293 /* Force no colors */
294 goto end;
295 }
62128320
PP
296 }
297
fcc7e8dd
PP
298 /* We need a compatible, known terminal */
299 term_env_var = getenv("TERM");
300 if (!term_env_var) {
290725f7
PP
301 goto end;
302 }
303
fcc7e8dd
PP
304 if (strncmp(term_env_var, "xterm", 5) != 0 &&
305 strncmp(term_env_var, "rxvt", 4) != 0 &&
306 strncmp(term_env_var, "konsole", 7) != 0 &&
307 strncmp(term_env_var, "gnome", 5) != 0 &&
308 strncmp(term_env_var, "screen", 5) != 0 &&
309 strncmp(term_env_var, "tmux", 4) != 0 &&
310 strncmp(term_env_var, "putty", 5) != 0) {
290725f7
PP
311 goto end;
312 }
313
fcc7e8dd
PP
314 /* Both standard output and error streams need to be TTYs */
315 if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) {
290725f7
PP
316 goto end;
317 }
318
319 supports_colors = true;
320
321end:
322 return supports_colors;
323}
324
325BT_HIDDEN
326const char *bt_common_color_reset(void)
327{
6c4a0731 328 return bt_common_color_code_reset;
290725f7
PP
329}
330
331BT_HIDDEN
332const char *bt_common_color_bold(void)
333{
6c4a0731 334 return bt_common_color_code_bold;
290725f7
PP
335}
336
337BT_HIDDEN
338const char *bt_common_color_fg_default(void)
339{
6c4a0731 340 return bt_common_color_code_fg_default;
290725f7
PP
341}
342
343BT_HIDDEN
344const char *bt_common_color_fg_red(void)
345{
6c4a0731 346 return bt_common_color_code_fg_red;
290725f7
PP
347}
348
349BT_HIDDEN
350const char *bt_common_color_fg_green(void)
351{
6c4a0731 352 return bt_common_color_code_fg_green;
290725f7
PP
353}
354
355BT_HIDDEN
356const char *bt_common_color_fg_yellow(void)
357{
6c4a0731 358 return bt_common_color_code_fg_yellow;
290725f7
PP
359}
360
361BT_HIDDEN
362const char *bt_common_color_fg_blue(void)
363{
6c4a0731 364 return bt_common_color_code_fg_blue;
290725f7
PP
365}
366
367BT_HIDDEN
368const char *bt_common_color_fg_magenta(void)
369{
6c4a0731 370 return bt_common_color_code_fg_magenta;
290725f7
PP
371}
372
373BT_HIDDEN
374const char *bt_common_color_fg_cyan(void)
375{
6c4a0731 376 return bt_common_color_code_fg_cyan;
290725f7
PP
377}
378
379BT_HIDDEN
380const char *bt_common_color_fg_light_gray(void)
381{
6c4a0731 382 return bt_common_color_code_fg_light_gray;
290725f7
PP
383}
384
385BT_HIDDEN
386const char *bt_common_color_bg_default(void)
387{
6c4a0731 388 return bt_common_color_code_bg_default;
290725f7
PP
389}
390
391BT_HIDDEN
392const char *bt_common_color_bg_red(void)
393{
6c4a0731 394 return bt_common_color_code_bg_red;
290725f7
PP
395}
396
397BT_HIDDEN
398const char *bt_common_color_bg_green(void)
399{
6c4a0731 400 return bt_common_color_code_bg_green;
290725f7
PP
401}
402
403BT_HIDDEN
404const char *bt_common_color_bg_yellow(void)
405{
6c4a0731 406 return bt_common_color_code_bg_yellow;
290725f7
PP
407}
408
409BT_HIDDEN
410const char *bt_common_color_bg_blue(void)
411{
6c4a0731 412 return bt_common_color_code_bg_blue;
290725f7
PP
413}
414
415BT_HIDDEN
416const char *bt_common_color_bg_magenta(void)
417{
6c4a0731 418 return bt_common_color_code_bg_magenta;
290725f7
PP
419}
420
421BT_HIDDEN
422const char *bt_common_color_bg_cyan(void)
423{
6c4a0731 424 return bt_common_color_code_bg_cyan;
290725f7
PP
425}
426
427BT_HIDDEN
428const char *bt_common_color_bg_light_gray(void)
429{
6c4a0731 430 return bt_common_color_code_bg_light_gray;
290725f7 431}
db0f160a
PP
432
433BT_HIDDEN
434GString *bt_common_string_until(const char *input, const char *escapable_chars,
435 const char *end_chars, size_t *end_pos)
436{
437 GString *output = g_string_new(NULL);
438 const char *ch;
439 const char *es_char;
440 const char *end_char;
441
442 if (!output) {
443 goto error;
444 }
445
446 for (ch = input; *ch != '\0'; ch++) {
447 if (*ch == '\\') {
448 bool continue_loop = false;
449
450 if (ch[1] == '\0') {
451 /* `\` at the end of the string: append `\` */
452 g_string_append_c(output, *ch);
453 ch++;
454 goto set_end_pos;
455 }
456
457 for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
458 if (ch[1] == *es_char) {
459 /*
460 * `\` followed by an escapable
461 * character: append the escaped
462 * character only.
463 */
464 g_string_append_c(output, ch[1]);
465 ch++;
466 continue_loop = true;
467 break;
468 }
469 }
470
471 if (continue_loop) {
472 continue;
473 }
474
475 /*
476 * `\` followed by a non-escapable character:
477 * append `\` and the character.
478 */
479 g_string_append_c(output, *ch);
480 g_string_append_c(output, ch[1]);
481 ch++;
482 continue;
483 } else {
484 for (end_char = end_chars; *end_char != '\0'; end_char++) {
485 if (*ch == *end_char) {
486 /*
487 * End character found:
488 * terminate this loop.
489 */
490 goto set_end_pos;
491 }
492 }
493
494 /* Normal character: append */
495 g_string_append_c(output, *ch);
496 }
497 }
498
499set_end_pos:
500 if (end_pos) {
501 *end_pos = ch - input;
502 }
503
504 goto end;
505
506error:
507 if (output) {
508 g_string_free(output, TRUE);
509 }
510
511end:
512 return output;
513}
514
515BT_HIDDEN
36b405c6 516GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
db0f160a
PP
517{
518 GString *output = g_string_new(NULL);
519 const char *ch;
520 bool no_quote = true;
521
522 if (!output) {
523 goto end;
524 }
525
526 if (strlen(input) == 0) {
36b405c6
PP
527 if (with_single_quotes) {
528 g_string_assign(output, "''");
529 }
530
db0f160a
PP
531 goto end;
532 }
533
534 for (ch = input; *ch != '\0'; ch++) {
535 const char c = *ch;
536
537 if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
538 c != '@' && c != '%' && c != '+' && c != '=' &&
539 c != ':' && c != ',' && c != '.' && c != '/' &&
540 c != '-') {
541 no_quote = false;
542 break;
543 }
544 }
545
546 if (no_quote) {
547 g_string_assign(output, input);
548 goto end;
549 }
550
36b405c6
PP
551 if (with_single_quotes) {
552 g_string_assign(output, "'");
553 }
db0f160a
PP
554
555 for (ch = input; *ch != '\0'; ch++) {
556 if (*ch == '\'') {
557 g_string_append(output, "'\"'\"'");
558 } else {
559 g_string_append_c(output, *ch);
560 }
561 }
562
36b405c6
PP
563 if (with_single_quotes) {
564 g_string_append_c(output, '\'');
565 }
db0f160a
PP
566
567end:
568 return output;
569}
570
571BT_HIDDEN
572bool bt_common_string_is_printable(const char *input)
573{
574 const char *ch;
575 bool printable = true;
576 assert(input);
577
578 for (ch = input; *ch != '\0'; ch++) {
579 if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
580 *ch != '\t' && *ch != '\v') {
581 printable = false;
582 goto end;
583 }
584 }
585
586end:
587 return printable;
588}
589
590BT_HIDDEN
591void bt_common_destroy_lttng_live_url_parts(
592 struct bt_common_lttng_live_url_parts *parts)
593{
594 if (!parts) {
595 goto end;
596 }
597
598 if (parts->proto) {
599 g_string_free(parts->proto, TRUE);
600 parts->proto = NULL;
601 }
602
603 if (parts->hostname) {
604 g_string_free(parts->hostname, TRUE);
605 parts->hostname = NULL;
606 }
607
608 if (parts->target_hostname) {
609 g_string_free(parts->target_hostname, TRUE);
610 parts->target_hostname = NULL;
611 }
612
613 if (parts->session_name) {
614 g_string_free(parts->session_name, TRUE);
615 parts->session_name = NULL;
616 }
617
618end:
619 return;
620}
621
622BT_HIDDEN
623struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
624 const char *url, char *error_buf, size_t error_buf_size)
625{
626 struct bt_common_lttng_live_url_parts parts;
627 const char *at = url;
628 size_t end_pos;
629
630 assert(url);
631 memset(&parts, 0, sizeof(parts));
632 parts.port = -1;
633
634 /* Protocol */
635 parts.proto = bt_common_string_until(at, "", ":", &end_pos);
636 if (!parts.proto || parts.proto->len == 0) {
637 if (error_buf) {
638 snprintf(error_buf, error_buf_size, "Missing protocol");
639 }
640
641 goto error;
642 }
643
644 if (strcmp(parts.proto->str, "net") == 0) {
645 g_string_assign(parts.proto, "net4");
646 }
647
648 if (strcmp(parts.proto->str, "net4") != 0 &&
649 strcmp(parts.proto->str, "net6") != 0) {
650 if (error_buf) {
651 snprintf(error_buf, error_buf_size,
652 "Unknown protocol: `%s`", parts.proto->str);
653 }
654
655 goto error;
656 }
657
658 if (at[end_pos] != ':') {
659 if (error_buf) {
660 snprintf(error_buf, error_buf_size,
661 "Expecting `:` after `%s`", parts.proto->str);
662 }
663
664 goto error;
665 }
666
667 at += end_pos;
668
669 /* :// */
670 if (strncmp(at, "://", 3) != 0) {
671 if (error_buf) {
672 snprintf(error_buf, error_buf_size,
673 "Expecting `://` after protocol");
674 }
675
676 goto error;
677 }
678
679 at += 3;
680
681 /* Hostname */
682 parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
683 if (!parts.hostname || parts.hostname->len == 0) {
684 if (error_buf) {
685 snprintf(error_buf, error_buf_size, "Missing hostname");
686 }
687
688 goto error;
689 }
690
691 if (at[end_pos] == ':') {
692 /* Port */
693 GString *port;
694
695 at += end_pos + 1;
696 port = bt_common_string_until(at, "", "/", &end_pos);
697 if (!port || port->len == 0) {
698 if (error_buf) {
699 snprintf(error_buf, error_buf_size, "Missing port");
700 }
701
702 goto error;
703 }
704
705 if (sscanf(port->str, "%d", &parts.port) != 1) {
706 if (error_buf) {
707 snprintf(error_buf, error_buf_size,
708 "Invalid port: `%s`", port->str);
709 }
710
711 g_string_free(port, TRUE);
712 goto error;
713 }
714
715 g_string_free(port, TRUE);
716
717 if (parts.port < 0 || parts.port >= 65536) {
718 if (error_buf) {
719 snprintf(error_buf, error_buf_size,
720 "Invalid port: %d", parts.port);
721 }
722
723 goto error;
724 }
725 }
726
727 if (at[end_pos] == '\0') {
728 goto end;
729 }
730
731 at += end_pos;
732
733 /* /host/ */
734 if (strncmp(at, "/host/", 6) != 0) {
735 if (error_buf) {
736 snprintf(error_buf, error_buf_size,
737 "Expecting `/host/` after hostname or port");
738 }
739
740 goto error;
741 }
742
743 at += 6;
744
745 /* Target hostname */
746 parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
747 if (!parts.target_hostname || parts.target_hostname->len == 0) {
748 if (error_buf) {
749 snprintf(error_buf, error_buf_size,
750 "Missing target hostname");
751 }
752
753 goto error;
754 }
755
756 if (at[end_pos] == '\0') {
757 goto end;
758 }
759
760 at += end_pos + 1;
761
762 /* Session name */
763 parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
764 if (!parts.session_name || parts.session_name->len == 0) {
765 if (error_buf) {
766 snprintf(error_buf, error_buf_size,
767 "Missing session name");
768 }
769
770 goto error;
771 }
772
773 if (at[end_pos] == '/') {
774 if (error_buf) {
775 snprintf(error_buf, error_buf_size,
776 "Unexpected `/` after session name (`%s`)",
777 parts.session_name->str);
778 }
779
780 goto error;
781 }
782
783 goto end;
784
785error:
786 bt_common_destroy_lttng_live_url_parts(&parts);
787
788end:
789 return parts;
790}
9009cc24
PP
791
792BT_HIDDEN
793void bt_common_normalize_star_glob_pattern(char *pattern)
794{
795 const char *p;
796 char *np;
797 bool got_star = false;
798
799 assert(pattern);
800
801 for (p = pattern, np = pattern; *p != '\0'; p++) {
802 switch (*p) {
803 case '*':
804 if (got_star) {
805 /* Avoid consecutive stars. */
806 continue;
807 }
808
809 got_star = true;
810 break;
811 case '\\':
812 /* Copy backslash character. */
813 *np = *p;
814 np++;
815 p++;
816
817 if (*p == '\0') {
818 goto end;
819 }
820
821 /* Fall through default case. */
822 default:
823 got_star = false;
824 break;
825 }
826
827 /* Copy single character. */
828 *np = *p;
829 np++;
830 }
831
832end:
833 *np = '\0';
834}
835
836static inline
837bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
838{
839 return (p - pattern) == pattern_len || *p == '\0';
840}
841
842/*
843 * Globbing matching function with the star feature only (`?` and
844 * character sets are not supported). This matches `candidate` (plain
845 * string) against `pattern`. A literal star can be escaped with `\` in
846 * `pattern`.
847 *
848 * `pattern_len` or `candidate_len` can be greater than the actual
849 * string length of `pattern` or `candidate` if the string is
850 * null-terminated.
851 */
852BT_HIDDEN
853bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
854 const char *candidate, size_t candidate_len) {
855 const char *retry_c = candidate, *retry_p = pattern, *c, *p;
856 bool got_a_star = false;
857
858retry:
859 c = retry_c;
860 p = retry_p;
861
862 /*
863 * The concept here is to retry a match in the specific case
864 * where we already got a star. The retry position for the
865 * pattern is just after the most recent star, and the retry
866 * position for the candidate is the character following the
867 * last try's first character.
868 *
869 * Example:
870 *
871 * candidate: hi ev every onyx one
872 * ^
873 * pattern: hi*every*one
874 * ^
875 *
876 * candidate: hi ev every onyx one
877 * ^
878 * pattern: hi*every*one
879 * ^
880 *
881 * candidate: hi ev every onyx one
882 * ^
883 * pattern: hi*every*one
884 * ^
885 *
886 * candidate: hi ev every onyx one
887 * ^
888 * pattern: hi*every*one
889 * ^ MISMATCH
890 *
891 * candidate: hi ev every onyx one
892 * ^
893 * pattern: hi*every*one
894 * ^
895 *
896 * candidate: hi ev every onyx one
897 * ^^
898 * pattern: hi*every*one
899 * ^^
900 *
901 * candidate: hi ev every onyx one
902 * ^ ^
903 * pattern: hi*every*one
904 * ^ ^ MISMATCH
905 *
906 * candidate: hi ev every onyx one
907 * ^
908 * pattern: hi*every*one
909 * ^ MISMATCH
910 *
911 * candidate: hi ev every onyx one
912 * ^
913 * pattern: hi*every*one
914 * ^ MISMATCH
915 *
916 * candidate: hi ev every onyx one
917 * ^
918 * pattern: hi*every*one
919 * ^
920 *
921 * candidate: hi ev every onyx one
922 * ^^
923 * pattern: hi*every*one
924 * ^^
925 *
926 * candidate: hi ev every onyx one
927 * ^ ^
928 * pattern: hi*every*one
929 * ^ ^
930 *
931 * candidate: hi ev every onyx one
932 * ^ ^
933 * pattern: hi*every*one
934 * ^ ^
935 *
936 * candidate: hi ev every onyx one
937 * ^ ^
938 * pattern: hi*every*one
939 * ^ ^
940 *
941 * candidate: hi ev every onyx one
942 * ^
943 * pattern: hi*every*one
944 * ^
945 *
946 * candidate: hi ev every onyx one
947 * ^
948 * pattern: hi*every*one
949 * ^ MISMATCH
950 *
951 * candidate: hi ev every onyx one
952 * ^
953 * pattern: hi*every*one
954 * ^
955 *
956 * candidate: hi ev every onyx one
957 * ^^
958 * pattern: hi*every*one
959 * ^^
960 *
961 * candidate: hi ev every onyx one
962 * ^ ^
963 * pattern: hi*every*one
964 * ^ ^ MISMATCH
965 *
966 * candidate: hi ev every onyx one
967 * ^
968 * pattern: hi*every*one
969 * ^ MISMATCH
970 *
971 * candidate: hi ev every onyx one
972 * ^
973 * pattern: hi*every*one
974 * ^ MISMATCH
975 *
976 * candidate: hi ev every onyx one
977 * ^
978 * pattern: hi*every*one
979 * ^ MISMATCH
980 *
981 * candidate: hi ev every onyx one
982 * ^
983 * pattern: hi*every*one
984 * ^ MISMATCH
985 *
986 * candidate: hi ev every onyx one
987 * ^
988 * pattern: hi*every*one
989 * ^
990 *
991 * candidate: hi ev every onyx one
992 * ^^
993 * pattern: hi*every*one
994 * ^^
995 *
996 * candidate: hi ev every onyx one
997 * ^ ^
998 * pattern: hi*every*one
999 * ^ ^
1000 *
1001 * candidate: hi ev every onyx one
1002 * ^ ^
1003 * pattern: hi*every*one
1004 * ^ ^ SUCCESS
1005 */
1006 while ((c - candidate) < candidate_len && *c != '\0') {
1007 assert(*c);
1008
1009 if (at_end_of_pattern(p, pattern, pattern_len)) {
1010 goto end_of_pattern;
1011 }
1012
1013 switch (*p) {
1014 case '*':
1015 got_a_star = true;
1016
1017 /*
1018 * Our first try starts at the current candidate
1019 * character and after the star in the pattern.
1020 */
1021 retry_c = c;
1022 retry_p = p + 1;
1023
1024 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
1025 /*
1026 * Star at the end of the pattern at
1027 * this point: automatic match.
1028 */
1029 return true;
1030 }
1031
1032 goto retry;
1033 case '\\':
1034 /* Go to escaped character. */
1035 p++;
1036
1037 /*
1038 * Fall through the default case which compares
1039 * the escaped character now.
1040 */
1041 default:
1042 if (at_end_of_pattern(p, pattern, pattern_len) ||
1043 *c != *p) {
1044end_of_pattern:
1045 /* Character mismatch OR end of pattern. */
1046 if (!got_a_star) {
1047 /*
1048 * We didn't get any star yet,
1049 * so this first mismatch
1050 * automatically makes the whole
1051 * test fail.
1052 */
1053 return false;
1054 }
1055
1056 /*
1057 * Next try: next candidate character,
1058 * original pattern character (following
1059 * the most recent star).
1060 */
1061 retry_c++;
1062 goto retry;
1063 }
1064 break;
1065 }
1066
1067 /* Next pattern and candidate characters. */
1068 c++;
1069 p++;
1070 }
1071
1072 /*
1073 * We checked every candidate character and we're still in a
1074 * success state: the only pattern character allowed to remain
1075 * is a star.
1076 */
1077 if (at_end_of_pattern(p, pattern, pattern_len)) {
1078 return true;
1079 }
1080
1081 p++;
1082 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
1083}
e49a18d1
PP
1084
1085static
1086void append_path_parts(const char *path, GPtrArray *parts)
1087{
1088 const char *ch = path;
1089 const char *last = path;
1090
1091 while (true) {
1092 if (*ch == G_DIR_SEPARATOR || *ch == '\0') {
1093 if (ch - last > 0) {
1094 GString *part = g_string_new(NULL);
1095
1096 assert(part);
1097 g_string_append_len(part, last, ch - last);
1098 g_ptr_array_add(parts, part);
1099 }
1100
1101 if (*ch == '\0') {
1102 break;
1103 }
1104
1105 last = ch + 1;
1106 }
1107
1108 ch++;
1109 }
1110}
1111
1112static
1113void destroy_gstring(void *gstring)
1114{
1115 (void) g_string_free(gstring, TRUE);
1116}
1117
1cd3decc
MJ
1118#ifdef __MINGW32__
1119BT_HIDDEN
1120GString *bt_common_normalize_path(const char *path, const char *wd)
1121{
1122 char *tmp;
1123 GString *norm_path = NULL;
1124
1125 assert(path);
1126
1127 tmp = _fullpath(NULL, path, PATH_MAX);
1128 if (!tmp) {
1129 goto error;
1130 }
1131
1132 norm_path = g_string_new(tmp);
1133 if (!norm_path) {
1134 goto error;
1135 }
1136
1137 goto end;
1138error:
1139 if (norm_path) {
1140 g_string_free(norm_path, TRUE);
1141 norm_path = NULL;
1142 }
1143end:
1144 if (tmp) {
1145 free(tmp);
1146 }
1147 return norm_path;
1148}
1149#else
e49a18d1
PP
1150BT_HIDDEN
1151GString *bt_common_normalize_path(const char *path, const char *wd)
1152{
1153 size_t i;
1154 GString *norm_path;
1155 GPtrArray *parts = NULL;
1156
1157 assert(path);
1158 norm_path = g_string_new(G_DIR_SEPARATOR_S);
1159 if (!norm_path) {
1160 goto error;
1161 }
1162
1163 parts = g_ptr_array_new_with_free_func(destroy_gstring);
1164 if (!parts) {
1165 goto error;
1166 }
1167
1168 if (path[0] != G_DIR_SEPARATOR) {
1169 /* Relative path: start with working directory */
1170 if (wd) {
1171 append_path_parts(wd, parts);
1172 } else {
1173 gchar *cd = g_get_current_dir();
1174
1175 append_path_parts(cd, parts);
1176 g_free(cd);
1177 }
1178 }
1179
1180 /* Append parts of the path parameter */
1181 append_path_parts(path, parts);
1182
1183 /* Resolve special `..` and `.` parts */
1184 for (i = 0; i < parts->len; i++) {
1185 GString *part = g_ptr_array_index(parts, i);
1186
1187 if (strcmp(part->str, "..") == 0) {
1188 if (i == 0) {
1189 /*
1190 * First part of absolute path is `..`:
1191 * this is invalid.
1192 */
1193 goto error;
1194 }
1195
1196 /* Remove `..` and previous part */
1197 g_ptr_array_remove_index(parts, i - 1);
1198 g_ptr_array_remove_index(parts, i - 1);
1199 i -= 2;
1200 } else if (strcmp(part->str, ".") == 0) {
1201 /* Remove `.` */
1202 g_ptr_array_remove_index(parts, i);
1203 i -= 1;
1204 }
1205 }
1206
1207 /* Create normalized path with what's left */
1208 for (i = 0; i < parts->len; i++) {
1209 GString *part = g_ptr_array_index(parts, i);
1210
1211 g_string_append(norm_path, part->str);
1212
1213 if (i < parts->len - 1) {
1214 g_string_append_c(norm_path, G_DIR_SEPARATOR);
1215 }
1216 }
1217
1218 goto end;
1219
1220error:
1221 if (norm_path) {
1222 g_string_free(norm_path, TRUE);
1223 norm_path = NULL;
1224 }
1225
1226end:
1227 if (parts) {
1228 g_ptr_array_free(parts, TRUE);
1229 }
1230
1231 return norm_path;
1232}
1cd3decc 1233#endif
108e5a1e
MJ
1234
1235BT_HIDDEN
1236size_t bt_common_get_page_size(void)
1237{
1238 int page_size;
1239
1240 page_size = bt_sysconf(_SC_PAGESIZE);
1241 if (page_size < 0) {
b4565e8b
PP
1242 BT_LOGF("Cannot get system's page size: ret=%d",
1243 page_size);
108e5a1e
MJ
1244 abort();
1245 }
1246
1247 return page_size;
1248}
This page took 0.078779 seconds and 4 git commands to generate.