Air quality sensor
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
esp-airsensor/components/vconsole/libconsole/src/console_prefix_match.c

196 lines
6.4 KiB

#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include "console/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 * restrict word, const char * restrict delims) {
char d;
const char *dp = delims;
size_t word_len = 0;
if (!word || !delims) return 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 * restrict sentence, const char * restrict 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;
}
const char *pm_skip_words(const char * restrict sentence, const char * restrict delims, size_t skip) {
char c;
size_t n = 0;
bool in_word = false;
if (!sentence || !delims) return NULL;
while (0 != (c = *sentence++)) {
bool is_delim = NULL != strchr(delims, c);
if (is_delim && in_word) {
in_word = false;
skip--;
if (skip == 0) {
return sentence - 1;
}
} else if (!in_word && !is_delim) {
n++;
in_word = true;
}
}
return sentence - 1;
}
enum pm_test_result prefix_multipart_test(
const char * restrict tested,
const char* restrict full,
const char * restrict 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(tested, full)) || (!case_sensitive && 0 == strcasecmp(tested, full))) {
return PM_TEST_MATCH; // full match
}
const char *word_t = tested;
const char *word_f = full;
size_t word_t_len = 0;
size_t word_f_len = 0;
while (1) {
word_t += word_t_len;
word_f += word_f_len;
// advance past leading delims, if any
while (*word_t != '\0' && NULL != strchr(delims, *word_t)) word_t++;
while (*word_f != '\0' && NULL != strchr(delims, *word_f)) word_f++;
// test for terminator
if (*word_t == '\0' && *word_f == '\0') {
// both ended at the same number of words
return PM_TEST_MATCH; // full match
}
if (*word_t == '\0' || *word_f == '\0') {
// sentences ended at different length
if (0 != (flags & PREFIXMATCH_MULTI_PARTIAL) && *word_f != '\0') { // word prefix match (a is a prefix of b)
return PM_TEST_MATCH_MULTI_PARTIAL;
} else {
return PM_TEST_NO_MATCH;
}
}
// find end of the words
word_t_len = pm_word_len(word_t, delims);
word_f_len = pm_word_len(word_f, delims);
if (word_t_len > word_f_len || (!can_abbrev && word_t_len != word_f_len)) {
return PM_TEST_NO_MATCH;
}
int cmp = ncmpfn(word_t, word_f, word_t_len);
if (0 != cmp) { // words differ
return PM_TEST_NO_MATCH;
}
}
}
int prefix_multipart_match(const char * restrict value, const char **options, const char * restrict 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; // -1=none yet, -2=ambiguous multi-word partial, >=0=index
int result_partial_nwords = 0;
while (NULL != (option = options[counter])) {
if (PM_TEST_MATCH == prefix_multipart_test(value, option, delims, flags | PREFIXMATCH_NOABBREV)) {
return counter; // full exact match
} else if (can_abbrev) {
// Test for partial match
if (PM_TEST_MATCH == prefix_multipart_test(value, option, delims, flags)) {
if (result == -1) {
result = counter; // first partial match in all words
} else {
return -1;
}
} else if (multi_partial && PM_TEST_MATCH_MULTI_PARTIAL == prefix_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;
}