| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 | /* * Compare project/version strings of the form: * * PROJECT v<num>.<num>...[ <flags>] * * Pattern is of the form: * * PROJECT[ v[<num>.<num>...][-[<num>.<num>...]] ][+-]flags... * * Chunks must be separated by exactly one space. Flags are A-Za-z0-9. * Any control character is treated as end of string. */#include "matchver.h"static int flag_val(char c){    if (c < '0')	return -1;    else if (c <= '9')	return c-'0';    else if (c < 'A')	return -1;    else if (c <= 'Z')	return c-'A'+10;    else if (c < 'a')	return -1;    else if (c <= 'z')	return c-'a'+36;    else	return -1;}static uint64_t flag_mask(char c){    int v = flag_val(c);    return (v < 0) ? 0 : UINT64_C(1) << v;}static inline bool is_digit(char c){    return (unsigned int)(c - '0') < 10;}/* * Compare numeric strings with components separated by dots, return the end * of each string. */static int compare_numbers(const char **ap, const char **bp,			   unsigned long bmissing){    bool adig, bdig;    unsigned long an, bn;    int result = 0;    const char *a = *ap, *b = *bp;    for (;;) {	adig = is_digit(*a);	bdig = is_digit(*b);	if (!adig && !bdig)	    break;	if (adig) {	    an = strtoul(a, (char **)&a, 10);	    if (*a == '.')		a++;	} else {	    an = 0;	}	if (bdig) {	    bn = strtoul(b, (char **)&b, 10);	    if (*b == '.')		b++;	} else {	    bn = bmissing;	}	/* If result is set, the answer is already known, just find the end */	if (!result) {	    result = (an < bn) ? -1 : (an > bn) ? 1 : 0;	}    }    *ap = a; *bp = b;    return result;}static const char *parse_flags(const char *str, uint64_t *flags, uint64_t *mask){    uint64_t polarity = -1;    uint64_t f = 0, m = 0;    while (1) {	char c = *str++;	uint64_t bit;	if (c == '+') {	    polarity = -1;	} else if (c == '-') {	    polarity = 0;	} else {	    bit = flag_mask(c);	    if (!bit)		break;	    m |= bit;	    f = (f & ~bit) | (polarity & bit);	}    }    *flags = f; *mask = m;    return str;}bool match_version(const char *version, const char *pattern){    char v, p;    const char *vstart, *pstart;    uint64_t vflags, pflags, pmask;    while (1) {	v = *version++;	p = *pattern++;	if (v <= ' ' && p <= ' ')	    break;	if (v != p)	    return false;    }    if (p < ' ')	return true;		/* Project-only pattern */    if (v != ' ' || *version++ != 'v')	return false;		/* Invalid/unparsable version string */    if (*pattern++ != 'v')	return false;		/* Invalid pattern */    vstart = version;    pstart = pattern;    if (compare_numbers(&version, &pattern, 0UL) < 0)	return false;    if (*pattern == '-')	pattern++;    else	pattern = pstart;    if (compare_numbers(&vstart, &pattern, -1UL) > 0)	return false;    v = *version++;    vflags = 0;    if (v == ' ') {	uint64_t dummy;	parse_flags(version, &vflags, &dummy);    } else if (v > ' ') {	return false;    }    p = *pattern++;    pflags = pmask = 0;    if (p == ' ') {	parse_flags(pattern, &pflags, &pmask);    } else if (p > ' ') {	return false;    }    return (vflags & pmask) == pflags;}#ifdef COMMAND#include <stdio.h>int main(int argc, char *argv[]){    if (argc != 3) {	fprintf(stderr, "Usage: %s version pattern\n", argv[0]);	return 127;    }    return !match_version(argv[1], argv[2]);}#endif
 |