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