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
|