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