END_TEST START_TEST(test_dm_match) { int i; char *candidate = "MyName is SAMMY @ and I am a SLUG!"; char *badpatterns[] = { "hello", "*hello", "*hello*", "hello*", "*hello", "*hello*", "hello*", "?", NULL }; char *goodpatterns[] = { "*", "*and*", "*SLUG!", "My*", "????My*", "MyName ?? *", "??Name*", "*SLUG?", NULL }; for (i = 0; badpatterns[i] != NULL; i++) { fail_unless(match_glob(badpatterns[i], candidate) == NULL, "test_dm_match failed on a bad pattern:" " [%s]", badpatterns[i]); } for (i = 0; goodpatterns[i] != NULL; i++) { fail_unless(match_glob(goodpatterns[i], candidate) == candidate, "test_dm_match failed on a good pattern:" " [%s]", goodpatterns[i]); } }
bool match_glob(const char* name, const char* pattern) { while (*pattern != '\0') { if (*pattern == '*') { if (pattern[1] == '\0') return true; const char *here = name; while (*name != '\0') ++name; while (name != here) { if (match_glob(name, pattern)) return true; --name; } } else { if (*name != *pattern) return false; ++name; } ++pattern; } // if we are here (*pattern == '\0') return *name == '\0'; }
/* * Match $str against an extended glob $pattern, like: * "alpha:be(t:l)a:g*a:d???a:ep(x(xx:yy)y:z*z)silon:sig(ma:)", * where parenthesis denote grouping and colons delimit alternatives. * * This implementation was found at least as fast as mainstream * library routines while being way smarter. The slightly less * flexible match_glob() "backend" beat them all, to death. * (These results are all for non-overcomplicated patterns * like "libg*2.0.*" which don't require too much backtracking.) * * match_glob(): 7.748s * match_eglob(): 18.083s * fnmatch(): 17.928s * g_pattern_match_string(): 16.391s * g_pattern_match_simple(): 1:22.820s */ static int match_eglob(char const *pattern, char const *str) { do if (match_glob(pattern, str)) return 1; while ((pattern = find_end_of_glob(pattern, ':')) != NULL); return 0; } /* match_eglob */
/* Like match_eglob(), but doesn't support top-level alterations. */ static int match_glob(char const *pattern, char const *str) { /* The very basic idea is due to BWK from the Beautiful Code. */ for (;;) { switch (*pattern) { case '(': /* Let match_eglob() sort it out. */ pattern++; return match_eglob(pattern, str); case ')': /* Like below. */ pattern++; break; case ':': /* An alternative matched, continue right after * the alteration. */ if ((pattern = find_end_of_glob(pattern, ')')) != NULL) break; case '\0': return !*str; case '*': /* Try to ignore more and more characters of $str * until we're out of them. */ pattern++; do if (match_glob(pattern, str)) return 1; while (*str++); return 0; case '?': if (!*str++) return 0; pattern++; break; default: if (*str++ != *pattern++) return 0; break; } /* switch */ } /* for */ } /* match_glob */
int pkg_info(struct pkg_info info) { unsigned int cur; int retval; struct pkg **pkgs; retval = 1; pkgs = NULL; /* -e package name */ if (info.check_package != NULL) { struct pkg *pkg; pkg = pkg_db_get_package(info.db, info.check_package); if (pkg != NULL) { pkg_free(pkg); return 0; } return 1; } /* -W <filename> */ if (info.search_file != NULL) { struct stat sb; if (stat(info.search_file, &sb) != 0) { /* XXX */ return 1; } pkgs = pkg_db_get_installed_match_count(info.db, pkg_match_by_file, 1, (const void *)info.search_file); if (info.quiet == 0) printf("The following installed package(s) has %s " "origin:\n", info.origin); printf("%s\n", pkg_get_name(pkgs[0])); return 0; } /* -O <origin> */ if (info.origin != NULL) { unsigned int pos; pkgs = pkg_db_get_installed_match(info.db, pkg_match_by_origin, (const void *)info.origin); if (info.quiet == 0) printf("The following installed package(s) has %s " "origin:\n", info.origin); for (pos = 0; pkgs[pos] != NULL; pos++) { printf("%s\n", pkg_get_name(pkgs[pos])); } return 0; } switch(info.match_type) { case MATCH_ALL: case MATCH_GLOB: case MATCH_NGLOB: case MATCH_REGEX: case MATCH_EREGEX: /* Display all packages installed */ if (info.match_type == MATCH_ALL) pkgs = pkg_db_get_installed(info.db); else if (info.match_type == MATCH_REGEX || info.match_type == MATCH_EREGEX) pkgs = match_regex(info.db, (const char**)info.pkgs, (info.match_type == MATCH_EREGEX)); else if (info.match_type == MATCH_GLOB || info.match_type == MATCH_NGLOB) pkgs = match_glob(info.db, (const char**)info.pkgs, (info.match_type == MATCH_GLOB)); else errx(1, "ERROR: Inconsistancy in pkg_info"); /* Sort the packages and display them */ if (pkgs == NULL) { /* XXX Error message */ return 1; } for (cur = 0; pkgs[cur] != NULL; cur++) continue; qsort(pkgs, cur, sizeof(struct pkg *), pkg_compare); for (cur = 0; pkgs[cur] != NULL; cur++) { show(info.db, pkgs[cur], info.flags, info.quiet, info.seperator, info.use_blocksize); } retval = 0; break; case MATCH_EXACT: /* Only match the exact names given */ retval = 0; for (cur = 0; info.pkgs[cur] != NULL; cur++) { struct pkg *pkg; pkg = pkg_db_get_package(info.db, info.pkgs[cur]); if (pkg != NULL) show(info.db, pkg, info.flags, info.quiet, info.seperator, info.use_blocksize); else { warnx("pkg_info: can't find package '%s' " "installed or in a file!", info.pkgs[cur]); retval = 1; } } break; } if (pkgs != NULL) pkg_list_free(pkgs); return retval; }
static gboolean test_match_glob(void) { gboolean ok = TRUE; struct { char *expr, *str; gboolean should_match; } tests[] = { /* literal, unanchored matching */ { "a", "a", TRUE }, { "abc", "abc", TRUE }, { "abc", "abcd", FALSE }, { "abc", "dabc", FALSE }, { "abc", "/usr/bin/abc", FALSE }, { "*.txt", "foo.txt", TRUE }, { "*.txt", ".txt", TRUE }, { "*.txt", "txt", FALSE }, { "?.txt", "X.txt", TRUE }, { "?.txt", ".txt", FALSE }, { "?.txt", "XY.txt", FALSE }, { "?*.txt", ".txt", FALSE }, { "?*.txt", "a.txt", TRUE }, { "?*.txt", "aa.txt", TRUE }, { "?*.txt", "aaa.txt", TRUE }, { "foo.[tT][xX][tT]", "foo.txt", TRUE }, { "foo.[tT][xX][tT]", "foo.TXt", TRUE }, { "foo.[tT][xX][tT]", "foo.TXT", TRUE }, { "foo.[tT][xX][tT]", "foo.TaT", FALSE }, { "foo.[tT][!yY][tT]", "foo.TXt", TRUE }, { "foo.[tT][!yY][tT]", "foo.TXT", TRUE }, { "foo.[tT][!yY][tT]", "foo.TyT", FALSE }, { "foo\\\\", "foo", FALSE }, { "foo\\\\", "foo\\", TRUE }, { "foo\\\\", "foo\\\\", FALSE }, { "(){}+.^$|", "(){}+.^$|", TRUE }, { "/usr/bin/*", "/usr/bin/tar", TRUE }, { "/usr/bin/*", "/usr/bin/local/tar", FALSE }, { "/usr/bin/*", "/usr/sbin/tar", FALSE }, { "/usr/bin/*", "/opt/usr/bin/tar", FALSE }, { "/usr?bin", "/usr/bin", FALSE }, { "/usr*bin", "/usr/bin", FALSE }, { NULL, NULL, FALSE }, }, *t; for (t = tests; t->expr; t++) { gboolean matched = match_glob(t->expr, t->str); if (!!matched != !!t->should_match) { ok = FALSE; if (t->should_match) { g_fprintf(stderr, "%s should have matched glob %s\n", t->str, t->expr); } else { g_fprintf(stderr, "%s unexpectedly matched glob %s\n", t->str, t->expr); } } } return ok; }