libata: glob_match for ata_device_blacklist (v2)
authorMark Lord <kernel@teksavvy.com>
Thu, 1 Jul 2010 22:16:14 +0000 (18:16 -0400)
committerJeff Garzik <jgarzik@redhat.com>
Sun, 1 Aug 2010 23:36:03 +0000 (19:36 -0400)
Replace rudimentry pattern matching with more capable shell-style globbing.
This will enable shrinking ata_device_blacklist[] table in subsequent patches,
and helps with future editions to the table, such as matching only the end
of a firmware revision string etc..

Signed-off-by: Mark Lord <mlord@pobox.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
drivers/ata/libata-core.c

index ddf8e48..f8c72a1 100644 (file)
@@ -4326,29 +4326,65 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
        { }
 };
 
-static int strn_pattern_cmp(const char *patt, const char *name, int wildchar)
+/**
+ *     glob_match - match a text string against a glob-style pattern
+ *     @text: the string to be examined
+ *     @pattern: the glob-style pattern to be matched against
+ *
+ *     Either/both of text and pattern can be empty strings.
+ *
+ *     Match text against a glob-style pattern, with wildcards and simple sets:
+ *
+ *             ?       matches any single character.
+ *             *       matches any run of characters.
+ *             [xyz]   matches a single character from the set: x, y, or z.
+ *
+ *     Note: hyphenated ranges [0-9] are _not_ supported here.
+ *     The special characters ?, [, or *, can be matched using a set, eg. [*]
+ *
+ *     Example patterns:  "SD1?",  "SD1[012345]",  "*R0",  SD*1?[012]*xx"
+ *
+ *     This function uses one level of recursion per '*' in pattern.
+ *     Since it calls _nothing_ else, and has _no_ explicit local variables,
+ *     this will not cause stack problems for any reasonable use here.
+ *
+ *     RETURNS:
+ *     0 on match, 1 otherwise.
+ */
+static int glob_match (const char *text, const char *pattern)
 {
-       const char *p;
-       int len;
-
-       /*
-        * check for trailing wildcard: *\0
-        */
-       p = strchr(patt, wildchar);
-       if (p && ((*(p + 1)) == 0))
-               len = p - patt;
-       else {
-               len = strlen(name);
-               if (!len) {
-                       if (!*patt)
-                               return 0;
-                       return -1;
+       do {
+               /* Match single character or a '?' wildcard */
+               if (*text == *pattern || *pattern == '?') {
+                       if (!*pattern++)
+                               return 0;  /* End of both strings: match */
+               } else {
+                       /* Match single char against a '[' bracketed ']' pattern set */
+                       if (!*text || *pattern != '[')
+                               break;  /* Not a pattern set */
+                       while (*++pattern && *pattern != ']' && *text != *pattern);
+                       if (!*pattern || *pattern == ']')
+                               return 1;  /* No match */
+                       while (*pattern && *pattern++ != ']');
+               }
+       } while (*++text && *pattern);
+
+       /* Match any run of chars against a '*' wildcard */
+       if (*pattern == '*') {
+               if (!*++pattern)
+                       return 0;  /* Match: avoid recursion at end of pattern */
+               /* Loop to handle additional pattern chars after the wildcard */
+               while (*text) {
+                       if (glob_match(text, pattern) == 0)
+                               return 0;  /* Remainder matched */
+                       ++text;  /* Absorb (match) this char and try again */
                }
        }
-
-       return strncmp(patt, name, len);
+       if (!*text && !*pattern)
+               return 0;  /* End of both strings: match */
+       return 1;  /* No match */
 }
-
 static unsigned long ata_dev_blacklisted(const struct ata_device *dev)
 {
        unsigned char model_num[ATA_ID_PROD_LEN + 1];
@@ -4359,10 +4395,10 @@ static unsigned long ata_dev_blacklisted(const struct ata_device *dev)
        ata_id_c_string(dev->id, model_rev, ATA_ID_FW_REV, sizeof(model_rev));
 
        while (ad->model_num) {
-               if (!strn_pattern_cmp(ad->model_num, model_num, '*')) {
+               if (!glob_match(model_num, ad->model_num)) {
                        if (ad->model_rev == NULL)
                                return ad->horkage;
-                       if (!strn_pattern_cmp(ad->model_rev, model_rev, '*'))
+                       if (!glob_match(model_rev, ad->model_rev))
                                return ad->horkage;
                }
                ad++;