diff --git a/src/util/stringops.c b/src/util/stringops.c index 03e28cb0..d7896677 100644 --- a/src/util/stringops.c +++ b/src/util/stringops.c @@ -452,3 +452,80 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_ } } #endif + +bool strnmatch(size_t globsize, const char glob[globsize], size_t insize, const char input[insize]) { + const char *ip = input; + const char *iend = input + insize; + const char *gp = glob; + const char *gend = glob + globsize; + + int isfirst = 1; + int islast = 0; + + for(;;) { + assert(!islast); + + if(gp > gend) { + break; + } + + const char *segment_start = gp; + const char *segment_end = memchr(segment_start, '*', gend - segment_start); + + if(!segment_end) { + segment_end = gend; + islast = 1; + } + + size_t segment_len = segment_end - segment_start; + + if(segment_len < 1) { + if(islast) { + // Pattern ends with a * and we matched everything up to that point. + return 1; + } + } else { + // If this is the first segment, match at the start of input. + // Else search input for the first occurrence of the segment (lazy match). + + if(isfirst) { + if(iend - ip < segment_len) { + // input shorter than pattern; can't possibly match + return 0; + } + + if(memcmp(ip, segment_start, segment_len)) { + return 0; + } + } else if(!(ip = memmem(ip, iend - ip, segment_start, segment_len))) { + return 0; + } + + ip += segment_len; + } + + isfirst = 0; + gp += segment_len + 1; + + if(islast) { + if(iend - ip == 0) { + // perfect match + return 1; + } + + // Try to match the last pattern "greedily" + // So that e.g. a pattern like "start_*_end" can match "start_123_end_123_end" + if(iend - ip >= segment_len && !memcmp(iend - segment_len, segment_start, segment_len)) { + return 1; + } + + return 0; + } + } + + return 1; +} + +bool strmatch(const char *glob, const char *input) { + return strnmatch(strlen(glob), glob, strlen(input), input); +} diff --git a/src/util/stringops.h b/src/util/stringops.h index a253a9ee..a366009e 100644 --- a/src/util/stringops.h +++ b/src/util/stringops.h @@ -82,3 +82,14 @@ void hexdigest(uint8_t *input, size_t input_size, char *output, size_t output_si #define FILENAME_TIMESTAMP_MIN_BUF_SIZE 23 size_t filename_timestamp(char *buf, size_t buf_size, const SystemTime time) attr_nonnull(1); + +/* + * Test if string matches a simple 'glob'-like pattern. + * + * Only '*' is supported as a metacharacter in the glob. + * '*' matches zero or more characters lazily. + * If the glob is not empty and does not end with a *, the last segment is matched greedily. + * An empty glob matches everything. + */ +bool strnmatch(size_t globsize, const char glob[globsize], size_t insize, const char input[insize]); +bool strmatch(const char *glob, const char *input);