Fix CLI to work with multiple ports and dynamically added ports
[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
PP
32#include <glib.h>
33#include <babeltrace/babeltrace-internal.h>
ad96d936 34#include <babeltrace/common-internal.h>
1670bffd
PP
35
36#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace/plugins"
37#define HOME_ENV_VAR "HOME"
38#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace/plugins"
39
40BT_HIDDEN
41const char *bt_common_get_system_plugin_path(void)
42{
43 return SYSTEM_PLUGIN_PATH;
44}
45
46BT_HIDDEN
47bool bt_common_is_setuid_setgid(void)
48{
49 return (geteuid() != getuid() || getegid() != getgid());
50}
51
52static char *bt_secure_getenv(const char *name)
53{
54 if (bt_common_is_setuid_setgid()) {
55 printf_error("Disregarding %s environment variable for setuid/setgid binary",
56 name);
57 return NULL;
58 }
59 return getenv(name);
60}
61
62static const char *get_home_dir(void)
63{
64 char *val = NULL;
65 struct passwd *pwd;
66
67 val = bt_secure_getenv(HOME_ENV_VAR);
68 if (val) {
69 goto end;
70 }
71 /* Fallback on password file. */
72 pwd = getpwuid(getuid());
73 if (!pwd) {
74 goto end;
75 }
76 val = pwd->pw_dir;
77end:
78 return val;
79}
80
81BT_HIDDEN
82char *bt_common_get_home_plugin_path(void)
83{
84 char *path = NULL;
85 const char *home_dir;
86
87 home_dir = get_home_dir();
88 if (!home_dir) {
89 goto end;
90 }
91
92 if (strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1 >= PATH_MAX) {
93 printf_error("Home directory path is too long: `%s`\n",
94 home_dir);
95 goto end;
96 }
97
98 path = malloc(PATH_MAX);
99 if (!path) {
100 goto end;
101 }
102
103 strcpy(path, home_dir);
104 strcat(path, HOME_PLUGIN_SUBPATH);
105
106end:
107 return path;
108}
109
110BT_HIDDEN
111int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
112{
113 int ret = 0;
114 const char *at;
115 const char *end;
116 size_t init_dirs_len;
117
118 assert(dirs);
119 init_dirs_len = dirs->len;
120
121 if (!paths) {
122 /* Nothing to append */
123 goto end;
124 }
125
126 at = paths;
127 end = paths + strlen(paths);
128
129 while (at < end) {
130 GString *path;
131 const char *next_colon;
132
133 next_colon = strchr(at, ':');
134 if (next_colon == at) {
135 /*
136 * Empty path: try next character (supported
137 * to conform to the typical parsing of $PATH).
138 */
139 at++;
140 continue;
141 } else if (!next_colon) {
142 /* No more colon: use the remaining */
143 next_colon = paths + strlen(paths);
144 }
145
146 path = g_string_new(NULL);
147 if (!path) {
148 goto error;
149 }
150
151 g_string_append_len(path, at, next_colon - at);
152 at = next_colon + 1;
153 g_ptr_array_add(dirs, path);
154 }
155
156 goto end;
157
158error:
159 ret = -1;
160
161 /* Remove the new entries in dirs */
162 while (dirs->len > init_dirs_len) {
163 g_ptr_array_remove_index(dirs, init_dirs_len);
164 }
165
166end:
167 return ret;
168}
290725f7 169
ad96d936
PP
170BT_HIDDEN
171bool bt_common_colors_supported(void)
290725f7
PP
172{
173 static bool supports_colors = false;
174 static bool supports_colors_set = false;
175 const char *term;
176
177 if (supports_colors_set) {
178 goto end;
179 }
180
181 supports_colors_set = true;
182
183 term = getenv("TERM");
184 if (!term) {
185 goto end;
186 }
187
188 if (strncmp(term, "xterm", 5) != 0 &&
189 strncmp(term, "rxvt", 4) != 0 &&
190 strncmp(term, "konsole", 7) != 0 &&
69c72672
PP
191 strncmp(term, "gnome", 5) != 0 &&
192 strncmp(term, "screen", 5) != 0 &&
193 strncmp(term, "tmux", 4) != 0 &&
194 strncmp(term, "putty", 5) != 0) {
290725f7
PP
195 goto end;
196 }
197
198 if (!isatty(1)) {
199 goto end;
200 }
201
202 supports_colors = true;
203
204end:
205 return supports_colors;
206}
207
208BT_HIDDEN
209const char *bt_common_color_reset(void)
210{
ad96d936 211 return bt_common_colors_supported() ? BT_COMMON_COLOR_RESET : "";
290725f7
PP
212}
213
214BT_HIDDEN
215const char *bt_common_color_bold(void)
216{
ad96d936 217 return bt_common_colors_supported() ? BT_COMMON_COLOR_BOLD : "";
290725f7
PP
218}
219
220BT_HIDDEN
221const char *bt_common_color_fg_default(void)
222{
ad96d936 223 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_DEFAULT : "";
290725f7
PP
224}
225
226BT_HIDDEN
227const char *bt_common_color_fg_red(void)
228{
ad96d936 229 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_RED : "";
290725f7
PP
230}
231
232BT_HIDDEN
233const char *bt_common_color_fg_green(void)
234{
ad96d936 235 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_GREEN : "";
290725f7
PP
236}
237
238BT_HIDDEN
239const char *bt_common_color_fg_yellow(void)
240{
ad96d936 241 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_YELLOW : "";
290725f7
PP
242}
243
244BT_HIDDEN
245const char *bt_common_color_fg_blue(void)
246{
ad96d936 247 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_BLUE : "";
290725f7
PP
248}
249
250BT_HIDDEN
251const char *bt_common_color_fg_magenta(void)
252{
ad96d936 253 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_MAGENTA : "";
290725f7
PP
254}
255
256BT_HIDDEN
257const char *bt_common_color_fg_cyan(void)
258{
ad96d936 259 return bt_common_colors_supported() ? BT_COMMON_COLOR_FG_CYAN : "";
290725f7
PP
260}
261
262BT_HIDDEN
263const char *bt_common_color_fg_light_gray(void)
264{
ad96d936
PP
265 return bt_common_colors_supported() ?
266 BT_COMMON_COLOR_FG_LIGHT_GRAY : "";
290725f7
PP
267}
268
269BT_HIDDEN
270const char *bt_common_color_bg_default(void)
271{
ad96d936 272 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_DEFAULT : "";
290725f7
PP
273}
274
275BT_HIDDEN
276const char *bt_common_color_bg_red(void)
277{
ad96d936 278 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_RED : "";
290725f7
PP
279}
280
281BT_HIDDEN
282const char *bt_common_color_bg_green(void)
283{
ad96d936 284 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_GREEN : "";
290725f7
PP
285}
286
287BT_HIDDEN
288const char *bt_common_color_bg_yellow(void)
289{
ad96d936 290 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_YELLOW : "";
290725f7
PP
291}
292
293BT_HIDDEN
294const char *bt_common_color_bg_blue(void)
295{
ad96d936 296 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_BLUE : "";
290725f7
PP
297}
298
299BT_HIDDEN
300const char *bt_common_color_bg_magenta(void)
301{
ad96d936 302 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_MAGENTA : "";
290725f7
PP
303}
304
305BT_HIDDEN
306const char *bt_common_color_bg_cyan(void)
307{
ad96d936 308 return bt_common_colors_supported() ? BT_COMMON_COLOR_BG_CYAN : "";
290725f7
PP
309}
310
311BT_HIDDEN
312const char *bt_common_color_bg_light_gray(void)
313{
ad96d936
PP
314 return bt_common_colors_supported() ?
315 BT_COMMON_COLOR_BG_LIGHT_GRAY : "";
290725f7 316}
db0f160a
PP
317
318BT_HIDDEN
319GString *bt_common_string_until(const char *input, const char *escapable_chars,
320 const char *end_chars, size_t *end_pos)
321{
322 GString *output = g_string_new(NULL);
323 const char *ch;
324 const char *es_char;
325 const char *end_char;
326
327 if (!output) {
328 goto error;
329 }
330
331 for (ch = input; *ch != '\0'; ch++) {
332 if (*ch == '\\') {
333 bool continue_loop = false;
334
335 if (ch[1] == '\0') {
336 /* `\` at the end of the string: append `\` */
337 g_string_append_c(output, *ch);
338 ch++;
339 goto set_end_pos;
340 }
341
342 for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
343 if (ch[1] == *es_char) {
344 /*
345 * `\` followed by an escapable
346 * character: append the escaped
347 * character only.
348 */
349 g_string_append_c(output, ch[1]);
350 ch++;
351 continue_loop = true;
352 break;
353 }
354 }
355
356 if (continue_loop) {
357 continue;
358 }
359
360 /*
361 * `\` followed by a non-escapable character:
362 * append `\` and the character.
363 */
364 g_string_append_c(output, *ch);
365 g_string_append_c(output, ch[1]);
366 ch++;
367 continue;
368 } else {
369 for (end_char = end_chars; *end_char != '\0'; end_char++) {
370 if (*ch == *end_char) {
371 /*
372 * End character found:
373 * terminate this loop.
374 */
375 goto set_end_pos;
376 }
377 }
378
379 /* Normal character: append */
380 g_string_append_c(output, *ch);
381 }
382 }
383
384set_end_pos:
385 if (end_pos) {
386 *end_pos = ch - input;
387 }
388
389 goto end;
390
391error:
392 if (output) {
393 g_string_free(output, TRUE);
394 }
395
396end:
397 return output;
398}
399
400BT_HIDDEN
36b405c6 401GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
db0f160a
PP
402{
403 GString *output = g_string_new(NULL);
404 const char *ch;
405 bool no_quote = true;
406
407 if (!output) {
408 goto end;
409 }
410
411 if (strlen(input) == 0) {
36b405c6
PP
412 if (with_single_quotes) {
413 g_string_assign(output, "''");
414 }
415
db0f160a
PP
416 goto end;
417 }
418
419 for (ch = input; *ch != '\0'; ch++) {
420 const char c = *ch;
421
422 if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
423 c != '@' && c != '%' && c != '+' && c != '=' &&
424 c != ':' && c != ',' && c != '.' && c != '/' &&
425 c != '-') {
426 no_quote = false;
427 break;
428 }
429 }
430
431 if (no_quote) {
432 g_string_assign(output, input);
433 goto end;
434 }
435
36b405c6
PP
436 if (with_single_quotes) {
437 g_string_assign(output, "'");
438 }
db0f160a
PP
439
440 for (ch = input; *ch != '\0'; ch++) {
441 if (*ch == '\'') {
442 g_string_append(output, "'\"'\"'");
443 } else {
444 g_string_append_c(output, *ch);
445 }
446 }
447
36b405c6
PP
448 if (with_single_quotes) {
449 g_string_append_c(output, '\'');
450 }
db0f160a
PP
451
452end:
453 return output;
454}
455
456BT_HIDDEN
457bool bt_common_string_is_printable(const char *input)
458{
459 const char *ch;
460 bool printable = true;
461 assert(input);
462
463 for (ch = input; *ch != '\0'; ch++) {
464 if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
465 *ch != '\t' && *ch != '\v') {
466 printable = false;
467 goto end;
468 }
469 }
470
471end:
472 return printable;
473}
474
475BT_HIDDEN
476void bt_common_destroy_lttng_live_url_parts(
477 struct bt_common_lttng_live_url_parts *parts)
478{
479 if (!parts) {
480 goto end;
481 }
482
483 if (parts->proto) {
484 g_string_free(parts->proto, TRUE);
485 parts->proto = NULL;
486 }
487
488 if (parts->hostname) {
489 g_string_free(parts->hostname, TRUE);
490 parts->hostname = NULL;
491 }
492
493 if (parts->target_hostname) {
494 g_string_free(parts->target_hostname, TRUE);
495 parts->target_hostname = NULL;
496 }
497
498 if (parts->session_name) {
499 g_string_free(parts->session_name, TRUE);
500 parts->session_name = NULL;
501 }
502
503end:
504 return;
505}
506
507BT_HIDDEN
508struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
509 const char *url, char *error_buf, size_t error_buf_size)
510{
511 struct bt_common_lttng_live_url_parts parts;
512 const char *at = url;
513 size_t end_pos;
514
515 assert(url);
516 memset(&parts, 0, sizeof(parts));
517 parts.port = -1;
518
519 /* Protocol */
520 parts.proto = bt_common_string_until(at, "", ":", &end_pos);
521 if (!parts.proto || parts.proto->len == 0) {
522 if (error_buf) {
523 snprintf(error_buf, error_buf_size, "Missing protocol");
524 }
525
526 goto error;
527 }
528
529 if (strcmp(parts.proto->str, "net") == 0) {
530 g_string_assign(parts.proto, "net4");
531 }
532
533 if (strcmp(parts.proto->str, "net4") != 0 &&
534 strcmp(parts.proto->str, "net6") != 0) {
535 if (error_buf) {
536 snprintf(error_buf, error_buf_size,
537 "Unknown protocol: `%s`", parts.proto->str);
538 }
539
540 goto error;
541 }
542
543 if (at[end_pos] != ':') {
544 if (error_buf) {
545 snprintf(error_buf, error_buf_size,
546 "Expecting `:` after `%s`", parts.proto->str);
547 }
548
549 goto error;
550 }
551
552 at += end_pos;
553
554 /* :// */
555 if (strncmp(at, "://", 3) != 0) {
556 if (error_buf) {
557 snprintf(error_buf, error_buf_size,
558 "Expecting `://` after protocol");
559 }
560
561 goto error;
562 }
563
564 at += 3;
565
566 /* Hostname */
567 parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
568 if (!parts.hostname || parts.hostname->len == 0) {
569 if (error_buf) {
570 snprintf(error_buf, error_buf_size, "Missing hostname");
571 }
572
573 goto error;
574 }
575
576 if (at[end_pos] == ':') {
577 /* Port */
578 GString *port;
579
580 at += end_pos + 1;
581 port = bt_common_string_until(at, "", "/", &end_pos);
582 if (!port || port->len == 0) {
583 if (error_buf) {
584 snprintf(error_buf, error_buf_size, "Missing port");
585 }
586
587 goto error;
588 }
589
590 if (sscanf(port->str, "%d", &parts.port) != 1) {
591 if (error_buf) {
592 snprintf(error_buf, error_buf_size,
593 "Invalid port: `%s`", port->str);
594 }
595
596 g_string_free(port, TRUE);
597 goto error;
598 }
599
600 g_string_free(port, TRUE);
601
602 if (parts.port < 0 || parts.port >= 65536) {
603 if (error_buf) {
604 snprintf(error_buf, error_buf_size,
605 "Invalid port: %d", parts.port);
606 }
607
608 goto error;
609 }
610 }
611
612 if (at[end_pos] == '\0') {
613 goto end;
614 }
615
616 at += end_pos;
617
618 /* /host/ */
619 if (strncmp(at, "/host/", 6) != 0) {
620 if (error_buf) {
621 snprintf(error_buf, error_buf_size,
622 "Expecting `/host/` after hostname or port");
623 }
624
625 goto error;
626 }
627
628 at += 6;
629
630 /* Target hostname */
631 parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
632 if (!parts.target_hostname || parts.target_hostname->len == 0) {
633 if (error_buf) {
634 snprintf(error_buf, error_buf_size,
635 "Missing target hostname");
636 }
637
638 goto error;
639 }
640
641 if (at[end_pos] == '\0') {
642 goto end;
643 }
644
645 at += end_pos + 1;
646
647 /* Session name */
648 parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
649 if (!parts.session_name || parts.session_name->len == 0) {
650 if (error_buf) {
651 snprintf(error_buf, error_buf_size,
652 "Missing session name");
653 }
654
655 goto error;
656 }
657
658 if (at[end_pos] == '/') {
659 if (error_buf) {
660 snprintf(error_buf, error_buf_size,
661 "Unexpected `/` after session name (`%s`)",
662 parts.session_name->str);
663 }
664
665 goto error;
666 }
667
668 goto end;
669
670error:
671 bt_common_destroy_lttng_live_url_parts(&parts);
672
673end:
674 return parts;
675}
9009cc24
PP
676
677BT_HIDDEN
678void bt_common_normalize_star_glob_pattern(char *pattern)
679{
680 const char *p;
681 char *np;
682 bool got_star = false;
683
684 assert(pattern);
685
686 for (p = pattern, np = pattern; *p != '\0'; p++) {
687 switch (*p) {
688 case '*':
689 if (got_star) {
690 /* Avoid consecutive stars. */
691 continue;
692 }
693
694 got_star = true;
695 break;
696 case '\\':
697 /* Copy backslash character. */
698 *np = *p;
699 np++;
700 p++;
701
702 if (*p == '\0') {
703 goto end;
704 }
705
706 /* Fall through default case. */
707 default:
708 got_star = false;
709 break;
710 }
711
712 /* Copy single character. */
713 *np = *p;
714 np++;
715 }
716
717end:
718 *np = '\0';
719}
720
721static inline
722bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
723{
724 return (p - pattern) == pattern_len || *p == '\0';
725}
726
727/*
728 * Globbing matching function with the star feature only (`?` and
729 * character sets are not supported). This matches `candidate` (plain
730 * string) against `pattern`. A literal star can be escaped with `\` in
731 * `pattern`.
732 *
733 * `pattern_len` or `candidate_len` can be greater than the actual
734 * string length of `pattern` or `candidate` if the string is
735 * null-terminated.
736 */
737BT_HIDDEN
738bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
739 const char *candidate, size_t candidate_len) {
740 const char *retry_c = candidate, *retry_p = pattern, *c, *p;
741 bool got_a_star = false;
742
743retry:
744 c = retry_c;
745 p = retry_p;
746
747 /*
748 * The concept here is to retry a match in the specific case
749 * where we already got a star. The retry position for the
750 * pattern is just after the most recent star, and the retry
751 * position for the candidate is the character following the
752 * last try's first character.
753 *
754 * Example:
755 *
756 * candidate: hi ev every onyx one
757 * ^
758 * pattern: hi*every*one
759 * ^
760 *
761 * candidate: hi ev every onyx one
762 * ^
763 * pattern: hi*every*one
764 * ^
765 *
766 * candidate: hi ev every onyx one
767 * ^
768 * pattern: hi*every*one
769 * ^
770 *
771 * candidate: hi ev every onyx one
772 * ^
773 * pattern: hi*every*one
774 * ^ MISMATCH
775 *
776 * candidate: hi ev every onyx one
777 * ^
778 * pattern: hi*every*one
779 * ^
780 *
781 * candidate: hi ev every onyx one
782 * ^^
783 * pattern: hi*every*one
784 * ^^
785 *
786 * candidate: hi ev every onyx one
787 * ^ ^
788 * pattern: hi*every*one
789 * ^ ^ MISMATCH
790 *
791 * candidate: hi ev every onyx one
792 * ^
793 * pattern: hi*every*one
794 * ^ MISMATCH
795 *
796 * candidate: hi ev every onyx one
797 * ^
798 * pattern: hi*every*one
799 * ^ MISMATCH
800 *
801 * candidate: hi ev every onyx one
802 * ^
803 * pattern: hi*every*one
804 * ^
805 *
806 * candidate: hi ev every onyx one
807 * ^^
808 * pattern: hi*every*one
809 * ^^
810 *
811 * candidate: hi ev every onyx one
812 * ^ ^
813 * pattern: hi*every*one
814 * ^ ^
815 *
816 * candidate: hi ev every onyx one
817 * ^ ^
818 * pattern: hi*every*one
819 * ^ ^
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 * ^ MISMATCH
835 *
836 * candidate: hi ev every onyx one
837 * ^
838 * pattern: hi*every*one
839 * ^
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 * ^ ^ MISMATCH
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 * ^ MISMATCH
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 * ^ ^ SUCCESS
890 */
891 while ((c - candidate) < candidate_len && *c != '\0') {
892 assert(*c);
893
894 if (at_end_of_pattern(p, pattern, pattern_len)) {
895 goto end_of_pattern;
896 }
897
898 switch (*p) {
899 case '*':
900 got_a_star = true;
901
902 /*
903 * Our first try starts at the current candidate
904 * character and after the star in the pattern.
905 */
906 retry_c = c;
907 retry_p = p + 1;
908
909 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
910 /*
911 * Star at the end of the pattern at
912 * this point: automatic match.
913 */
914 return true;
915 }
916
917 goto retry;
918 case '\\':
919 /* Go to escaped character. */
920 p++;
921
922 /*
923 * Fall through the default case which compares
924 * the escaped character now.
925 */
926 default:
927 if (at_end_of_pattern(p, pattern, pattern_len) ||
928 *c != *p) {
929end_of_pattern:
930 /* Character mismatch OR end of pattern. */
931 if (!got_a_star) {
932 /*
933 * We didn't get any star yet,
934 * so this first mismatch
935 * automatically makes the whole
936 * test fail.
937 */
938 return false;
939 }
940
941 /*
942 * Next try: next candidate character,
943 * original pattern character (following
944 * the most recent star).
945 */
946 retry_c++;
947 goto retry;
948 }
949 break;
950 }
951
952 /* Next pattern and candidate characters. */
953 c++;
954 p++;
955 }
956
957 /*
958 * We checked every candidate character and we're still in a
959 * success state: the only pattern character allowed to remain
960 * is a star.
961 */
962 if (at_end_of_pattern(p, pattern, pattern_len)) {
963 return true;
964 }
965
966 p++;
967 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
968}
This page took 0.06094 seconds and 4 git commands to generate.