2
0

matchver.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * Compare project/version strings of the form:
  3. *
  4. * PROJECT v<num>.<num>...[ <flags>]
  5. *
  6. * Pattern is of the form:
  7. *
  8. * PROJECT[ v[<num>.<num>...][-[<num>.<num>...]] ][+-]flags...
  9. *
  10. * Chunks must be separated by exactly one space. Flags are A-Za-z0-9.
  11. * Any control character is treated as end of string.
  12. */
  13. #include "matchver.h"
  14. static int flag_val(char c)
  15. {
  16. if (c < '0')
  17. return -1;
  18. else if (c <= '9')
  19. return c-'0';
  20. else if (c < 'A')
  21. return -1;
  22. else if (c <= 'Z')
  23. return c-'A'+10;
  24. else if (c < 'a')
  25. return -1;
  26. else if (c <= 'z')
  27. return c-'a'+36;
  28. else
  29. return -1;
  30. }
  31. static uint64_t flag_mask(char c)
  32. {
  33. int v = flag_val(c);
  34. return (v < 0) ? 0 : UINT64_C(1) << v;
  35. }
  36. static inline bool is_digit(char c)
  37. {
  38. return (unsigned int)(c - '0') < 10;
  39. }
  40. /*
  41. * Compare numeric strings with components separated by dots, return the end
  42. * of each string.
  43. */
  44. static int compare_numbers(const char **ap, const char **bp,
  45. unsigned long bmissing)
  46. {
  47. bool adig, bdig;
  48. unsigned long an, bn;
  49. int result = 0;
  50. const char *a = *ap, *b = *bp;
  51. for (;;) {
  52. adig = is_digit(*a);
  53. bdig = is_digit(*b);
  54. if (!adig && !bdig)
  55. break;
  56. if (adig) {
  57. an = strtoul(a, (char **)&a, 10);
  58. if (*a == '.')
  59. a++;
  60. } else {
  61. an = 0;
  62. }
  63. if (bdig) {
  64. bn = strtoul(b, (char **)&b, 10);
  65. if (*b == '.')
  66. b++;
  67. } else {
  68. bn = bmissing;
  69. }
  70. /* If result is set, the answer is already known, just find the end */
  71. if (!result) {
  72. result = (an < bn) ? -1 : (an > bn) ? 1 : 0;
  73. }
  74. }
  75. *ap = a; *bp = b;
  76. return result;
  77. }
  78. static const char *parse_flags(const char *str, uint64_t *flags, uint64_t *mask)
  79. {
  80. uint64_t polarity = -1;
  81. uint64_t f = 0, m = 0;
  82. while (1) {
  83. char c = *str++;
  84. uint64_t bit;
  85. if (c == '+') {
  86. polarity = -1;
  87. } else if (c == '-') {
  88. polarity = 0;
  89. } else {
  90. bit = flag_mask(c);
  91. if (!bit)
  92. break;
  93. m |= bit;
  94. f = (f & ~bit) | (polarity & bit);
  95. }
  96. }
  97. *flags = f; *mask = m;
  98. return str;
  99. }
  100. bool match_version(const char *version, const char *pattern)
  101. {
  102. char v, p;
  103. const char *vstart, *pstart;
  104. uint64_t vflags, pflags, pmask;
  105. while (1) {
  106. v = *version++;
  107. p = *pattern++;
  108. if (v <= ' ' && p <= ' ')
  109. break;
  110. if (v != p)
  111. return false;
  112. }
  113. if (p < ' ')
  114. return true; /* Project-only pattern */
  115. if (v != ' ' || *version++ != 'v')
  116. return false; /* Invalid/unparsable version string */
  117. if (*pattern++ != 'v')
  118. return false; /* Invalid pattern */
  119. vstart = version;
  120. pstart = pattern;
  121. if (compare_numbers(&version, &pattern, 0UL) < 0)
  122. return false;
  123. if (*pattern == '-')
  124. pattern++;
  125. else
  126. pattern = pstart;
  127. if (compare_numbers(&vstart, &pattern, -1UL) > 0)
  128. return false;
  129. v = *version++;
  130. vflags = 0;
  131. if (v == ' ') {
  132. uint64_t dummy;
  133. parse_flags(version, &vflags, &dummy);
  134. } else if (v > ' ') {
  135. return false;
  136. }
  137. p = *pattern++;
  138. pflags = pmask = 0;
  139. if (p == ' ') {
  140. parse_flags(pattern, &pflags, &pmask);
  141. } else if (p > ' ') {
  142. return false;
  143. }
  144. return (vflags & pmask) == pflags;
  145. }
  146. #ifdef COMMAND
  147. #include <stdio.h>
  148. int main(int argc, char *argv[])
  149. {
  150. if (argc != 3) {
  151. fprintf(stderr, "Usage: %s version pattern\n", argv[0]);
  152. return 127;
  153. }
  154. return !match_version(argv[1], argv[2]);
  155. }
  156. #endif