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