--- webform_validation.validators.inc.old	2011-04-10 15:28:39.000000000 -0500
+++ webform_validation.validators.inc	2011-04-10 19:24:25.000000000 -0500
@@ -34,6 +34,22 @@ function webform_validation_webform_vali
       ),
       'description' => t('Verifies that user-entered values are numeric, with the option to specify min and / or max values.'),
     ),
+    'pattern' => array(
+      'name' => "Pattern",
+      'component_types' => array(
+        'textfield',
+        'hidden',
+      ),
+      'custom_error' => TRUE,
+      'custom_data' => array(
+        'label' => t('Pattern'),
+        'description' => t('Specify a pattern where:') 
+        . theme('item_list', array(t('@ = any letter A-Z'), t('# = any numeral 0-9'), t('Any other character must appear in its exact position')))
+        . t('Examples')
+        . theme('item_list', array(t('North American phone number: (###) ###-####'), t('Canadian Postal Code: @#@ #@#'), t('D-2500 series model numbers: D-25##'))),
+      ),
+      'description' => t('Verifies that a user-entered value conforms to a specified pattern.'),
+    ),    
     'min_length' => array(
       'name' => "Minimum length",
       'component_types' => array(
@@ -457,6 +473,22 @@ function webform_validation_webform_vali
         }
         return $errors;
         break;
+      case "pattern":
+        $pattern = $rule['data'];
+        $pattern = preg_quote($pattern, "/"); // Escape regex control characters
+        If (!function_exists('array_replace')) { // PHP < 5.3
+          $pattern = preg_replace('/-/', '\-', $pattern); // PHP bug #47229
+        }
+        $pattern = preg_replace('/@/', '[a-zA-Z]', $pattern);
+        $pattern = preg_replace('/#/', '[0-9]', $pattern);
+        //drupal_set_message($pattern); // @diag
+        foreach ($items as $key => $val) {
+          if ($val != '' && (!preg_match('/^('.$pattern.')$/', $val))) {
+            $errors[$key] = _webform_validation_i18n_error_message($rule);
+          }
+        }
+        return $errors;
+        break;        
       case 'must_be_empty':
         foreach ($items as $key => $val) {
           if ($val) {
