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