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