#include #include #include #include "prefix_match.h" int prefix_match(const char *value, const char **options, int flags) { flags &= ~PREFIXMATCH_MULTI_PARTIAL; // this doesn't make sense here bool case_sensitive = PREFIXMATCH_CASE_SENSITIVE == (flags & PREFIXMATCH_CASE_SENSITIVE); bool can_abbrev = 0 == (flags & PREFIXMATCH_NOABBREV); int (*cmpfn) (const char *, const char *) = case_sensitive ? strcmp : strcasecmp; int (*ncmpfn) (const char *, const char *, size_t) = case_sensitive ? strncmp : strncasecmp; if (!value || !options) return -1; size_t input_len = strlen(value); const char *option = NULL; int counter = 0; int result = -1; while (NULL != (option = options[counter])) { if (cmpfn(option, value) == 0) { return counter; // full exact match } else { // Test for partial match if (can_abbrev && ncmpfn(value, option, input_len) == 0) { if (result == -1) { result = counter; // first partial match } else { // ambiguous match return -1; } } } counter++; } return result; } size_t pm_word_len(const char *word, const char *delims) { char d = 0; const char *dp = delims; size_t word_len = 0; while ('\0' != (d = *dp++)) { char *end = strchr(word, d); if (NULL == end) continue; size_t len = end - word; if (!word_len || len < word_len) { word_len = len; } } if (!word_len) { word_len = strlen(word); } return word_len; } size_t pm_count_words(const char *sentence, const char *delims) { char c; size_t n = 0; bool in_word = false; if (!sentence || !delims) return 0; while (0 != (c = *sentence++)) { bool is_delim = NULL != strchr(delims, c); if (is_delim && in_word) { in_word = false; } else if (!in_word && !is_delim) { n++; in_word = true; } } return n; } int pm_multipart_test(const char *a, const char* b, const char *delims, int flags) { bool case_sensitive = PREFIXMATCH_CASE_SENSITIVE == (flags & PREFIXMATCH_CASE_SENSITIVE); bool can_abbrev = 0 == (flags & PREFIXMATCH_NOABBREV); int (*ncmpfn) (const char *, const char *, size_t) = case_sensitive ? strncmp : strncasecmp; // lazy shortcut first... if ((case_sensitive && 0 == strcmp(a, b)) || (!case_sensitive && 0 == strcasecmp(a, b))) { return 1; // full match } const char *word_a = a; const char *word_b = b; size_t word_a_len = 0; size_t word_b_len = 0; while (1) { word_a += word_a_len; word_b += word_b_len; // advance past leading delims, if any while (*word_a != '\0' && NULL != strchr(delims, *word_a)) word_a++; while (*word_b != '\0' && NULL != strchr(delims, *word_b)) word_b++; // test for terminator if (*word_a == '\0' && *word_b == '\0') { // both ended at the same number of words return 1; // full match } if (*word_a == '\0' || *word_b == '\0') { // sentences ended at different length if (0 != (flags & PREFIXMATCH_MULTI_PARTIAL) && *word_b != '\0') { // word prefix match (a is a prefix of b) return 2; // partial word match } else { return 0; // no match } } // find end of the words word_a_len = pm_word_len(word_a, delims); word_b_len = pm_word_len(word_b, delims); if (word_a_len > word_b_len || (!can_abbrev && word_a_len != word_b_len)) { return 0; // no match } int cmp = ncmpfn(word_a, word_b, word_a_len); if (0 != cmp) { // words differ return 0; // no match } } } int prefix_multipart_match(const char *value, const char **options, const char* delims, int flags) { bool multi_partial = 0 != (flags & PREFIXMATCH_MULTI_PARTIAL); flags &= ~PREFIXMATCH_MULTI_PARTIAL; // turn it off for passing the to test fn bool can_abbrev = 0 == (flags & PREFIXMATCH_NOABBREV); if (!value || !options) return -1; const char *option = NULL; int counter = 0; int result = -1; int result_partial = -1; int result_partial_nwords = 0; while (NULL != (option = options[counter])) { if (pm_multipart_test(value, option, delims, flags | PREFIXMATCH_NOABBREV)) { return counter; // full exact match } else if (can_abbrev) { // Test for partial match if (pm_multipart_test(value, option, delims, flags)) { if (result == -1) { result = counter; // first partial match in all words } else { return -1; } } else if (multi_partial && 2 == pm_multipart_test(value, option, delims, flags | PREFIXMATCH_MULTI_PARTIAL)) { int nwords = pm_count_words(option, delims); if (result_partial == -1 || result_partial_nwords < nwords) { result_partial = counter; // first partial match result_partial_nwords = nwords; } else { result_partial = -2; } } } counter++; } if (result != -1) { return result; } if (result_partial >= 0) { return result_partial; } return -1; }